diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 79263cfd6e..36aeb6c6c5 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -6,10 +6,11 @@ Here are release notes for each snapshot release from M9 onwards. Unreleased ---------- -** Enum Class Evolution - With the addition of AMQP serialization Corda now supports enum constant evolution, that is the abilit to alter - an enum constant and, as long as certain rules ar followed and the correct annotations applied, have older and - newer instances of that enumeration be understood. +* **Enum Class Evolution** + With the addition of AMQP serialization Corda now supports enum constant evolution. + + That is the ability to alter an enum constant and, as long as certain rules are followed and the correct + annotations applied, have older and newer instances of that enumeration be understood. Release 2.0 ---------- diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt index 221954371b..701df3732d 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt @@ -1,33 +1,33 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.core.internal.uncheckedCast import org.apache.qpid.proton.amqp.Symbol import org.apache.qpid.proton.codec.Data import java.io.NotSerializableException +import java.lang.UnsupportedOperationException import java.lang.reflect.Type -import java.util.* /** - * @property clazz The enum as it exists now, not as it did when it was seialised (either in the past + * @property clazz The enum as it exists now, not as it did when it was serialized (either in the past * or future). * @property factory the [SerializerFactory] that is building this serialization object. - * @property conversions A mapping between all potential enum constants that could've been assigned as - * existed at serialisation and those that exist now + * @property conversions A mapping between all potential enum constants that could've been assigned to + * an instance of the enum as it existed at serialisation and those that exist now * @property ordinals Convenience mapping of constant to ordinality - * */ class EnumEvolutionSerializer( clazz: Type, factory: SerializerFactory, - private val conversions : Map, - private val ordinals : Map) : AMQPSerializer { + private val conversions: Map, + private val ordinals: Map) : AMQPSerializer { override val type: Type = clazz override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}")!! companion object { - private fun MutableMap.mapInPlace(f : (String)->String) { - val i = this.iterator() - while(i.hasNext()) { - val curr = (i.next()) + private fun MutableMap.mapInPlace(f: (String) -> String) { + val i = iterator() + while (i.hasNext()) { + val curr = i.next() curr.setValue(f(curr.value)) } } @@ -35,7 +35,7 @@ class EnumEvolutionSerializer( /** * Builds an Enum Evolver serializer. * - * @param old The description of the enum as it existed at the time of serialisation take from the + * @param old The description of the enum as it existed at the time of serialisation taken from the * received AMQP header * @param new The Serializer object we built based on the current state of the enum class on our classpath * @param factory the [SerializerFactory] that is building this serialization object. @@ -53,21 +53,19 @@ class EnumEvolutionSerializer( // if either of these isn't of the cast type then something has gone terribly wrong // elsewhere in the code - @Suppress("UNCHECKED_CAST") - val defaultRules = transforms[TransformTypes.EnumDefault] as? List - @Suppress("UNCHECKED_CAST") - val renameRules = transforms[TransformTypes.Rename] as? List + val defaultRules: List? = uncheckedCast(transforms[TransformTypes.EnumDefault]) + val renameRules: List? = uncheckedCast(transforms[TransformTypes.Rename]) // What values exist on the enum as it exists on the class path val localValues = new.type.asClass()!!.enumConstants.map { it.toString() } - val conversions : MutableMap = new.type.asClass()!!.enumConstants.map { it.toString() } + val conversions: MutableMap = localValues .union(defaultRules?.map { it.new }?.toSet() ?: emptySet()) .union(renameRules?.map { it.to } ?: emptySet()) .associateBy({ it }, { it }) .toMutableMap() - val rules : MutableMap = mutableMapOf() + val rules: MutableMap = mutableMapOf() rules.putAll(defaultRules?.associateBy({ it.new }, { it.old }) ?: emptyMap()) rules.putAll(renameRules?.associateBy({ it.to }, { it.from }) ?: emptyMap()) @@ -79,7 +77,7 @@ class EnumEvolutionSerializer( // rather tricky when you don't have access to the actual type, so this is a nice way to be able // to precompute and pass to the actual object var idx = 0 - return EnumEvolutionSerializer(new.type, factory, conversions, localValues.associateBy( {it}, { idx++ })) + return EnumEvolutionSerializer(new.type, factory, conversions, localValues.associateBy({ it }, { idx++ })) } } @@ -87,17 +85,17 @@ class EnumEvolutionSerializer( val enumName = (obj as List<*>)[0] as String if (enumName !in conversions) { - throw NotSerializableException ("No rule to evolve enum constant $type::$enumName") + throw NotSerializableException("No rule to evolve enum constant $type::$enumName") } return type.asClass()!!.enumConstants[ordinals[conversions[enumName]]!!] } override fun writeClassInfo(output: SerializationOutput) { - throw IllegalAccessException("It should be impossible to write an evolution serializer") + throw UnsupportedOperationException("It should be impossible to write an evolution serializer") } override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) { - throw IllegalAccessException("It should be impossible to write an evolution serializer") + throw UnsupportedOperationException("It should be impossible to write an evolution serializer") } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt index 59f22598a0..411e405aad 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt @@ -109,7 +109,7 @@ class EvolutionSerializer( } override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) { - throw IllegalAccessException("It should be impossible to write an evolution serializer") + throw UnsupportedOperationException("It should be impossible to write an evolution serializer") } /** diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TansformTypes.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TansformTypes.kt index 6fd1e4a95f..e21498f7b0 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TansformTypes.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TansformTypes.kt @@ -1,5 +1,6 @@ package net.corda.nodeapi.internal.serialization.amqp +import net.corda.core.internal.uncheckedCast import net.corda.core.serialization.CordaSerializationTransformEnumDefault import net.corda.core.serialization.CordaSerializationTransformEnumDefaults import net.corda.core.serialization.CordaSerializationTransformRename @@ -43,7 +44,7 @@ enum class TransformTypes(val build: (Annotation) -> Transform) : DescribedType * @param constants The list of enum constants on the type the transforms are being applied to */ override fun validate(l : List, constants: Set) { - @Suppress("UNCHECKED_CAST") (l as List).forEach { + uncheckedCast, List>(l).forEach { if (!constants.contains(it.old)) { throw NotSerializableException( "Enum extension defaults must be to a valid constant: ${it.new} -> ${it.old}. ${it.old} " + diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt index a8409a64c3..c88addacaa 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/TransformsSchema.kt @@ -146,10 +146,8 @@ class EnumDefaultSchemaTransform(val old: String, val new: String) : Transform() /** * Transform applied to either a class or enum where a property is renamed * - * @property from the name at time of change of the property - * @property to the new name of the property - * - * + * @property from the name of the property or constant prior to being changed, i.e. what it was + * @property to the new name of the property or constant after the change has been made, i.e. what it is now */ class RenameSchemaTransform(val from: String, val to: String) : Transform() { companion object : DescribedTypeConstructor { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt index c28ec8d95f..2b7930c707 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolveTests.kt @@ -27,7 +27,7 @@ class EnumEvolveTests { @Test fun deserialiseNewerSetToUnknown() { - val resource = "${this.javaClass.simpleName}.${testName()}" + val resource = "${javaClass.simpleName}.${testName()}" val sf = testDefaultFactory() data class C (val e : DeserializeNewerSetToUnknown) @@ -55,7 +55,7 @@ class EnumEvolveTests { @Test fun deserialiseNewerSetToUnknown2() { - val resource = "${this.javaClass.simpleName}.${testName()}" + val resource = "${javaClass.simpleName}.${testName()}" val sf = testDefaultFactory() data class C(val e: DeserializeNewerSetToUnknown2) @@ -95,7 +95,7 @@ class EnumEvolveTests { // Lets test to see if they forgot to provide an upgrade rule @Test fun deserialiseNewerWithNoRule() { - val resource = "${this.javaClass.simpleName}.${testName()}" + val resource = "${javaClass.simpleName}.${testName()}" val sf = testDefaultFactory() data class C(val e: DeserializeNewerWithNoRule) @@ -143,7 +143,7 @@ class EnumEvolveTests { @Test fun deserializeWithRename() { - val resource = "${this.javaClass.simpleName}.${testName()}" + val resource = "${javaClass.simpleName}.${testName()}" val sf = testDefaultFactory() data class C(val e: DeserializeWithRename) @@ -265,7 +265,7 @@ class EnumEvolveTests { @Test fun multiOperations() { - val resource = "${this.javaClass.simpleName}.${testName()}" + val resource = "${javaClass.simpleName}.${testName()}" val sf = testDefaultFactory() data class C(val e: MultiOperations)