CORDA-1497 replace missing validation (#4349)

This commit is contained in:
Dominic Fox 2018-12-04 10:11:07 +00:00 committed by GitHub
parent 9232b3637b
commit c2986ca31d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 45 additions and 7 deletions

View File

@ -53,7 +53,7 @@ data class EnumTransforms(
}
private fun validate(constants: Map<String, Int>): EnumTransforms {
validateNoCycles()
validateNoCycles(constants)
// For any name in the enum's constants, get all its previous names
fun renameChain(newName: String): Sequence<String> = generateSequence(newName) { renames[it] }
@ -85,12 +85,16 @@ data class EnumTransforms(
*
* By detecting each condition, and updating the chains accordingly, we can perform cycle-detection in O(n) time.
*/
private fun validateNoCycles() {
private fun validateNoCycles(constants: Map<String, Int>) {
// We keep track of chains in both directions
val chainStartsToEnds = mutableMapOf<String, String>()
val chainEndsToStarts = mutableMapOf<String, String>()
for ((from, to) in renames) {
for ((to, from) in renames) {
if (from in constants) {
throw InvalidEnumTransformsException("Rename from $from to $to would rename existing constant in $constants.keys")
}
// If there is an existing chain, starting at the "to" node of this edge, then there is a chain from this edge's
// "from" to that chain's end.
val newEnd = chainStartsToEnds[to] ?: to
@ -105,13 +109,17 @@ data class EnumTransforms(
}
// Either update, or create, the chains in both directions.
chainStartsToEnds[from] = newEnd
chainEndsToStarts[to] = newStart
// If we have joined two previously unconnected chains, update their starts and ends accordingly.
chainStartsToEnds[newStart] = newEnd
chainEndsToStarts[newEnd] = newStart
}
// Make sure that every rename chain ends with a known constant.
for ((chainStart, chainEnd) in chainStartsToEnds) {
if (chainEnd !in constants) {
throw InvalidEnumTransformsException(
"Rename chain from $chainStart to $chainEnd does not end with a known constant in ${constants.keys}")
}
}
}
/**

View File

@ -51,6 +51,36 @@ class EnumTransformationTests {
}
}
@CordaSerializationTransformRenames(
CordaSerializationTransformRename(from = "P", to = "Q"),
CordaSerializationTransformRename(from = "Q", to = "R")
)
enum class DanglingRenames { A, B, C }
@Test
fun renameCycleDoesNotTerminateInConstant() {
assertFailsWith<InvalidEnumTransformsException> {
EnumTransforms.build(
TransformsAnnotationProcessor.getTransformsSchema(DanglingRenames::class.java),
DanglingRenames::class.java.constants)
}
}
@CordaSerializationTransformRenames(
CordaSerializationTransformRename(from = "P", to = "Q"),
CordaSerializationTransformRename(from = "Q", to = "R")
)
enum class RenamesExisting { Q, R, S }
@Test
fun renamesRenameExistingConstant() {
assertFailsWith<InvalidEnumTransformsException> {
EnumTransforms.build(
TransformsAnnotationProcessor.getTransformsSchema(RenamesExisting::class.java),
RenamesExisting::class.java.constants)
}
}
private val Class<*>.constants: Map<String, Int> get() =
enumConstants.asSequence().mapIndexed { index, constant -> constant.toString() to index }.toMap()
}