Maintain a per-(table, column) registry of Transformer strategies. The masker walks each cell during pipeline execution and asks the registry "do I have a transformer for this?" — if yes, apply; if no, pass through unchanged.
This is exactly how Synthesized.io's column-level masking config maps onto its execution engine. Decouples policy (what to mask) from mechanism (how).
Tests this task must pass
Registered transformer applied — users.email with the star-masker turns "alex@test.com" into "***13***".
Unregistered column passes through — users.name returns the original string unchanged.
Null passthrough — calling transform on a null value returns null.
Table-scoped registration — orders.email is not affected by a transformer registered on users.email.
Multiple transformers coexist — separate registrations for users.email and users.phone both fire.
//sampleStart
interface Transformer {
fun transform(value: Any?, salt: String): Any?
}
class TransformerRegistry {
// Register a transformer for a specific table.column
// Transform: apply registered transformer, or return value unchanged (passthrough)
fun register(table: String, column: String, transformer: Transformer) { TODO() }
fun transform(table: String, column: String, value: Any?, salt: String = "salt"): Any? { TODO() }
}
//sampleEnd
fun main() {
val registry = TransformerRegistry()
val starMasker = object : Transformer {
override fun transform(value: Any?, salt: String): Any? {
return if (value is String) "***${value.length}***" else value
}
}
registry.register("users", "email", starMasker)
registry.register("users", "phone", starMasker)
// Test 1: Registered transformer applied
val masked = registry.transform("users", "email", "alex@test.com")
check(masked == "***13***") { "FAIL: should mask. Got: $masked" }
println("✅ Test 1: Transformer applied — $masked")
// Test 2: Passthrough for unregistered column
val pass = registry.transform("users", "name", "Alex")
check(pass == "Alex") { "FAIL: unregistered column should passthrough" }
println("✅ Test 2: Passthrough — $pass")
// Test 3: Null handling
val n = registry.transform("users", "email", null)
check(n == null) { "FAIL: null passthrough" }
println("✅ Test 3: Null → null")
// Test 4: Different table, same column name — no transformer
val other = registry.transform("orders", "email", "test@test.com")
check(other == "test@test.com") { "FAIL: orders.email not registered" }
println("✅ Test 4: Different table — passthrough")
// Test 5: Multiple transformers
val m1 = registry.transform("users", "email", "a@b.com")
val m2 = registry.transform("users", "phone", "+71234567")
check(m1 == "***7***" && m2 == "***10***") { "FAIL: both columns masked" }
println("✅ Test 5: Multiple transformers work")
println("\n🎉 ALL TESTS PASSED!")
}
Hint
Use Map<Pair<String, String>, Transformer> keyed by (table, column). The Elvis operator (?:) gives you passthrough for unregistered columns in one line.
Solution
class TransformerRegistry {
private val map = mutableMapOf<Pair<String, String>, Transformer>()
fun register(table: String, column: String, transformer: Transformer) {
map[table to column] = transformer
}
fun transform(table: String, column: String, value: Any?, salt: String): Any? {
return map[table to column]?.transform(value, salt) ?: value
}
}