mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
CORDA-3745: Modify DJVM serializers to support Enum Evolution. (#6189)
This commit is contained in:
parent
2ce76e407d
commit
107819f5b5
@ -4,6 +4,10 @@ import net.corda.core.internal.BasicVerifier
|
||||
import net.corda.core.internal.Verifier
|
||||
import net.corda.core.serialization.ConstructorForDeserialization
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.CordaSerializationTransformEnumDefault
|
||||
import net.corda.core.serialization.CordaSerializationTransformEnumDefaults
|
||||
import net.corda.core.serialization.CordaSerializationTransformRename
|
||||
import net.corda.core.serialization.CordaSerializationTransformRenames
|
||||
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
@ -38,6 +42,10 @@ class DeterministicVerifierFactoryService(
|
||||
whitelist = Whitelist.MINIMAL,
|
||||
visibleAnnotations = setOf(
|
||||
CordaSerializable::class.java,
|
||||
CordaSerializationTransformEnumDefault::class.java,
|
||||
CordaSerializationTransformEnumDefaults::class.java,
|
||||
CordaSerializationTransformRename::class.java,
|
||||
CordaSerializationTransformRenames::class.java,
|
||||
ConstructorForDeserialization::class.java,
|
||||
DeprecatedConstructorForDeserialization::class.java
|
||||
),
|
||||
|
@ -98,7 +98,8 @@ class SandboxSerializerFactoryFactory(
|
||||
localSerializerFactory = localSerializerFactory,
|
||||
classLoader = classLoader,
|
||||
mustPreserveDataWhenEvolving = context.preventDataLoss,
|
||||
primitiveTypes = primitiveTypes
|
||||
primitiveTypes = primitiveTypes,
|
||||
baseTypes = localTypes
|
||||
)
|
||||
|
||||
val remoteSerializerFactory = DefaultRemoteSerializerFactory(
|
||||
|
@ -61,8 +61,9 @@ fun createSandboxSerializationEnv(
|
||||
@Suppress("unchecked_cast")
|
||||
val isEnumPredicate = predicateFactory.apply(CheckEnum::class.java) as Predicate<Class<*>>
|
||||
@Suppress("unchecked_cast")
|
||||
val enumConstants = taskFactory.apply(DescribeEnum::class.java)
|
||||
.andThen(taskFactory.apply(GetEnumNames::class.java))
|
||||
val enumConstants = taskFactory.apply(DescribeEnum::class.java) as Function<Class<*>, Array<out Any>>
|
||||
@Suppress("unchecked_cast")
|
||||
val enumConstantNames = enumConstants.andThen(taskFactory.apply(GetEnumNames::class.java))
|
||||
.andThen { (it as Array<out Any>).map(Any::toString) } as Function<Class<*>, List<String>>
|
||||
|
||||
val sandboxLocalTypes = BaseLocalTypes(
|
||||
@ -72,7 +73,8 @@ fun createSandboxSerializationEnv(
|
||||
mapClass = classLoader.toSandboxClass(Map::class.java),
|
||||
stringClass = classLoader.toSandboxClass(String::class.java),
|
||||
isEnum = isEnumPredicate,
|
||||
enumConstants = enumConstants
|
||||
enumConstants = enumConstants,
|
||||
enumConstantNames = enumConstantNames
|
||||
)
|
||||
val schemeBuilder = SandboxSerializationSchemeBuilder(
|
||||
classLoader = classLoader,
|
||||
|
@ -0,0 +1,155 @@
|
||||
package net.corda.serialization.djvm
|
||||
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.CordaSerializationTransformEnumDefault
|
||||
import net.corda.core.serialization.CordaSerializationTransformEnumDefaults
|
||||
import net.corda.core.serialization.CordaSerializationTransformRename
|
||||
import net.corda.core.serialization.CordaSerializationTransformRenames
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.serialization.internal._contextSerializationEnv
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.serialization.djvm.EvolvedEnum.ONE
|
||||
import net.corda.serialization.djvm.EvolvedEnum.TWO
|
||||
import net.corda.serialization.djvm.EvolvedEnum.THREE
|
||||
import net.corda.serialization.djvm.EvolvedEnum.FOUR
|
||||
import net.corda.serialization.djvm.OriginalEnum.One
|
||||
import net.corda.serialization.djvm.OriginalEnum.Two
|
||||
import net.corda.serialization.djvm.SandboxType.KOTLIN
|
||||
import net.corda.serialization.internal.amqp.CompositeType
|
||||
import net.corda.serialization.internal.amqp.DeserializationInput
|
||||
import net.corda.serialization.internal.amqp.RestrictedType
|
||||
import net.corda.serialization.internal.amqp.Transform
|
||||
import net.corda.serialization.internal.amqp.TransformTypes
|
||||
import net.corda.serialization.internal.amqp.TypeNotation
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.junit.jupiter.api.extension.ExtensionContext
|
||||
import org.junit.jupiter.api.fail
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.Arguments
|
||||
import org.junit.jupiter.params.provider.ArgumentsProvider
|
||||
import org.junit.jupiter.params.provider.ArgumentsSource
|
||||
import java.util.EnumMap
|
||||
import java.util.function.Function
|
||||
import java.util.stream.Stream
|
||||
|
||||
@ExtendWith(LocalSerialization::class)
|
||||
class DeserializeEnumWithEvolutionTest : TestBase(KOTLIN) {
|
||||
class EvolutionArgumentProvider : ArgumentsProvider {
|
||||
override fun provideArguments(context: ExtensionContext?): Stream<out Arguments> {
|
||||
return Stream.of(
|
||||
Arguments.of(ONE, One),
|
||||
Arguments.of(TWO, Two),
|
||||
Arguments.of(THREE, One),
|
||||
Arguments.of(FOUR, Two)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.devolve() = replace("Evolved", "Original")
|
||||
|
||||
private fun devolveType(type: TypeNotation): TypeNotation {
|
||||
return when (type) {
|
||||
is CompositeType -> type.copy(
|
||||
name = type.name.devolve(),
|
||||
fields = type.fields.map { it.copy(type = it.type.devolve()) }
|
||||
)
|
||||
is RestrictedType -> type.copy(name = type.name.devolve())
|
||||
else -> type
|
||||
}
|
||||
}
|
||||
|
||||
private fun SerializedBytes<*>.devolve(context: SerializationContext): SerializedBytes<Any> {
|
||||
val envelope = DeserializationInput.getEnvelope(this, context.encodingWhitelist).apply {
|
||||
val schemaTypes = schema.types.map(::devolveType)
|
||||
with(schema.types as MutableList<TypeNotation>) {
|
||||
clear()
|
||||
addAll(schemaTypes)
|
||||
}
|
||||
|
||||
val transforms = transformsSchema.types.asSequence().associateTo(LinkedHashMap()) {
|
||||
it.key.devolve() to it.value
|
||||
}
|
||||
with(transformsSchema.types as MutableMap<String, EnumMap<TransformTypes, MutableList<Transform>>>) {
|
||||
clear()
|
||||
putAll(transforms)
|
||||
}
|
||||
}
|
||||
return SerializedBytes(envelope.write())
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(EvolutionArgumentProvider::class)
|
||||
fun `test deserialising evolved enum`(value: EvolvedEnum, expected: OriginalEnum) {
|
||||
val context = (_contextSerializationEnv.get() ?: fail("No serialization environment!")).p2pContext
|
||||
|
||||
val evolvedData = value.serialize()
|
||||
val originalData = evolvedData.devolve(context)
|
||||
|
||||
sandbox {
|
||||
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
|
||||
val sandboxOriginal = originalData.deserializeFor(classLoader)
|
||||
assertEquals("sandbox." + OriginalEnum::class.java.name, sandboxOriginal::class.java.name)
|
||||
assertEquals(expected.toString(), sandboxOriginal.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(EvolutionArgumentProvider::class)
|
||||
fun `test deserialising data with evolved enum`(value: EvolvedEnum, expected: OriginalEnum) {
|
||||
val context = (_contextSerializationEnv.get() ?: fail("No serialization environment!")).p2pContext
|
||||
|
||||
val evolvedData = EvolvedData(value).serialize()
|
||||
val originalData = evolvedData.devolve(context)
|
||||
|
||||
sandbox {
|
||||
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
|
||||
val sandboxOriginal = originalData.deserializeFor(classLoader)
|
||||
|
||||
val taskFactory = classLoader.createRawTaskFactory()
|
||||
val result = taskFactory.compose(classLoader.createSandboxFunction())
|
||||
.apply(ShowOriginalData::class.java)
|
||||
.apply(sandboxOriginal) ?: fail("Result cannot be null")
|
||||
assertThat(result.toString())
|
||||
.isEqualTo(ShowOriginalData().apply(OriginalData(expected)))
|
||||
}
|
||||
}
|
||||
|
||||
class ShowOriginalData : Function<OriginalData, String> {
|
||||
override fun apply(input: OriginalData): String {
|
||||
return with(input) {
|
||||
"Name='${value.name}', Ordinal='${value.ordinal}'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
enum class OriginalEnum {
|
||||
One,
|
||||
Two
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
data class OriginalData(val value: OriginalEnum)
|
||||
|
||||
@CordaSerializable
|
||||
@CordaSerializationTransformRenames(
|
||||
CordaSerializationTransformRename(from = "One", to = "ONE"),
|
||||
CordaSerializationTransformRename(from = "Two", to = "TWO")
|
||||
)
|
||||
@CordaSerializationTransformEnumDefaults(
|
||||
CordaSerializationTransformEnumDefault(new = "THREE", old = "One"),
|
||||
CordaSerializationTransformEnumDefault(new = "FOUR", old = "Two")
|
||||
)
|
||||
enum class EvolvedEnum {
|
||||
ONE,
|
||||
TWO,
|
||||
THREE,
|
||||
FOUR
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
data class EvolvedData(val value: EvolvedEnum)
|
@ -4,22 +4,14 @@ import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.serialization.internal._contextSerializationEnv
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.serialization.djvm.SandboxType.KOTLIN
|
||||
import net.corda.serialization.internal.SectionId
|
||||
import net.corda.serialization.internal.amqp.CompositeType
|
||||
import net.corda.serialization.internal.amqp.DeserializationInput
|
||||
import net.corda.serialization.internal.amqp.Envelope
|
||||
import net.corda.serialization.internal.amqp.TypeNotation
|
||||
import net.corda.serialization.internal.amqp.alsoAsByteBuffer
|
||||
import net.corda.serialization.internal.amqp.amqpMagic
|
||||
import net.corda.serialization.internal.amqp.withDescribed
|
||||
import net.corda.serialization.internal.amqp.withList
|
||||
import org.apache.qpid.proton.codec.Data
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.junit.jupiter.api.fail
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.function.Function
|
||||
|
||||
@ExtendWith(LocalSerialization::class)
|
||||
@ -37,12 +29,8 @@ class SafeDeserialisationTest : TestBase(KOTLIN) {
|
||||
val innocentData = innocent.serialize()
|
||||
val envelope = DeserializationInput.getEnvelope(innocentData, context.encodingWhitelist).apply {
|
||||
val innocentType = schema.types[0] as CompositeType
|
||||
(schema.types as MutableList<TypeNotation>)[0] = CompositeType(
|
||||
name = innocentType.name.replace("Innocent", "VeryEvil"),
|
||||
label = innocentType.label,
|
||||
provides = innocentType.provides,
|
||||
descriptor = innocentType.descriptor,
|
||||
fields = innocentType.fields
|
||||
(schema.types as MutableList<TypeNotation>)[0] = innocentType.copy(
|
||||
name = innocentType.name.replace("Innocent", "VeryEvil")
|
||||
)
|
||||
}
|
||||
val evilData = SerializedBytes<Any>(envelope.write())
|
||||
@ -68,23 +56,6 @@ class SafeDeserialisationTest : TestBase(KOTLIN) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Envelope.write(): ByteArray {
|
||||
val data = Data.Factory.create()
|
||||
data.withDescribed(Envelope.DESCRIPTOR_OBJECT) {
|
||||
withList {
|
||||
putObject(obj)
|
||||
putObject(schema)
|
||||
putObject(transformsSchema)
|
||||
}
|
||||
}
|
||||
return ByteArrayOutputStream().use {
|
||||
amqpMagic.writeTo(it)
|
||||
SectionId.DATA_AND_STOP.writeTo(it)
|
||||
it.alsoAsByteBuffer(data.encodedSize().toInt(), data::encode)
|
||||
it.toByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
class ShowInnocentData : Function<InnocentData, String> {
|
||||
override fun apply(data: InnocentData): String {
|
||||
return "${data::class.java.name}: ${data.message}, ${data.number}"
|
||||
|
@ -2,6 +2,10 @@ package net.corda.serialization.djvm
|
||||
|
||||
import net.corda.core.serialization.ConstructorForDeserialization
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.CordaSerializationTransformEnumDefault
|
||||
import net.corda.core.serialization.CordaSerializationTransformEnumDefaults
|
||||
import net.corda.core.serialization.CordaSerializationTransformRename
|
||||
import net.corda.core.serialization.CordaSerializationTransformRenames
|
||||
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
|
||||
import net.corda.djvm.SandboxConfiguration
|
||||
import net.corda.djvm.SandboxRuntimeContext
|
||||
@ -51,6 +55,10 @@ abstract class TestBase(type: SandboxType) {
|
||||
whitelist = MINIMAL,
|
||||
visibleAnnotations = setOf(
|
||||
CordaSerializable::class.java,
|
||||
CordaSerializationTransformEnumDefault::class.java,
|
||||
CordaSerializationTransformEnumDefaults::class.java,
|
||||
CordaSerializationTransformRename::class.java,
|
||||
CordaSerializationTransformRenames::class.java,
|
||||
ConstructorForDeserialization::class.java,
|
||||
DeprecatedConstructorForDeserialization::class.java
|
||||
),
|
||||
|
@ -0,0 +1,28 @@
|
||||
@file:JvmName("TestHelpers")
|
||||
package net.corda.serialization.djvm
|
||||
|
||||
import net.corda.serialization.internal.SectionId
|
||||
import net.corda.serialization.internal.amqp.Envelope
|
||||
import net.corda.serialization.internal.amqp.alsoAsByteBuffer
|
||||
import net.corda.serialization.internal.amqp.amqpMagic
|
||||
import net.corda.serialization.internal.amqp.withDescribed
|
||||
import net.corda.serialization.internal.amqp.withList
|
||||
import org.apache.qpid.proton.codec.Data
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
fun Envelope.write(): ByteArray {
|
||||
val data = Data.Factory.create()
|
||||
data.withDescribed(Envelope.DESCRIPTOR_OBJECT) {
|
||||
withList {
|
||||
putObject(obj)
|
||||
putObject(schema)
|
||||
putObject(transformsSchema)
|
||||
}
|
||||
}
|
||||
return ByteArrayOutputStream().use {
|
||||
amqpMagic.writeTo(it)
|
||||
SectionId.DATA_AND_STOP.writeTo(it)
|
||||
it.alsoAsByteBuffer(data.encodedSize().toInt(), data::encode)
|
||||
it.toByteArray()
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package net.corda.serialization.internal.amqp
|
||||
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.serialization.internal.model.BaseLocalTypes
|
||||
import org.apache.qpid.proton.codec.Data
|
||||
import java.lang.UnsupportedOperationException
|
||||
import java.lang.reflect.Type
|
||||
@ -34,6 +35,7 @@ import java.lang.reflect.Type
|
||||
class EnumEvolutionSerializer(
|
||||
override val type: Type,
|
||||
factory: LocalSerializerFactory,
|
||||
private val baseLocalTypes: BaseLocalTypes,
|
||||
private val conversions: Map<String, String>,
|
||||
private val ordinals: Map<String, Int>) : AMQPSerializer<Any> {
|
||||
override val typeDescriptor = factory.createDescriptor(type)
|
||||
@ -46,7 +48,7 @@ class EnumEvolutionSerializer(
|
||||
val converted = conversions[enumName] ?: throw AMQPNotSerializableException(type, "No rule to evolve enum constant $type::$enumName")
|
||||
val ordinal = ordinals[converted] ?: throw AMQPNotSerializableException(type, "Ordinal not found for enum value $type::$converted")
|
||||
|
||||
return type.asClass().enumConstants[ordinal]
|
||||
return baseLocalTypes.enumConstants.apply(type.asClass())[ordinal]
|
||||
}
|
||||
|
||||
override fun writeClassInfo(output: SerializationOutput) {
|
||||
|
@ -40,7 +40,8 @@ class DefaultEvolutionSerializerFactory(
|
||||
private val localSerializerFactory: LocalSerializerFactory,
|
||||
private val classLoader: ClassLoader,
|
||||
private val mustPreserveDataWhenEvolving: Boolean,
|
||||
override val primitiveTypes: Map<Class<*>, Class<*>>
|
||||
override val primitiveTypes: Map<Class<*>, Class<*>>,
|
||||
private val baseTypes: BaseLocalTypes
|
||||
): EvolutionSerializerFactory {
|
||||
// Invert the "primitive -> boxed primitive" mapping.
|
||||
private val primitiveBoxedTypes: Map<Class<*>, Class<*>>
|
||||
@ -172,7 +173,7 @@ class DefaultEvolutionSerializerFactory(
|
||||
if (constantsAreReordered(localOrdinals, convertedOrdinals)) throw EvolutionSerializationException(this,
|
||||
"Constants have been reordered, additions must be appended to the end")
|
||||
|
||||
return EnumEvolutionSerializer(localTypeInformation.observedType, localSerializerFactory, conversions, localOrdinals)
|
||||
return EnumEvolutionSerializer(localTypeInformation.observedType, localSerializerFactory, baseTypes, conversions, localOrdinals)
|
||||
}
|
||||
|
||||
private fun constantsAreReordered(localOrdinals: Map<String, Int>, convertedOrdinals: Map<Int, String>): Boolean =
|
||||
|
@ -97,10 +97,8 @@ object SerializerFactoryBuilder {
|
||||
mustPreserveDataWhenEvolving: Boolean): SerializerFactory {
|
||||
val customSerializerRegistry = CachingCustomSerializerRegistry(descriptorBasedSerializerRegistry)
|
||||
|
||||
val localTypeModel = ConfigurableLocalTypeModel(
|
||||
WhitelistBasedTypeModelConfiguration(
|
||||
whitelist,
|
||||
customSerializerRegistry))
|
||||
val typeModelConfiguration = WhitelistBasedTypeModelConfiguration(whitelist, customSerializerRegistry)
|
||||
val localTypeModel = ConfigurableLocalTypeModel(typeModelConfiguration)
|
||||
|
||||
val fingerPrinter = overrideFingerPrinter ?:
|
||||
TypeModellingFingerPrinter(customSerializerRegistry)
|
||||
@ -124,7 +122,8 @@ object SerializerFactoryBuilder {
|
||||
localSerializerFactory,
|
||||
classCarpenter.classloader,
|
||||
mustPreserveDataWhenEvolving,
|
||||
javaPrimitiveTypes
|
||||
javaPrimitiveTypes,
|
||||
typeModelConfiguration.baseTypes
|
||||
) else NoEvolutionSerializerFactory
|
||||
|
||||
val remoteSerializerFactory = DefaultRemoteSerializerFactory(
|
||||
|
@ -46,7 +46,7 @@ abstract class Transform : DescribedType {
|
||||
* descendants of this class
|
||||
*/
|
||||
override fun newInstance(obj: Any?): Transform {
|
||||
val described = Transform.checkDescribed(obj) as List<*>
|
||||
val described = checkDescribed(obj) as List<*>
|
||||
return when (described[0]) {
|
||||
EnumDefaultSchemaTransform.typeName -> EnumDefaultSchemaTransform.newInstance(described)
|
||||
RenameSchemaTransform.typeName -> RenameSchemaTransform.newInstance(described)
|
||||
@ -195,18 +195,24 @@ object TransformsAnnotationProcessor {
|
||||
* Obtain all of the transforms applied for the given [Class].
|
||||
*/
|
||||
fun getTransformsSchema(type: Class<*>): TransformsMap {
|
||||
val result = TransformsMap(TransformTypes::class.java)
|
||||
// We only have transforms for enums at present.
|
||||
if (!type.isEnum) return result
|
||||
return when {
|
||||
// This only detects Enum classes that are outside the DJVM sandbox.
|
||||
type.isEnum -> getEnumTransformsSchema(type)
|
||||
|
||||
// We only have transforms for enums at present.
|
||||
else -> TransformsMap(TransformTypes::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
fun getEnumTransformsSchema(type: Class<*>): TransformsMap {
|
||||
val result = TransformsMap(TransformTypes::class.java)
|
||||
supportedTransforms.forEach { supportedTransform ->
|
||||
val annotationContainer = type.getAnnotation(supportedTransform.type) ?: return@forEach
|
||||
result.processAnnotations(
|
||||
type,
|
||||
supportedTransform.enum,
|
||||
supportedTransform.getAnnotations(annotationContainer))
|
||||
type,
|
||||
supportedTransform.enum,
|
||||
supportedTransform.getAnnotations(annotationContainer))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,8 @@ private val DEFAULT_BASE_TYPES = BaseLocalTypes(
|
||||
mapClass = Map::class.java,
|
||||
stringClass = String::class.java,
|
||||
isEnum = Predicate { clazz -> clazz.isEnum },
|
||||
enumConstants = Function { clazz ->
|
||||
enumConstants = Function { clazz -> clazz.enumConstants },
|
||||
enumConstantNames = Function { clazz ->
|
||||
(clazz as Class<out Enum<*>>).enumConstants.map(Enum<*>::name)
|
||||
}
|
||||
)
|
@ -115,13 +115,13 @@ internal data class LocalTypeInformationBuilder(val lookup: LocalTypeLookup,
|
||||
baseTypes.mapClass.isAssignableFrom(type) -> AMap(type, typeIdentifier, Unknown, Unknown)
|
||||
type === baseTypes.stringClass -> Atomic(type, typeIdentifier)
|
||||
type.kotlin.javaPrimitiveType != null -> Atomic(type, typeIdentifier)
|
||||
baseTypes.isEnum.test(type) -> baseTypes.enumConstants.apply(type).let { enumConstants ->
|
||||
baseTypes.isEnum.test(type) -> baseTypes.enumConstantNames.apply(type).let { enumConstantNames ->
|
||||
AnEnum(
|
||||
type,
|
||||
typeIdentifier,
|
||||
enumConstants,
|
||||
enumConstantNames,
|
||||
buildInterfaceInformation(type),
|
||||
getEnumTransforms(type, enumConstants)
|
||||
getEnumTransforms(type, enumConstantNames)
|
||||
)
|
||||
}
|
||||
type.kotlinObjectInstance != null -> Singleton(
|
||||
@ -145,7 +145,7 @@ internal data class LocalTypeInformationBuilder(val lookup: LocalTypeLookup,
|
||||
private fun getEnumTransforms(type: Class<*>, enumConstants: List<String>): EnumTransforms {
|
||||
try {
|
||||
val constants = enumConstants.asSequence().mapIndexed { index, constant -> constant to index }.toMap()
|
||||
return EnumTransforms.build(TransformsAnnotationProcessor.getTransformsSchema(type), constants)
|
||||
return EnumTransforms.build(TransformsAnnotationProcessor.getEnumTransformsSchema(type), constants)
|
||||
} catch (e: InvalidEnumTransformsException) {
|
||||
throw NotSerializableDetailedException(type.name, e.message!!)
|
||||
}
|
||||
|
@ -136,5 +136,6 @@ class BaseLocalTypes(
|
||||
val mapClass: Class<*>,
|
||||
val stringClass: Class<*>,
|
||||
val isEnum: Predicate<Class<*>>,
|
||||
val enumConstants: Function<Class<*>, List<String>>
|
||||
val enumConstants: Function<Class<*>, Array<out Any>>,
|
||||
val enumConstantNames: Function<Class<*>, List<String>>
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user