Ensure typename is calculated correctly for nested arrays

We were relying on the Java typename conversion to work for our AMQP
envelope type selection, and only special casing arrays of primitives.

However with nested arrays this breaks as the intermediate serialises
for the nested arrays still have no idea as to what the underlaying type
is as the assumption will be being made that the type is a boxed
primitive.

Solution is to compute the typename properly, walking down the nested
array chain
This commit is contained in:
Katelyn Baker 2017-07-25 15:36:42 +01:00
parent 6dc48e7cea
commit b8ae03410d
2 changed files with 84 additions and 15 deletions

View File

@ -15,9 +15,20 @@ open class ArraySerializer(override val type: Type, factory: SerializerFactory)
}
}
// because this might be an array of array of primitives (to any recursive depth) and
// because we care that the lowest type is unboxed we can't rely on the inbuilt type
// id to generate it properly (it will always return [[[Ljava.lang.type -> type[][][]
// for example).
//
// We *need* to retain knowledge for AMQP deserialisation weather that lowest primitive
// was boxed or unboxed so just infer it recursively
private fun calcTypeName(type: Type) : String =
if (type.componentType().isArray()) "${calcTypeName(type.componentType())}[]"
else "${type.componentType().typeName}${if (type.asClass()!!.componentType.isPrimitive) "[p]" else "[]"}"
override val typeDescriptor by lazy { "$DESCRIPTOR_DOMAIN:${fingerprintForType(type, factory)}" }
internal val elementType: Type by lazy { type.componentType() }
internal open val typeName by lazy { type.typeName }
internal open val typeName by lazy { calcTypeName(type) }
internal val typeNotation: TypeNotation by lazy {
RestrictedType(typeName, null, emptyList(), "list", Descriptor(typeDescriptor, null), emptyList())
@ -73,13 +84,13 @@ abstract class PrimArraySerializer(type: Type, factory: SerializerFactory) : Arr
// We don't need to handle the unboxed byte type as that is coercible to a byte array, but
// the other 7 primitive types we do
val primTypes: Map<Type, (SerializerFactory) -> PrimArraySerializer> = mapOf(
IntArray::class.java to { f -> PrimIntArraySerializer(f, "int[p]") },
CharArray::class.java to { f -> PrimCharArraySerializer(f, "char[p]") },
BooleanArray::class.java to { f -> PrimBooleanArraySerializer(f, "boolean[p]") },
FloatArray::class.java to { f -> PrimFloatArraySerializer(f, "float[p]") },
ShortArray::class.java to { f -> PrimShortArraySerializer(f, "short[p]") },
DoubleArray::class.java to { f -> PrimDoubleArraySerializer(f, "double[p]") },
LongArray::class.java to { f -> PrimLongArraySerializer(f, "long[p]") }
IntArray::class.java to { f -> PrimIntArraySerializer(f) },
CharArray::class.java to { f -> PrimCharArraySerializer(f) },
BooleanArray::class.java to { f -> PrimBooleanArraySerializer(f) },
FloatArray::class.java to { f -> PrimFloatArraySerializer(f) },
ShortArray::class.java to { f -> PrimShortArraySerializer(f) },
DoubleArray::class.java to { f -> PrimDoubleArraySerializer(f) },
LongArray::class.java to { f -> PrimLongArraySerializer(f) }
// ByteArray::class.java <-> NOT NEEDED HERE (see comment above)
)
@ -91,14 +102,14 @@ abstract class PrimArraySerializer(type: Type, factory: SerializerFactory) : Arr
}
}
class PrimIntArraySerializer(factory: SerializerFactory, override val typeName: String) :
class PrimIntArraySerializer(factory: SerializerFactory) :
PrimArraySerializer(IntArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) {
localWriteObject(data) { (obj as IntArray).forEach { output.writeObjectOrNull(it, data, elementType) } }
}
}
class PrimCharArraySerializer(factory: SerializerFactory, override val typeName: String) :
class PrimCharArraySerializer(factory: SerializerFactory) :
PrimArraySerializer(CharArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) {
localWriteObject(data) { (obj as CharArray).forEach { output.writeObjectOrNull(it, data, elementType) } }
@ -114,35 +125,35 @@ class PrimCharArraySerializer(factory: SerializerFactory, override val typeName:
}
}
class PrimBooleanArraySerializer(factory: SerializerFactory, override val typeName: String) :
class PrimBooleanArraySerializer(factory: SerializerFactory) :
PrimArraySerializer(BooleanArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) {
localWriteObject(data) { (obj as BooleanArray).forEach { output.writeObjectOrNull(it, data, elementType) } }
}
}
class PrimDoubleArraySerializer(factory: SerializerFactory, override val typeName: String) :
class PrimDoubleArraySerializer(factory: SerializerFactory) :
PrimArraySerializer(DoubleArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) {
localWriteObject(data) { (obj as DoubleArray).forEach { output.writeObjectOrNull(it, data, elementType) } }
}
}
class PrimFloatArraySerializer(factory: SerializerFactory, override val typeName: String) :
class PrimFloatArraySerializer(factory: SerializerFactory) :
PrimArraySerializer(FloatArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) {
localWriteObject(data) { (obj as FloatArray).forEach { output.writeObjectOrNull(it, data, elementType) } }
}
}
class PrimShortArraySerializer(factory: SerializerFactory, override val typeName: String) :
class PrimShortArraySerializer(factory: SerializerFactory) :
PrimArraySerializer(ShortArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) {
localWriteObject(data) { (obj as ShortArray).forEach { output.writeObjectOrNull(it, data, elementType) } }
}
}
class PrimLongArraySerializer(factory: SerializerFactory, override val typeName: String) :
class PrimLongArraySerializer(factory: SerializerFactory) :
PrimArraySerializer(LongArray::class.java, factory) {
override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) {
localWriteObject(data) { (obj as LongArray).forEach { output.writeObjectOrNull(it, data, elementType) } }

View File

@ -370,5 +370,63 @@ class DeserializeSimpleTypesTests {
assertEquals(c.c[1], deserializedC.c[1])
assertEquals(c.c[2], deserializedC.c[2])
}
@Test
fun arrayOfArrayOfInt() {
class C(val c: Array<Array<Int>>)
val c = C (arrayOf (arrayOf(1,2,3), arrayOf(4,5,6)))
val serialisedC = TestSerializationOutput(VERBOSE, sf).serialize(c)
val deserializedC = DeserializationInput(sf).deserialize(serialisedC)
assertEquals(c.c.size, deserializedC.c.size)
assertEquals(c.c[0].size, deserializedC.c[0].size)
assertEquals(c.c[0][0], deserializedC.c[0][0])
assertEquals(c.c[0][1], deserializedC.c[0][1])
assertEquals(c.c[0][2], deserializedC.c[0][2])
assertEquals(c.c[1].size, deserializedC.c[1].size)
assertEquals(c.c[1][0], deserializedC.c[1][0])
assertEquals(c.c[1][1], deserializedC.c[1][1])
assertEquals(c.c[1][2], deserializedC.c[1][2])
}
@Test
fun arrayOfIntArray() {
class C(val c: Array<IntArray>)
val c = C (arrayOf (IntArray(3), IntArray(3)))
c.c[0][0] = 1; c.c[0][1] = 2; c.c[0][2] = 3
c.c[1][0] = 4; c.c[1][1] = 5; c.c[1][2] = 6
val serialisedC = TestSerializationOutput(VERBOSE, sf).serialize(c)
val deserializedC = DeserializationInput(sf).deserialize(serialisedC)
assertEquals(c.c.size, deserializedC.c.size)
assertEquals(c.c[0].size, deserializedC.c[0].size)
assertEquals(c.c[0][0], deserializedC.c[0][0])
assertEquals(c.c[0][1], deserializedC.c[0][1])
assertEquals(c.c[0][2], deserializedC.c[0][2])
assertEquals(c.c[1].size, deserializedC.c[1].size)
assertEquals(c.c[1][0], deserializedC.c[1][0])
assertEquals(c.c[1][1], deserializedC.c[1][1])
assertEquals(c.c[1][2], deserializedC.c[1][2])
}
@Test
fun arrayOfArrayOfIntArray() {
class C(val c: Array<Array<IntArray>>)
val c = C(arrayOf(arrayOf(IntArray(3), IntArray(3), IntArray(3)),
arrayOf(IntArray(3), IntArray(3), IntArray(3)),
arrayOf(IntArray(3), IntArray(3), IntArray(3))))
for (i in 0..2) { for (j in 0..2) { for (k in 0..2) { c.c[i][j][k] = i + j + k } } }
val serialisedC = TestSerializationOutput(VERBOSE, sf).serialize(c)
val deserializedC = DeserializationInput(sf).deserialize(serialisedC)
for (i in 0..2) { for (j in 0..2) { for (k in 0..2) {
assertEquals(c.c[i][j][k], deserializedC.c[i][j][k])
}}}
}
}