diff --git a/platform/object-serializer/src/BeanBinding.kt b/platform/object-serializer/src/BeanBinding.kt index 667f56bd283e..7d69dc246fba 100644 --- a/platform/object-serializer/src/BeanBinding.kt +++ b/platform/object-serializer/src/BeanBinding.kt @@ -19,8 +19,8 @@ private val structReaderBuilder by lazy { private const val ID_FIELD_NAME = "@id" -internal class BeanBinding(beanClass: Class<*>) : BaseBeanBinding(beanClass), RootBinding { - private lateinit var bindings: Array +internal class BeanBinding(beanClass: Class<*>) : BaseBeanBinding(beanClass), Binding { + private lateinit var bindings: Array private lateinit var nameToBindingIndex: ObjectIntHashMap private lateinit var properties: List diff --git a/platform/object-serializer/src/Binding.kt b/platform/object-serializer/src/Binding.kt index 39c21b1092bd..46f4ae10e5eb 100644 --- a/platform/object-serializer/src/Binding.kt +++ b/platform/object-serializer/src/Binding.kt @@ -1,27 +1,22 @@ // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.serialization +import com.amazon.ion.IonType import java.lang.reflect.Type -interface Binding { -} +internal interface Binding { + fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) { + write(hostObject, property, context) { + serialize(it, context) + } + } -/** - * Binding that can read and write data of object properties. - * - * Each NestedBinding also is a [RootBinding] not only because of ability to serialize any type as root (including primitives), - * but because of support for non-default class constructors - for this case BeanBinding will use nested binding as root binding (read value without passing property). - */ -interface NestedBinding : RootBinding { - fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) + fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { + read(hostObject, property, context) { + deserialize(context) + } + } - fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) -} - -/** - * Binding that can read and write data of root object. - */ -interface RootBinding : Binding { fun createCacheKey(aClass: Class<*>, type: Type = aClass) = type fun init(originalType: Type, context: BindingInitializationContext) { @@ -32,10 +27,35 @@ interface RootBinding : Binding { fun deserialize(context: ReadContext): Any } -interface BindingInitializationContext { +internal interface BindingInitializationContext { val propertyCollector: PropertyCollector val bindingProducer: BindingProducer val isResolveConstructorOnInit: Boolean get() = false +} + +internal inline fun write(hostObject: Any, accessor: MutableAccessor, context: WriteContext, write: ValueWriter.(value: Any) -> Unit) { + val value = accessor.readUnsafe(hostObject) + if (context.filter.isSkipped(value)) { + return + } + + val writer = context.writer + writer.setFieldName(accessor.name) + if (value == null) { + writer.writeNull() + } + else { + writer.write(value) + } +} + +internal inline fun read(hostObject: Any, property: MutableAccessor, context: ReadContext, read: ValueReader.() -> Any) { + if (context.reader.type == IonType.NULL) { + property.set(hostObject, null) + } + else { + property.set(hostObject, context.reader.read()) + } } \ No newline at end of file diff --git a/platform/object-serializer/src/BindingProducer.kt b/platform/object-serializer/src/BindingProducer.kt index b7b3c2e88c85..cc54353cbb99 100644 --- a/platform/object-serializer/src/BindingProducer.kt +++ b/platform/object-serializer/src/BindingProducer.kt @@ -9,8 +9,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read import kotlin.concurrent.write -abstract class BindingProducer : BindingInitializationContext { - private val cache: MutableMap = THashMap() +internal abstract class BindingProducer : BindingInitializationContext { + private val cache: MutableMap = THashMap() private val cacheLock = ReentrantReadWriteLock() @get:TestOnly @@ -22,10 +22,10 @@ abstract class BindingProducer : BindingInitializationContext { override val isResolveConstructorOnInit = SystemProperties.`is`("idea.serializer.resolve.ctor.on.init") - abstract fun getNestedBinding(accessor: MutableAccessor): NestedBinding + abstract fun getNestedBinding(accessor: MutableAccessor): Binding - fun getRootBinding(aClass: Class<*>, type: Type = aClass): RootBinding { - fun getByTypeOrByClass(): RootBinding? { + fun getRootBinding(aClass: Class<*>, type: Type = aClass): Binding { + fun getByTypeOrByClass(): Binding? { var result = cache.get(type) if (result == null && aClass !== type) { result = cache.get(aClass) @@ -57,7 +57,7 @@ abstract class BindingProducer : BindingInitializationContext { } } - protected abstract fun createRootBinding(aClass: Class<*>, type: Type): RootBinding + protected abstract fun createRootBinding(aClass: Class<*>, type: Type): Binding @Suppress("unused") fun clearBindingCache() { diff --git a/platform/object-serializer/src/CollectionBinding.kt b/platform/object-serializer/src/CollectionBinding.kt index 2e3228da74f7..1a0e14b6ad33 100644 --- a/platform/object-serializer/src/CollectionBinding.kt +++ b/platform/object-serializer/src/CollectionBinding.kt @@ -10,16 +10,9 @@ import java.lang.reflect.ParameterizedType import java.lang.reflect.Type import java.util.function.Consumer -// CollectionBinding implements NestedBinding directly because can mutate property value directly -internal abstract class BaseCollectionBinding(itemType: Type, context: BindingInitializationContext) : RootBinding, NestedBinding { +internal abstract class BaseCollectionBinding(itemType: Type, context: BindingInitializationContext) : Binding { private val itemBinding = createElementBindingByType(itemType, context) - final override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) { - write(hostObject, property, context) { - serialize(it, context) - } - } - protected fun createItemConsumer(context: WriteContext): Consumer { val writer = context.writer return Consumer { @@ -120,12 +113,6 @@ internal class ArrayBinding(private val itemClass: Class<*>, context: BindingIni writer.stepOut() } - override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { - read(hostObject, property, context) { - readArray(context) - } - } - private fun readArray(context: ReadContext): Array { val list = ArrayList() readInto(list, context) diff --git a/platform/object-serializer/src/InterfacePropertyBinding.kt b/platform/object-serializer/src/InterfacePropertyBinding.kt index 20d2ed4ec1b1..b9aa280a401d 100644 --- a/platform/object-serializer/src/InterfacePropertyBinding.kt +++ b/platform/object-serializer/src/InterfacePropertyBinding.kt @@ -2,7 +2,7 @@ package com.intellij.serialization @Suppress("ArrayInDataClass") -internal data class InterfacePropertyBinding(private val allowedTypes: Array>) : NestedBinding { +internal data class InterfacePropertyBinding(private val allowedTypes: Array>) : Binding { override fun serialize(obj: Any, context: WriteContext) = throw IllegalStateException("InterfacePropertyBinding cannot be used as root binding") override fun deserialize(context: ReadContext) = throw IllegalStateException("InterfacePropertyBinding cannot be used as root binding") diff --git a/platform/object-serializer/src/IonBindingProducer.kt b/platform/object-serializer/src/IonBindingProducer.kt index 34d926ab14ff..16fe06db1f90 100644 --- a/platform/object-serializer/src/IonBindingProducer.kt +++ b/platform/object-serializer/src/IonBindingProducer.kt @@ -1,7 +1,6 @@ // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.serialization -import com.amazon.ion.IonType import com.amazon.ion.Timestamp import com.intellij.util.containers.ContainerUtil import gnu.trove.THashMap @@ -14,8 +13,8 @@ import java.nio.file.FileSystems import java.nio.file.Path import java.util.* -internal typealias NestedBindingFactory = (accessor: MutableAccessor) -> NestedBinding -internal typealias RootBindingFactory = () -> RootBinding +internal typealias NestedBindingFactory = (accessor: MutableAccessor) -> Binding +internal typealias RootBindingFactory = () -> Binding internal class IonBindingProducer(override val propertyCollector: PropertyCollector) : BindingProducer() { companion object { @@ -37,13 +36,13 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec registerPrimitiveBindings(classToRootBindingFactory, classToNestedBindingFactory) classToRootBindingFactory.forEachEntry { key, factory -> - classToNestedBindingFactory.put(key) { PropertyBinding(factory()) } + classToNestedBindingFactory.put(key) { factory() } true } } } - override fun createRootBinding(aClass: Class<*>, type: Type): RootBinding { + override fun createRootBinding(aClass: Class<*>, type: Type): Binding { val customFactory = classToRootBindingFactory.get(aClass) if (customFactory != null) { return customFactory.invoke() @@ -67,6 +66,7 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec aClass.isInterface || Modifier.isAbstract(aClass.modifiers) -> { PolymorphicBinding(aClass) } + java.lang.Number::class.java.isAssignableFrom(aClass) -> NumberAsObjectBinding() aClass is Proxy -> { throw SerializationException("$aClass class is not supported") } @@ -81,7 +81,7 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec } // note about field name - Ion binary writer interns string automatically, no need to intern (text writer doesn't support symbol tables) - override fun getNestedBinding(accessor: MutableAccessor): NestedBinding { + override fun getNestedBinding(accessor: MutableAccessor): Binding { val type = accessor.genericType val aClass = ClassUtil.typeToClass(type) @@ -92,13 +92,11 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec } return when { - aClass.isArray -> ArrayBinding(aClass.componentType, this) - java.lang.Number::class.java.isAssignableFrom(aClass) -> PropertyBinding(NumberAsObjectBinding()) aClass.isInterface || Modifier.isAbstract(aClass.modifiers) -> { val annotation = accessor.getAnnotation(Property::class.java) if (annotation == null) { // todo respect configuration - return PropertyBinding(getRootBinding(aClass, type)) + return getRootBinding(aClass, type) // throw SerializationException("Allowed types are not specified", linkedMapOf("accessor" to accessor)) } @@ -110,17 +108,13 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec InterfacePropertyBinding(allowedTypes) } else -> { - val binding = getRootBinding(aClass, type) - when (binding) { - is NestedBinding -> binding - else -> PropertyBinding(binding) - } + getRootBinding(aClass, type) } } } } -private class FileBinding : RootBinding { +private class FileBinding : Binding { override fun deserialize(context: ReadContext): Any { return File(context.reader.stringValue()) } @@ -130,7 +124,7 @@ private class FileBinding : RootBinding { } } -private class PathBinding : RootBinding { +private class PathBinding : Binding { override fun deserialize(context: ReadContext): Any { return FileSystems.getDefault().getPath(context.reader.stringValue()) } @@ -140,7 +134,7 @@ private class PathBinding : RootBinding { } } -private class EnumBinding(private val valueClass: Class>) : RootBinding { +private class EnumBinding(private val valueClass: Class>) : Binding { override fun deserialize(context: ReadContext): Any { val enumConstants = valueClass.enumConstants val value = context.reader.stringValue() @@ -154,7 +148,7 @@ private class EnumBinding(private val valueClass: Class>) : RootBind } } -private class DateBinding : RootBinding { +private class DateBinding : Binding { override fun serialize(obj: Any, context: WriteContext) { context.writer.writeTimestamp(Timestamp.forDateZ(obj as Date)) } @@ -164,33 +158,7 @@ private class DateBinding : RootBinding { } } -internal inline fun write(hostObject: Any, accessor: MutableAccessor, context: WriteContext, write: ValueWriter.(value: Any) -> Unit) { - val value = accessor.readUnsafe(hostObject) - if (context.filter.isSkipped(value)) { - return - } - - val writer = context.writer - writer.setFieldName(accessor.name) - if (value == null) { - writer.writeNull() - } - else { - writer.write(value) - } -} - -internal inline fun read(hostObject: Any, property: MutableAccessor, context: ReadContext, read: ValueReader.() -> Any) { - val type = context.reader.type - if (type == IonType.NULL) { - property.set(hostObject, null) - } - else { - property.set(hostObject, context.reader.read()) - } -} - -private class ByteArrayBinding : RootBinding { +private class ByteArrayBinding : Binding { override fun serialize(obj: Any, context: WriteContext) { context.writer.writeBlob(obj as ByteArray) } @@ -200,6 +168,6 @@ private class ByteArrayBinding : RootBinding { } } -internal fun createElementBindingByType(type: Type, context: BindingInitializationContext): RootBinding { +internal fun createElementBindingByType(type: Type, context: BindingInitializationContext): Binding { return context.bindingProducer.getRootBinding(ClassUtil.typeToClass(type), type) } \ No newline at end of file diff --git a/platform/object-serializer/src/MapBinding.kt b/platform/object-serializer/src/MapBinding.kt index 81217d053ce9..e6cecbabf165 100644 --- a/platform/object-serializer/src/MapBinding.kt +++ b/platform/object-serializer/src/MapBinding.kt @@ -7,18 +7,12 @@ import gnu.trove.THashMap import java.lang.reflect.Type import java.util.* -internal class MapBinding(keyType: Type, valueType: Type, context: BindingInitializationContext) : RootBinding, NestedBinding { +internal class MapBinding(keyType: Type, valueType: Type, context: BindingInitializationContext) : Binding { private val keyBinding = createElementBindingByType(keyType, context) private val valueBinding = createElementBindingByType(valueType, context) private val isKeyComparable = Comparable::class.java.isAssignableFrom(ClassUtil.typeToClass(keyType)) - override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) { - write(hostObject, property, context) { - serialize(it, context) - } - } - override fun serialize(obj: Any, context: WriteContext) { val writer = context.writer val map = obj as Map<*, *> @@ -103,7 +97,7 @@ internal class MapBinding(keyType: Type, valueType: Type, context: BindingInitia reader.stepOut() } - private fun read(type: IonType, binding: RootBinding, context: ReadContext): Any? { + private fun read(type: IonType, binding: Binding, context: ReadContext): Any? { return when (type) { IonType.NULL -> null else -> binding.deserialize(context) diff --git a/platform/object-serializer/src/PolymorphicBinding.kt b/platform/object-serializer/src/PolymorphicBinding.kt index be341fb9b830..da95ba128a2f 100644 --- a/platform/object-serializer/src/PolymorphicBinding.kt +++ b/platform/object-serializer/src/PolymorphicBinding.kt @@ -1,7 +1,7 @@ // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.serialization -internal class PolymorphicBinding(private val superClass: Class<*>) : RootBinding { +internal class PolymorphicBinding(private val superClass: Class<*>) : Binding { override fun serialize(obj: Any, context: WriteContext) { val valueClass = obj.javaClass val writer = context.writer diff --git a/platform/object-serializer/src/PropertyBinding.kt b/platform/object-serializer/src/PropertyBinding.kt deleted file mode 100644 index c71127991354..000000000000 --- a/platform/object-serializer/src/PropertyBinding.kt +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.intellij.serialization - -internal data class PropertyBinding(private val binding: RootBinding) : NestedBinding { - override fun serialize(obj: Any, context: WriteContext) { - binding.serialize(obj, context) - } - - override fun deserialize(context: ReadContext) = binding.deserialize(context) - - override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) { - write(hostObject, property, context) { - binding.serialize(it, context) - } - } - - override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { - read(hostObject, property, context) { - binding.deserialize(context) - } - } -} \ No newline at end of file diff --git a/platform/object-serializer/src/context.kt b/platform/object-serializer/src/context.kt index 717ecd46c8e7..9d2da9277c65 100644 --- a/platform/object-serializer/src/context.kt +++ b/platform/object-serializer/src/context.kt @@ -14,13 +14,13 @@ data class WriteConfiguration(val binary: Boolean = true, val orderMapEntriesByKeys: Boolean = false, val allowAnySubTypes: Boolean = false) -data class WriteContext(val writer: ValueWriter, - val filter: SerializationFilter, - val objectIdWriter: ObjectIdWriter?, - val configuration: WriteConfiguration, - val bindingProducer: BindingProducer) +internal data class WriteContext(val writer: ValueWriter, + val filter: SerializationFilter, + val objectIdWriter: ObjectIdWriter?, + val configuration: WriteConfiguration, + val bindingProducer: BindingProducer) -interface ReadContext { +internal interface ReadContext { val reader: ValueReader val objectIdReader: ObjectIdReader val bindingProducer: BindingProducer diff --git a/platform/object-serializer/src/primitiveBindings.kt b/platform/object-serializer/src/primitiveBindings.kt index 7391c2dc854b..4bc7cb844286 100644 --- a/platform/object-serializer/src/primitiveBindings.kt +++ b/platform/object-serializer/src/primitiveBindings.kt @@ -1,7 +1,9 @@ // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.serialization -private fun resolved(binding: NestedBinding): NestedBindingFactory = { binding } +import java.lang.reflect.Type + +private fun resolved(binding: Binding): NestedBindingFactory = { binding } internal fun registerPrimitiveBindings(classToRootBindingFactory: MutableMap, RootBindingFactory>, classToNestedBindingFactory: MutableMap, NestedBindingFactory>) { classToRootBindingFactory.put(java.lang.String::class.java) { StringBinding() } @@ -34,7 +36,7 @@ internal fun registerPrimitiveBindings(classToRootBindingFactory: MutableMap, type: Type) = aClass + override fun serialize(obj: Any, context: WriteContext) { context.writer.writeInt((obj as Number).toLong()) } @@ -58,7 +62,7 @@ internal class NumberAsObjectBinding : RootBinding { override fun deserialize(context: ReadContext) = context.reader.intValue() } -private class BooleanAsObjectBinding : RootBinding { +private class BooleanAsObjectBinding : Binding { override fun serialize(obj: Any, context: WriteContext) { context.writer.writeBool(obj as Boolean) } @@ -66,7 +70,7 @@ private class BooleanAsObjectBinding : RootBinding { override fun deserialize(context: ReadContext) = context.reader.booleanValue() } -private class BooleanBinding : NestedBinding, RootBinding { +private class BooleanBinding : Binding { override fun serialize(obj: Any, context: WriteContext) { context.writer.writeBool(obj as Boolean) } @@ -84,7 +88,7 @@ private class BooleanBinding : NestedBinding, RootBinding { } } -private open class IntBinding : NestedBinding, RootBinding { +private open class IntBinding : Binding { override fun serialize(obj: Any, context: WriteContext) { context.writer.writeInt(obj as Long) } @@ -108,7 +112,7 @@ private class ShortBinding : IntBinding() { } } -private class LongBinding : NestedBinding, RootBinding { +private class LongBinding : Binding { override fun serialize(obj: Any, context: WriteContext) { context.writer.writeInt(obj as Long) } @@ -126,7 +130,7 @@ private class LongBinding : NestedBinding, RootBinding { } } -private class FloatBinding : NestedBinding, RootBinding { +private class FloatBinding : Binding { override fun serialize(obj: Any, context: WriteContext) { context.writer.writeFloat(obj as Double) } @@ -144,7 +148,7 @@ private class FloatBinding : NestedBinding, RootBinding { } } -private class DoubleBinding : NestedBinding, RootBinding { +private class DoubleBinding : Binding { override fun serialize(obj: Any, context: WriteContext) { context.writer.writeFloat(obj as Double) } @@ -162,7 +166,7 @@ private class DoubleBinding : NestedBinding, RootBinding { } } -private class StringBinding : RootBinding { +private class StringBinding : Binding { override fun deserialize(context: ReadContext): Any { return context.reader.stringValue() } diff --git a/platform/object-serializer/testInternalSrc/InternalExposer.kt b/platform/object-serializer/testInternalSrc/InternalExposer.kt index 91ce23d841bb..ee38cc11952d 100644 --- a/platform/object-serializer/testInternalSrc/InternalExposer.kt +++ b/platform/object-serializer/testInternalSrc/InternalExposer.kt @@ -1,6 +1,8 @@ // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.serialization -fun getBindingProducer(serializer: ObjectSerializer): BindingProducer = serializer.serializer.bindingProducer +fun getBinding(aClass: Class<*>, serializer: ObjectSerializer): Any = serializer.serializer.bindingProducer.getRootBinding(aClass) -fun getBindingCount(producer: BindingProducer) = producer.bindingCount \ No newline at end of file +fun getBindingProducer(serializer: ObjectSerializer): Any = serializer.serializer.bindingProducer + +fun getBindingCount(producer: Any) = (producer as BindingProducer).bindingCount \ No newline at end of file diff --git a/platform/object-serializer/testSrc/ObjectSerializerTest.kt b/platform/object-serializer/testSrc/ObjectSerializerTest.kt index 11cd0a786094..5a817f2d9991 100644 --- a/platform/object-serializer/testSrc/ObjectSerializerTest.kt +++ b/platform/object-serializer/testSrc/ObjectSerializerTest.kt @@ -23,9 +23,9 @@ class ObjectSerializerTest { @Test fun `same bean binding regardless of type parameters`() { - val bindingProducer = getBindingProducer(ObjectSerializer()) - bindingProducer.getRootBinding(TestGenericBean::class.java) - assertThat(getBindingCount(bindingProducer)).isEqualTo(1) + val serializer = ObjectSerializer() + getBinding(TestGenericBean::class.java, serializer) + assertThat(getBindingCount(getBindingProducer(serializer))).isEqualTo(1) } @Test