diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt index 2853ba47ee..eb45430453 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt @@ -18,18 +18,23 @@ class MapSerializer(val declaredType: ParameterizedType, factory: SerializerFact override val typeDescriptor = "$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}" companion object { - private val supportedTypes: Map, (Map<*, *>) -> Map<*, *>> = mapOf( + private val supportedTypes: Map, (Map<*, *>) -> Map<*, *>> = mapOf( + // Interfaces Map::class.java to { map -> map }, SortedMap::class.java to { map -> TreeMap(map) }, NavigableMap::class.java to { map -> TreeMap(map) }, + // Sub types Dictionary::class.java to { map -> Hashtable(map) }, - // concrete types for user convienience - HashMap::class.java to { map -> LinkedHashMap(map) }, + AbstractMap::class.java to { map -> LinkedHashMap(map) }, + // concrete types LinkedHashMap::class.java to { map -> LinkedHashMap(map) }, TreeMap::class.java to { map -> TreeMap(map) }, - Hashtable::class.java to { map -> Hashtable(map) } + Hashtable::class.java to { map -> Hashtable(map) }, + WeakHashMap::class.java to { map -> WeakHashMap(map) } + // Explicitly disallowed + // - HashMap::class.java + // - EnumMap::class.java ) - private fun findConcreteType(clazz: Class<*>): (Map<*, *>) -> Map<*, *> { return supportedTypes[clazz] ?: throw NotSerializableException("Unsupported map type $clazz.") } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt index a8c15461ca..57efe40cab 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationHelper.kt @@ -35,6 +35,7 @@ internal fun constructorForDeserialization(type: Type): KFunction? { val kotlinConstructors = clazz.kotlin.constructors val hasDefault = kotlinConstructors.any { it.parameters.isEmpty() } for (kotlinConstructor in kotlinConstructors) { + println (kotlinConstructor) if (preferredCandidate == null && kotlinConstructors.size == 1 && !hasDefault) { preferredCandidate = kotlinConstructor } else if (preferredCandidate == null && kotlinConstructors.size == 2 && hasDefault && kotlinConstructor.parameters.isNotEmpty()) { @@ -44,6 +45,7 @@ internal fun constructorForDeserialization(type: Type): KFunction? { throw NotSerializableException("More than one constructor for $clazz is annotated with @CordaConstructor.") } preferredCandidate = kotlinConstructor + println (" -> $preferredCandidate") } } return preferredCandidate ?: throw NotSerializableException("No constructor for deserialization found for $clazz.") diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeMapTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeMapTests.kt index da16929ccb..3c9a615baa 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeMapTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeMapTests.kt @@ -5,7 +5,8 @@ import java.util.* import org.apache.qpid.proton.codec.Data class DeserializeCollectionTests { -class TestSerializationOutput( + + class TestSerializationOutput( private val verbose: Boolean, serializerFactory: SerializerFactory = SerializerFactory()) : SerializationOutput(serializerFactory) { @@ -32,6 +33,23 @@ class TestSerializationOutput( DeserializationInput(sf).deserialize(serialisedBytes) } + @Test + fun abstractMapFromMapOf() { + data class C(val c: AbstractMap) + val c = C (mapOf("A" to 1, "B" to 2) as AbstractMap) + + val serialisedBytes = TestSerializationOutput(VERBOSE, sf).serialize(c) + DeserializationInput(sf).deserialize(serialisedBytes) + } + + @Test + fun abstractMapFromTreeMap() { + data class C(val c: AbstractMap) + val c = C (TreeMap(mapOf("A" to 1, "B" to 2))) + + val serialisedBytes = TestSerializationOutput(VERBOSE, sf).serialize(c) + DeserializationInput(sf).deserialize(serialisedBytes) + } @Test fun sortedMapTest() { data class C(val c: SortedMap) @@ -40,6 +58,15 @@ class TestSerializationOutput( DeserializationInput(sf).deserialize(serialisedBytes) } + @Test + fun navigableMapTest() { + data class C(val c: NavigableMap) + val c = C(TreeMap (mapOf("A" to 1, "B" to 2)).descendingMap()) + + val serialisedBytes = TestSerializationOutput(VERBOSE, sf).serialize(c) + DeserializationInput(sf).deserialize(serialisedBytes) + } + @Test fun dictionaryTest() { data class C(val c: Dictionary) @@ -51,17 +78,8 @@ class TestSerializationOutput( DeserializationInput(sf).deserialize(serialisedBytes) } - @Test - fun navigableMapTest() { - data class C(val c: NavigableMap) - val c = C(TreeMap (mapOf("A" to 1, "B" to 3)).descendingMap()) - - val serialisedBytes = TestSerializationOutput(VERBOSE, sf).serialize(c) - DeserializationInput(sf).deserialize(serialisedBytes) - } - @Test(expected=java.lang.IllegalArgumentException::class) - fun HashMapTest() { + fun hashMapTest() { data class C(val c : HashMap) val c = C (HashMap (mapOf("A" to 1, "B" to 2))) @@ -69,6 +87,15 @@ class TestSerializationOutput( TestSerializationOutput(VERBOSE, sf).serialize(c) } + @Test + fun weakHashMapTest() { + data class C(val c : WeakHashMap) + val c = C (WeakHashMap (mapOf("A" to 1, "B" to 2))) + + val serialisedBytes = TestSerializationOutput(VERBOSE, sf).serialize(c) + DeserializationInput(sf).deserialize(serialisedBytes) + } + @Test fun concreteTreeMapTest() { data class C(val c: TreeMap) @@ -84,10 +111,6 @@ class TestSerializationOutput( val c = C (LinkedHashMap (mapOf("A" to 1, "B" to 2))) val serialisedBytes = TestSerializationOutput(VERBOSE, sf).serialize(c) - val deserializedObj = DeserializationInput(sf).deserialize(serialisedBytes) + DeserializationInput(sf).deserialize(serialisedBytes) } - - - - } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt index 70a68e8cce..013fbaddc8 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt @@ -356,7 +356,7 @@ class SerializationOutputTests { serdes(obj) } - @Test(expected = NotSerializableException::class) + @Test fun `test TreeMap property`() { val obj = TreeMapWrapper(TreeMap()) obj.tree[456] = Foo("Fred", 123)