From b6e3ff604a077fa991338fb939506bda5111a928 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Mon, 13 May 2019 20:50:59 +0200 Subject: [PATCH] unsafe polymorphic types GitOrigin-RevId: 4ff1cb0f480e018760fec4d934b6252a34b59a01 --- .../externalSystem/model/DataNode.java | 4 +- .../model/project/ContentRootData.java | 34 ++- .../externalSystem/model/serialization.kt | 10 +- .../manage/ExternalProjectsDataStorage.java | 4 +- .../externalSystem/model/DataNodeTest.kt | 2 +- platform/object-serializer/src/BeanBinding.kt | 8 +- .../src/CollectionBinding.kt | 6 +- .../src/IonBindingProducer.kt | 244 ++++-------------- .../src/IonObjectSerializer.kt | 28 +- platform/object-serializer/src/MapBinding.kt | 4 +- .../object-serializer/src/ObjectSerializer.kt | 55 ++-- .../src/PolymorphicBinding.kt | 37 +++ .../object-serializer/src/VersionedFile.kt | 7 +- platform/object-serializer/src/context.kt | 34 +++ .../src/primitiveBindings.kt | 179 +++++++++++++ ...terface_type_for_field___allowSubTypes.ion | 7 + ...ace_type_for_map_value___allowSubTypes.ion | 10 + .../object-serializer/testSrc/ListTest.kt | 2 +- .../testSrc/ObjectSerializerTest.kt | 29 ++- .../testSrc/ObjectSerializerTestSuite.kt | 4 +- .../DefaultExternalSourceDirectorySet.java | 96 +++---- .../model/DefaultGradleConfiguration.java | 49 ++-- .../gradle/model/DefaultGradleConvention.java | 5 + .../gradle/model/DefaultGradleExtension.java | 5 + .../gradle/model/DefaultGradleProperty.java | 42 ++- .../gradle/model/FilePatternSetImpl.java | 9 +- 26 files changed, 520 insertions(+), 394 deletions(-) create mode 100644 platform/object-serializer/src/PolymorphicBinding.kt create mode 100644 platform/object-serializer/src/context.kt create mode 100644 platform/object-serializer/src/primitiveBindings.kt create mode 100644 platform/object-serializer/testSnapshots/interface_type_for_field___allowSubTypes.ion create mode 100644 platform/object-serializer/testSnapshots/interface_type_for_map_value___allowSubTypes.ion diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java index ee4de947f771..9bf39cd42c2a 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DataNode.java @@ -124,7 +124,7 @@ public class DataNode implements UserDataHolderEx { try { MultiLoaderWrapper classLoader = new MultiLoaderWrapper(getClass().getClassLoader(), classLoaders); //noinspection unchecked - data = ObjectSerializer.getInstance().read((Class)classLoader.findClass(dataClassName), rawData, SerializationKt.getExternalSystemBeanConstructed()); + data = ObjectSerializer.getInstance().read((Class)classLoader.findClass(dataClassName), rawData, SerializationKt.createDataNodeReadConfiguration(classLoader)); assert data != null; clearRawData(); } @@ -232,7 +232,7 @@ public class DataNode implements UserDataHolderEx { else { LOG.assertTrue(!(data instanceof Proxy)); dataClassName = data.getClass().getName(); - rawData = ObjectSerializer.getInstance().writeAsBytes(data); + rawData = ObjectSerializer.getInstance().writeAsBytes(data, SerializationKt.getDataNodeWriteConfiguration()); } } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ContentRootData.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ContentRootData.java index e0812869642a..0c92fb5133ff 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ContentRootData.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ContentRootData.java @@ -92,24 +92,30 @@ public final class ContentRootData extends AbstractExternalEntityData { public static class SourceRoot implements Serializable { @NotNull - private final String myPath; + private final String path; @Nullable - private final String myPackagePrefix; + private final String packagePrefix; public SourceRoot(@NotNull String path, @Nullable String prefix) { - myPath = path; - myPackagePrefix = prefix; + this.path = path; + packagePrefix = prefix; + } + + @SuppressWarnings("unused") + private SourceRoot() { + path = ""; + packagePrefix = ""; } @NotNull public String getPath() { - return myPath; + return path; } @Nullable public String getPackagePrefix() { - return myPackagePrefix; + return packagePrefix; } @Override @@ -117,24 +123,24 @@ public final class ContentRootData extends AbstractExternalEntityData { if (this == o) return true; if (!(o instanceof SourceRoot)) return false; SourceRoot root = (SourceRoot)o; - if (myPackagePrefix != null ? !myPackagePrefix.equals(root.myPackagePrefix) : root.myPackagePrefix != null) return false; - if (!myPath.equals(root.myPath)) return false; + if (packagePrefix != null ? !packagePrefix.equals(root.packagePrefix) : root.packagePrefix != null) return false; + if (!path.equals(root.path)) return false; return true; } @Override public int hashCode() { - int result = myPath.hashCode(); - result = 31 * result + (myPackagePrefix != null ? myPackagePrefix.hashCode() : 0); + int result = path.hashCode(); + result = 31 * result + (packagePrefix != null ? packagePrefix.hashCode() : 0); return result; } @Override public String toString() { StringBuilder buffer = new StringBuilder("source_root("); - buffer.append(myPath); - if (myPackagePrefix != null) { - buffer.append(", ").append(myPackagePrefix); + buffer.append(path); + if (packagePrefix != null) { + buffer.append(", ").append(packagePrefix); } buffer.append(")"); return buffer.toString(); @@ -146,7 +152,7 @@ public final class ContentRootData extends AbstractExternalEntityData { @Override public int compare(@NotNull SourceRoot o1, @NotNull SourceRoot o2) { - return StringUtil.naturalCompare(o1.myPath, o2.myPath); + return StringUtil.naturalCompare(o1.path, o2.path); } } } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/serialization.kt b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/serialization.kt index b4fc47b7e6e8..62f1b7352dfd 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/serialization.kt +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/serialization.kt @@ -2,6 +2,8 @@ package com.intellij.openapi.externalSystem.model import com.intellij.serialization.BeanConstructed +import com.intellij.serialization.ReadConfiguration +import com.intellij.serialization.WriteConfiguration val externalSystemBeanConstructed: BeanConstructed = { if (it is ProjectSystemId) { @@ -10,4 +12,10 @@ val externalSystemBeanConstructed: BeanConstructed = { else { it } -} \ No newline at end of file +} + +fun createDataNodeReadConfiguration(classLoader: ClassLoader): ReadConfiguration { + return ReadConfiguration(allowAnySubTypes = true, classLoader = classLoader, beanConstructed = externalSystemBeanConstructed) +} + +val dataNodeWriteConfiguration = WriteConfiguration(allowAnySubTypes = true) \ No newline at end of file diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsDataStorage.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsDataStorage.java index d571ab0e0356..34ce4109daf6 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsDataStorage.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ExternalProjectsDataStorage.java @@ -54,7 +54,7 @@ import static com.intellij.openapi.externalSystem.model.ProjectKeys.PROJECT; public class ExternalProjectsDataStorage implements SettingsSavingComponentJavaAdapter, PersistentStateComponent { private static final Logger LOG = Logger.getInstance(ExternalProjectsDataStorage.class); - private static final int STORAGE_VERSION = 3; + private static final int STORAGE_VERSION = 4; @NotNull private final Project myProject; @@ -365,7 +365,7 @@ public class ExternalProjectsDataStorage implements SettingsSavingComponentJavaA if (isInvalidated(cacheFile.getFile(), fileAttributes)) { throw new IOException("External projects data storage was invalidated"); } - return ContainerUtil.notNullize(cacheFile.readList(InternalExternalProjectInfo.class)); + return ContainerUtil.notNullize(cacheFile.readList(InternalExternalProjectInfo.class, SerializationKt.getExternalSystemBeanConstructed())); } private static boolean isInvalidated(@NotNull Path configurationFile, @NotNull BasicFileAttributes fileAttributes) throws IOException { diff --git a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/model/DataNodeTest.kt b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/model/DataNodeTest.kt index 0f01f2fa802b..790db2685d47 100644 --- a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/model/DataNodeTest.kt +++ b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/model/DataNodeTest.kt @@ -65,7 +65,7 @@ class DataNodeTest { val out = BufferExposingByteArrayOutputStream() ObjectSerializer.instance.writeList(dataNodes, DataNode::class.java, out) val bytes = out.toByteArray() - val deserializedList = ObjectSerializer.instance.readList(DataNode::class.java, bytes, beanConstructed = externalSystemBeanConstructed) + val deserializedList = ObjectSerializer.instance.readList(DataNode::class.java, bytes, createDataNodeReadConfiguration(javaClass.classLoader)) assertThat(deserializedList).hasSize(2) assertThat(deserializedList[0].data === deserializedList[1].data) diff --git a/platform/object-serializer/src/BeanBinding.kt b/platform/object-serializer/src/BeanBinding.kt index 88f9317c5145..4c963f40715a 100644 --- a/platform/object-serializer/src/BeanBinding.kt +++ b/platform/object-serializer/src/BeanBinding.kt @@ -98,6 +98,10 @@ internal class BeanBinding(beanClass: Class<*>) : BaseBeanBinding(beanClass), Ro reader.next() val subReadContext = context.createSubContext(reader) readStruct(reader) { fieldName, type -> + if (type == IonType.NULL) { + return@readStruct + } + if (type == IonType.INT && fieldName == ID_FIELD_NAME) { id = reader.intValue() return@readStruct @@ -135,7 +139,7 @@ internal class BeanBinding(beanClass: Class<*>) : BaseBeanBinding(beanClass), Ro readIntoObject(instance, context.createSubContext(reader), checkId = false /* already registered */) { !names.contains(it) } } } - return context.beanConstructed?.let { it(instance) } ?: instance + return context.configuration.beanConstructed?.let { it(instance) } ?: instance } override fun deserialize(context: ReadContext): Any { @@ -166,7 +170,7 @@ internal class BeanBinding(beanClass: Class<*>) : BaseBeanBinding(beanClass), Ro readIntoObject(instance, context) - return context.beanConstructed?.let { it(instance) } ?: instance + return context.configuration.beanConstructed?.let { it(instance) } ?: instance } private fun readIntoObject(instance: Any, context: ReadContext, checkId: Boolean = true, filter: ((fieldName: String) -> Boolean)? = null) { diff --git a/platform/object-serializer/src/CollectionBinding.kt b/platform/object-serializer/src/CollectionBinding.kt index 2b269cfa4944..5737ab79bd2f 100644 --- a/platform/object-serializer/src/CollectionBinding.kt +++ b/platform/object-serializer/src/CollectionBinding.kt @@ -10,12 +10,8 @@ import java.lang.reflect.ParameterizedType import java.lang.reflect.Type import java.util.function.Consumer -internal fun createBindingByType(type: Type, context: BindingInitializationContext): RootBinding { - return context.bindingProducer.getRootBinding(ClassUtil.typeToClass(type), type) -} - internal abstract class BaseCollectionBinding(itemType: Type, context: BindingInitializationContext) : RootBinding, NestedBinding { - private val itemBinding = createBindingByType(itemType, context) + private val itemBinding = createElementBindingByType(itemType, context) final override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) { write(hostObject, property, context) { diff --git a/platform/object-serializer/src/IonBindingProducer.kt b/platform/object-serializer/src/IonBindingProducer.kt index 1f90df9d814c..43d267a99235 100644 --- a/platform/object-serializer/src/IonBindingProducer.kt +++ b/platform/object-serializer/src/IonBindingProducer.kt @@ -7,25 +7,23 @@ import com.intellij.util.SystemProperties import com.intellij.util.containers.ContainerUtil import gnu.trove.THashMap import java.io.File +import java.lang.reflect.Modifier import java.lang.reflect.ParameterizedType import java.lang.reflect.Type import java.nio.file.FileSystems import java.nio.file.Path import java.util.* -private typealias NestedBindingFactory = (accessor: MutableAccessor) -> NestedBinding -private typealias RootBindingFactory = () -> RootBinding +internal typealias NestedBindingFactory = (accessor: MutableAccessor) -> NestedBinding +internal typealias RootBindingFactory = () -> RootBinding internal class IonBindingProducer(override val propertyCollector: PropertyCollector) : BindingProducer(), BindingInitializationContext { companion object { private val classToNestedBindingFactory = THashMap, NestedBindingFactory>(32, ContainerUtil.identityStrategy()) private val classToRootBindingFactory = THashMap, RootBindingFactory>(32, ContainerUtil.identityStrategy()) - private fun resolved(binding: NestedBinding): NestedBindingFactory = { binding } - init { // for root resolved factory doesn't make sense because root bindings will be cached - classToRootBindingFactory.put(java.lang.String::class.java) { StringBinding() } classToRootBindingFactory.put(File::class.java) { FileBinding() } classToRootBindingFactory.put(Path::class.java) { PathBinding() } classToRootBindingFactory.put(Date::class.java) { DateBinding() } @@ -36,31 +34,10 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec classToRootBindingFactory.put(java.lang.Integer::class.java, numberFactory) classToRootBindingFactory.put(java.lang.Long::class.java, numberFactory) - // java.lang.Float cannot be cast to java.lang.Double - classToRootBindingFactory.put(java.lang.Float::class.java) { FloatAsObjectBinding() } - classToRootBindingFactory.put(java.lang.Double::class.java) { DoubleAsObjectBinding() } - classToRootBindingFactory.put(java.lang.Boolean::class.java) { BooleanAsObjectBinding() } - - val map = classToNestedBindingFactory - map.put(java.lang.Short.TYPE, resolved(ShortBinding())) - map.put(Integer.TYPE, resolved(IntBinding())) - map.put(java.lang.Long.TYPE, resolved(LongBinding())) - - map.put(java.lang.Float.TYPE, resolved(FloatBinding())) - map.put(java.lang.Double.TYPE, resolved(DoubleBinding())) - - map.put(java.lang.Boolean.TYPE, resolved(BooleanBinding())) - - val char: NestedBindingFactory = { throw UnsupportedOperationException("char is not supported") } - map.put(Character.TYPE, char) - map.put(Character::class.java, char) - - val byte: NestedBindingFactory = { throw UnsupportedOperationException("byte is not supported") } - map.put(java.lang.Byte.TYPE, byte) - map.put(java.lang.Byte::class.java, byte) + registerPrimitiveBindings(classToRootBindingFactory, classToNestedBindingFactory) classToRootBindingFactory.forEachEntry { key, factory -> - map.put(key) { PropertyBinding(factory()) } + classToNestedBindingFactory.put(key) { PropertyBinding(factory()) } true } } @@ -82,30 +59,7 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec } override fun createRootBinding(aClass: Class<*>, type: Type, cacheKey: Type, map: MutableMap): RootBinding { - val binding = if (Collection::class.java.isAssignableFrom(aClass)) { - createCollectionBinding(type) - } - else { - val custom = classToRootBindingFactory.get(aClass)?.invoke() - @Suppress("IfThenToElvis") - if (custom == null) { - when { - aClass.isArray -> ArrayBinding(aClass.componentType, this) - aClass.isEnum -> { - @Suppress("UNCHECKED_CAST") - EnumBinding(aClass as Class>) - } - else -> { - assert(cacheKey === aClass) - BeanBinding(aClass) - } - } - } - else { - custom - } - } - + val binding = doCreateRootBinding(aClass, type, cacheKey) map.put(cacheKey, binding) try { binding.init(type, this) @@ -117,6 +71,35 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec return binding } + private fun doCreateRootBinding(aClass: Class<*>, type: Type, cacheKey: Type): RootBinding { + val customFactory = classToRootBindingFactory.get(aClass) + if (customFactory != null) { + return customFactory.invoke() + } + + return when { + Collection::class.java.isAssignableFrom(aClass) -> createCollectionBinding(type) + Map::class.java.isAssignableFrom(aClass) -> { + val typeArguments = (type as ParameterizedType).actualTypeArguments + MapBinding(typeArguments[0], typeArguments[1], this) + } + aClass.isArray -> ArrayBinding(aClass.componentType, this) + aClass.isEnum -> { + @Suppress("UNCHECKED_CAST") + EnumBinding(aClass as Class>) + } + else -> { + assert(cacheKey === aClass) + if (aClass.isInterface || Modifier.isAbstract(aClass.modifiers)) { + PolymorphicBinding(aClass) + } + else { + BeanBinding(aClass) + } + } + } + } + // 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 { val type = accessor.genericType @@ -137,9 +120,14 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec } aClass.isArray -> ArrayBinding(aClass.componentType, this) java.lang.Number::class.java.isAssignableFrom(aClass) -> PropertyBinding(NumberAsObjectBinding()) - aClass.isInterface -> { + aClass.isInterface || Modifier.isAbstract(aClass.modifiers) -> { val annotation = accessor.getAnnotation(Property::class.java) - ?: throw SerializationException("Allowed types are not specified", linkedMapOf("accessor" to accessor)) + if (annotation == null) { + // todo respect configuration + return PropertyBinding(getRootBinding(aClass, type)) + // throw SerializationException("Allowed types are not specified", linkedMapOf("accessor" to accessor)) + } + // even if annotation in Java, Kotlin forces to use Klass, so, use java bridge val allowedTypes = PropertyAnnotationUtil.getAllowedClass(annotation) if (allowedTypes.isEmpty()) { @@ -158,150 +146,6 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec } } -private class FloatAsObjectBinding : RootBinding { - override fun serialize(obj: Any, context: WriteContext) { - context.writer.writeFloat((obj as Float).toDouble()) - } - - override fun deserialize(context: ReadContext) = context.reader.doubleValue().toFloat() -} - -private class DoubleAsObjectBinding : RootBinding { - override fun serialize(obj: Any, context: WriteContext) { - context.writer.writeFloat(obj as Double) - } - - override fun deserialize(context: ReadContext) = context.reader.doubleValue() -} - -private class NumberAsObjectBinding : RootBinding { - override fun serialize(obj: Any, context: WriteContext) { - context.writer.writeInt((obj as Number).toLong()) - } - - override fun deserialize(context: ReadContext) = context.reader.intValue() -} - -private class BooleanAsObjectBinding : RootBinding { - override fun serialize(obj: Any, context: WriteContext) { - context.writer.writeBool(obj as Boolean) - } - - override fun deserialize(context: ReadContext) = context.reader.booleanValue() -} - -private class BooleanBinding : NestedBinding, RootBinding { - override fun serialize(obj: Any, context: WriteContext) { - context.writer.writeBool(obj as Boolean) - } - - override fun deserialize(context: ReadContext) = context.reader.booleanValue() - - override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) { - val writer = context.writer - writer.setFieldName(property.name) - writer.writeBool(property.readBoolean(hostObject)) - } - - override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { - property.setBoolean(hostObject, context.reader.booleanValue()) - } -} - -private open class IntBinding : NestedBinding, RootBinding { - override fun serialize(obj: Any, context: WriteContext) { - context.writer.writeInt(obj as Long) - } - - override fun deserialize(context: ReadContext) = context.reader.intValue() - - override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) { - val writer = context.writer - writer.setFieldName(property.name) - writer.writeInt(property.readInt(hostObject).toLong()) - } - - override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { - property.setInt(hostObject, context.reader.intValue()) - } -} - -private class ShortBinding : IntBinding() { - override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { - property.setShort(hostObject, context.reader.intValue().toShort()) - } -} - -private class LongBinding : NestedBinding, RootBinding { - override fun serialize(obj: Any, context: WriteContext) { - context.writer.writeInt(obj as Long) - } - - override fun deserialize(context: ReadContext) = context.reader.longValue() - - override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) { - val writer = context.writer - writer.setFieldName(property.name) - writer.writeInt(property.readLong(hostObject)) - } - - override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { - property.setLong(hostObject, context.reader.longValue()) - } -} - -private class FloatBinding : NestedBinding, RootBinding { - override fun serialize(obj: Any, context: WriteContext) { - context.writer.writeFloat(obj as Double) - } - - override fun deserialize(context: ReadContext) = context.reader.doubleValue() - - override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) { - val writer = context.writer - writer.setFieldName(property.name) - writer.writeFloat(property.readFloat(hostObject).toDouble()) - } - - override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { - property.setFloat(hostObject, context.reader.doubleValue().toFloat()) - } -} - -private class DoubleBinding : NestedBinding, RootBinding { - override fun serialize(obj: Any, context: WriteContext) { - context.writer.writeFloat(obj as Double) - } - - override fun deserialize(context: ReadContext) = context.reader.doubleValue() - - override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) { - val writer = context.writer - writer.setFieldName(property.name) - writer.writeFloat(property.readDouble(hostObject)) - } - - override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { - property.setDouble(hostObject, context.reader.doubleValue()) - } -} - -private class StringBinding : RootBinding { - override fun deserialize(context: ReadContext): Any { - return context.reader.stringValue() - } - - override fun serialize(obj: Any, context: WriteContext) { - val s = obj as String - if (s.length < 64) { - context.writer.writeSymbol(s) - } - else { - context.writer.writeString(s) - } - } -} - private class FileBinding : RootBinding { override fun deserialize(context: ReadContext): Any { return File(context.reader.stringValue()) @@ -380,4 +224,8 @@ private class ByteArrayBinding : RootBinding { override fun deserialize(context: ReadContext): Any { return context.reader.newBytes() } +} + +internal fun createElementBindingByType(type: Type, context: BindingInitializationContext): RootBinding { + return context.bindingProducer.getRootBinding(ClassUtil.typeToClass(type), type) } \ No newline at end of file diff --git a/platform/object-serializer/src/IonObjectSerializer.kt b/platform/object-serializer/src/IonObjectSerializer.kt index e003348d6228..344a678154ff 100644 --- a/platform/object-serializer/src/IonObjectSerializer.kt +++ b/platform/object-serializer/src/IonObjectSerializer.kt @@ -19,7 +19,7 @@ import java.lang.reflect.Type import java.nio.file.Path import kotlin.experimental.or -private const val FORMAT_VERSION = 0 +private const val FORMAT_VERSION = 1 internal class IonObjectSerializer { val readerBuilder: IonReaderBuilder = IonReaderBuilder.standard().immutable() @@ -48,7 +48,7 @@ internal class IonObjectSerializer { } @Throws(IOException::class) - fun readVersioned(objectClass: Class, file: Path, expectedVersion: Int, beanConstructed: BeanConstructed? = null, originalType: Type? = null): T? { + fun readVersioned(objectClass: Class, file: Path, expectedVersion: Int, configuration: ReadConfiguration, originalType: Type? = null): T? { readerBuilder.build(file.inputStream().buffered()).use { reader -> @Suppress("UNUSED_VARIABLE") var isVersionChecked = 0 @@ -93,7 +93,7 @@ internal class IonObjectSerializer { return null } - return doRead(objectClass, originalType, reader, beanConstructed) + return doRead(objectClass, originalType, reader, configuration) } else -> LOG.warn("Unknown field: $fieldName (file=$file, expectedVersion=$expectedVersion, objectClass=$objectClass)") } @@ -116,33 +116,33 @@ internal class IonObjectSerializer { bindingProducer.getRootBinding(aClass, originalType ?: aClass).serialize(obj, writeContext) } - fun read(objectClass: Class, reader: ValueReader, beanConstructed: BeanConstructed? = null, originalType: Type? = null): T { + fun read(objectClass: Class, reader: ValueReader, configuration: ReadConfiguration, originalType: Type? = null): T { reader.use { reader.next() - return doRead(objectClass, originalType, reader, beanConstructed) + return doRead(objectClass, originalType, reader, configuration) } } // reader cursor must be already pointed to struct - private fun doRead(objectClass: Class, originalType: Type?, reader: ValueReader, beanConstructed: BeanConstructed?): T { + private fun doRead(objectClass: Class, originalType: Type?, reader: ValueReader, configuration: ReadConfiguration): T { when (reader.type) { IonType.NULL -> throw SerializationException("root value is null") null -> throw SerializationException("empty input") else -> { + val binding = bindingProducer.getRootBinding(objectClass, originalType ?: objectClass) @Suppress("UNCHECKED_CAST") - return bindingProducer.getRootBinding(objectClass, originalType ?: objectClass).deserialize( - createReadContext(reader, beanConstructed)) as T + return binding.deserialize(createReadContext(reader, configuration)) as T } } } - fun readList(itemClass: Class, reader: ValueReader, beanConstructed: BeanConstructed?): List { + fun readList(itemClass: Class, reader: ValueReader, configuration: ReadConfiguration): List { @Suppress("UNCHECKED_CAST") - return read(List::class.java, reader, beanConstructed, ParameterizedTypeImpl(List::class.java, itemClass)) as List + return read(List::class.java, reader, configuration, ParameterizedTypeImpl(List::class.java, itemClass)) as List } - private fun createReadContext(reader: ValueReader, beanConstructed: BeanConstructed? = null): ReadContext { - return ReadContextImpl(reader, ObjectIdReader(), bindingProducer, beanConstructed) + private fun createReadContext(reader: ValueReader, configuration: ReadConfiguration): ReadContext { + return ReadContextImpl(reader, ObjectIdReader(), bindingProducer, configuration) } } @@ -153,7 +153,7 @@ private val DEFAULT_FILTER = object : SerializationFilter { private data class ReadContextImpl(override val reader: ValueReader, override val objectIdReader: ObjectIdReader, override val bindingProducer: BindingProducer, - override val beanConstructed: BeanConstructed?) : ReadContext { + override val configuration: ReadConfiguration) : ReadContext { private var byteArrayOutputStream: BufferExposingByteArrayOutputStream? = null override fun allocateByteArrayOutputStream(): BufferExposingByteArrayOutputStream { @@ -168,7 +168,7 @@ private data class ReadContextImpl(override val reader: ValueReader, return result } - override fun createSubContext(reader: ValueReader) = ReadContextImpl(reader, objectIdReader, bindingProducer, beanConstructed) + override fun createSubContext(reader: ValueReader) = ReadContextImpl(reader, objectIdReader, bindingProducer, configuration) } private val binaryWriterBuilder by lazy { IonBinaryWriterBuilder.standard().immutable() } diff --git a/platform/object-serializer/src/MapBinding.kt b/platform/object-serializer/src/MapBinding.kt index 5b01944221eb..c3f373ece853 100644 --- a/platform/object-serializer/src/MapBinding.kt +++ b/platform/object-serializer/src/MapBinding.kt @@ -8,8 +8,8 @@ import java.lang.reflect.Type import java.util.* internal class MapBinding(keyType: Type, valueType: Type, context: BindingInitializationContext) : RootBinding, NestedBinding { - private val keyBinding = createBindingByType(keyType, context) - private val valueBinding = createBindingByType(valueType, context) + private val keyBinding = createElementBindingByType(keyType, context) + private val valueBinding = createElementBindingByType(valueType, context) private val isKeyComparable = Comparable::class.java.isAssignableFrom(ClassUtil.typeToClass(keyType)) diff --git a/platform/object-serializer/src/ObjectSerializer.kt b/platform/object-serializer/src/ObjectSerializer.kt index 8c4a63b0f484..089f7f54223a 100644 --- a/platform/object-serializer/src/ObjectSerializer.kt +++ b/platform/object-serializer/src/ObjectSerializer.kt @@ -25,6 +25,8 @@ annotation class PropertyMapping(val value: Array) internal val defaultWriteConfiguration = WriteConfiguration() +val defaultReadConfiguration = ReadConfiguration() + /** * @see [VersionedFile] */ @@ -56,67 +58,42 @@ class ObjectSerializer { serializer.write(obj, outputStream, configuration, ParameterizedTypeImpl(Collection::class.java, itemClass)) } - fun read(objectClass: Class, bytes: ByteArray, beanConstructed: BeanConstructed? = null): T { - return serializer.read(objectClass, readerBuilder.build(bytes), beanConstructed) + fun read(objectClass: Class, bytes: ByteArray, configuration: ReadConfiguration = defaultReadConfiguration): T { + return serializer.read(objectClass, readerBuilder.build(bytes), configuration) } - fun read(objectClass: Class, inputStream: InputStream): T { - return serializer.read(objectClass, readerBuilder.build(inputStream)) + fun read(objectClass: Class, inputStream: InputStream, configuration: ReadConfiguration = defaultReadConfiguration): T { + return serializer.read(objectClass, readerBuilder.build(inputStream), configuration) } - fun read(objectClass: Class, reader: Reader): T { - return serializer.read(objectClass, readerBuilder.build(reader)) + fun read(objectClass: Class, reader: Reader, configuration: ReadConfiguration = defaultReadConfiguration): T { + return serializer.read(objectClass, readerBuilder.build(reader), configuration) } - fun read(objectClass: Class, text: String): T { - return serializer.read(objectClass, readerBuilder.build(text)) + fun read(objectClass: Class, text: String, configuration: ReadConfiguration = defaultReadConfiguration): T { + return serializer.read(objectClass, readerBuilder.build(text), configuration) } - fun readList(itemClass: Class, reader: Reader, beanConstructed: BeanConstructed? = null): List { - return serializer.readList(itemClass, readerBuilder.build(reader), beanConstructed) + fun readList(itemClass: Class, reader: Reader, configuration: ReadConfiguration = defaultReadConfiguration): List { + return serializer.readList(itemClass, readerBuilder.build(reader), configuration) } @JvmOverloads - fun readList(itemClass: Class, bytes: ByteArray, beanConstructed: BeanConstructed? = null): List { - return serializer.readList(itemClass, readerBuilder.build(bytes), beanConstructed) + fun readList(itemClass: Class, bytes: ByteArray, configuration: ReadConfiguration = defaultReadConfiguration): List { + return serializer.readList(itemClass, readerBuilder.build(bytes), configuration) } @JvmOverloads - fun readList(itemClass: Class, input: InputStream, beanConstructed: BeanConstructed? = null): List { - return serializer.readList(itemClass, readerBuilder.build(input), beanConstructed) + fun readList(itemClass: Class, input: InputStream, configuration: ReadConfiguration = defaultReadConfiguration): List { + return serializer.readList(itemClass, readerBuilder.build(input), configuration) } } -data class WriteConfiguration(val binary: Boolean = true, - val filter: SerializationFilter? = null, - val orderMapEntriesByKeys: Boolean = false) - // not finished concept because not required for object graph serialization interface SerializationFilter { fun isSkipped(value: Any?): Boolean } -data class WriteContext(val writer: ValueWriter, - val filter: SerializationFilter, - val objectIdWriter: ObjectIdWriter?, - val configuration: WriteConfiguration, - val bindingProducer: BindingProducer) - -interface ReadContext { - val reader: ValueReader - val objectIdReader: ObjectIdReader - val bindingProducer: BindingProducer - - val beanConstructed: BeanConstructed? - - /** - * Each call will reset previously allocated result. For sub readers it is not a problem, because you must use [createSubContext] for this case. - */ - fun allocateByteArrayOutputStream(): BufferExposingByteArrayOutputStream - - fun createSubContext(reader: ValueReader): ReadContext -} - class ObjectIdWriter { private val map: ObjectIntHashMap = ObjectIntHashMap(TObjectHashingStrategy.IDENTITY) private var counter = 0 diff --git a/platform/object-serializer/src/PolymorphicBinding.kt b/platform/object-serializer/src/PolymorphicBinding.kt new file mode 100644 index 000000000000..be341fb9b830 --- /dev/null +++ b/platform/object-serializer/src/PolymorphicBinding.kt @@ -0,0 +1,37 @@ +// 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 { + override fun serialize(obj: Any, context: WriteContext) { + val valueClass = obj.javaClass + val writer = context.writer + + if (!context.configuration.allowAnySubTypes) { + throw SerializationException("Polymorphic type without specified allowed sub types is forbidden") + } + + writer.addTypeAnnotation(valueClass.name) + context.bindingProducer.getRootBinding(valueClass).serialize(obj, context) + } + + override fun deserialize(context: ReadContext): Any { + if (!context.configuration.allowAnySubTypes) { + throw SerializationException("Polymorphic type without specified allowed sub types is forbidden") + } + + val reader = context.reader + val beanClass: Class<*> + val typeAnnotationIterator = reader.iterateTypeAnnotations() + if (typeAnnotationIterator.hasNext()) { + val className = typeAnnotationIterator.next() + beanClass = (context.configuration.classLoader ?: javaClass.classLoader).loadClass(className) + if (!superClass.isAssignableFrom(beanClass)) { + throw SerializationException("Class \"$className\" must be assignable to \"${superClass.name}\"") + } + } + else { + throw SerializationException("Class name is not specified") + } + return context.bindingProducer.getRootBinding(beanClass).deserialize(context) + } +} \ No newline at end of file diff --git a/platform/object-serializer/src/VersionedFile.kt b/platform/object-serializer/src/VersionedFile.kt index d8a91ece149c..6fdcf733a28c 100644 --- a/platform/object-serializer/src/VersionedFile.kt +++ b/platform/object-serializer/src/VersionedFile.kt @@ -13,8 +13,11 @@ data class VersionedFile(val file: Path, val version: Int) { } @Throws(IOException::class) - fun readList(itemClass: Class): List? { + @JvmOverloads + fun readList(itemClass: Class, beanConstructed: BeanConstructed? = null): List? { @Suppress("UNCHECKED_CAST") - return ObjectSerializer.instance.serializer.readVersioned(ArrayList::class.java, file, version, originalType = ParameterizedTypeImpl(ArrayList::class.java, itemClass)) as List? + return ObjectSerializer.instance.serializer.readVersioned(ArrayList::class.java, file, version, + originalType = ParameterizedTypeImpl(ArrayList::class.java, itemClass), + configuration = ReadConfiguration(beanConstructed = beanConstructed)) as List? } } diff --git a/platform/object-serializer/src/context.kt b/platform/object-serializer/src/context.kt new file mode 100644 index 000000000000..7583e20a1abb --- /dev/null +++ b/platform/object-serializer/src/context.kt @@ -0,0 +1,34 @@ +// 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.intellij.openapi.util.io.BufferExposingByteArrayOutputStream + +data class ReadConfiguration(val allowAnySubTypes: Boolean = false, + val classLoader: ClassLoader? = null, + val beanConstructed: BeanConstructed? = null) + +data class WriteConfiguration(val binary: Boolean = true, + val filter: SerializationFilter? = null, + 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) + +interface ReadContext { + val reader: ValueReader + val objectIdReader: ObjectIdReader + val bindingProducer: BindingProducer + + val configuration: ReadConfiguration + + /** + * Each call will reset previously allocated result. For sub readers it is not a problem, because you must use [createSubContext] for this case. + */ + fun allocateByteArrayOutputStream(): BufferExposingByteArrayOutputStream + + fun createSubContext(reader: ValueReader): ReadContext +} \ No newline at end of file diff --git a/platform/object-serializer/src/primitiveBindings.kt b/platform/object-serializer/src/primitiveBindings.kt new file mode 100644 index 000000000000..7391c2dc854b --- /dev/null +++ b/platform/object-serializer/src/primitiveBindings.kt @@ -0,0 +1,179 @@ +// 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 } + +internal fun registerPrimitiveBindings(classToRootBindingFactory: MutableMap, RootBindingFactory>, classToNestedBindingFactory: MutableMap, NestedBindingFactory>) { + classToRootBindingFactory.put(java.lang.String::class.java) { StringBinding() } + + val numberAsObjectBinding = NumberAsObjectBinding() + classToRootBindingFactory.put(java.lang.Integer::class.java) { numberAsObjectBinding } + classToRootBindingFactory.put(java.lang.Long::class.java) { numberAsObjectBinding } + classToRootBindingFactory.put(java.lang.Short::class.java) { numberAsObjectBinding } + + // java.lang.Float cannot be cast to java.lang.Double + classToRootBindingFactory.put(java.lang.Float::class.java) { FloatAsObjectBinding() } + classToRootBindingFactory.put(java.lang.Double::class.java) { DoubleAsObjectBinding() } + classToRootBindingFactory.put(java.lang.Boolean::class.java) { BooleanAsObjectBinding() } + + classToNestedBindingFactory.put(java.lang.Short.TYPE, resolved(ShortBinding())) + classToNestedBindingFactory.put(Integer.TYPE, resolved(IntBinding())) + classToNestedBindingFactory.put(java.lang.Long.TYPE, resolved(LongBinding())) + + classToNestedBindingFactory.put(java.lang.Float.TYPE, resolved(FloatBinding())) + classToNestedBindingFactory.put(java.lang.Double.TYPE, resolved(DoubleBinding())) + + classToNestedBindingFactory.put(java.lang.Boolean.TYPE, resolved(BooleanBinding())) + + val char: NestedBindingFactory = { throw UnsupportedOperationException("char is not supported") } + classToNestedBindingFactory.put(Character.TYPE, char) + classToNestedBindingFactory.put(Character::class.java, char) + + val byte: NestedBindingFactory = { throw UnsupportedOperationException("byte is not supported") } + classToNestedBindingFactory.put(java.lang.Byte.TYPE, byte) + classToNestedBindingFactory.put(java.lang.Byte::class.java, byte) +} + +private class FloatAsObjectBinding : RootBinding { + override fun serialize(obj: Any, context: WriteContext) { + context.writer.writeFloat((obj as Float).toDouble()) + } + + override fun deserialize(context: ReadContext) = context.reader.doubleValue().toFloat() +} + +private class DoubleAsObjectBinding : RootBinding { + override fun serialize(obj: Any, context: WriteContext) { + context.writer.writeFloat(obj as Double) + } + + override fun deserialize(context: ReadContext) = context.reader.doubleValue() +} + +internal class NumberAsObjectBinding : RootBinding { + override fun serialize(obj: Any, context: WriteContext) { + context.writer.writeInt((obj as Number).toLong()) + } + + override fun deserialize(context: ReadContext) = context.reader.intValue() +} + +private class BooleanAsObjectBinding : RootBinding { + override fun serialize(obj: Any, context: WriteContext) { + context.writer.writeBool(obj as Boolean) + } + + override fun deserialize(context: ReadContext) = context.reader.booleanValue() +} + +private class BooleanBinding : NestedBinding, RootBinding { + override fun serialize(obj: Any, context: WriteContext) { + context.writer.writeBool(obj as Boolean) + } + + override fun deserialize(context: ReadContext) = context.reader.booleanValue() + + override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) { + val writer = context.writer + writer.setFieldName(property.name) + writer.writeBool(property.readBoolean(hostObject)) + } + + override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { + property.setBoolean(hostObject, context.reader.booleanValue()) + } +} + +private open class IntBinding : NestedBinding, RootBinding { + override fun serialize(obj: Any, context: WriteContext) { + context.writer.writeInt(obj as Long) + } + + override fun deserialize(context: ReadContext) = context.reader.intValue() + + override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) { + val writer = context.writer + writer.setFieldName(property.name) + writer.writeInt(property.readInt(hostObject).toLong()) + } + + override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { + property.setInt(hostObject, context.reader.intValue()) + } +} + +private class ShortBinding : IntBinding() { + override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { + property.setShort(hostObject, context.reader.intValue().toShort()) + } +} + +private class LongBinding : NestedBinding, RootBinding { + override fun serialize(obj: Any, context: WriteContext) { + context.writer.writeInt(obj as Long) + } + + override fun deserialize(context: ReadContext) = context.reader.longValue() + + override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) { + val writer = context.writer + writer.setFieldName(property.name) + writer.writeInt(property.readLong(hostObject)) + } + + override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { + property.setLong(hostObject, context.reader.longValue()) + } +} + +private class FloatBinding : NestedBinding, RootBinding { + override fun serialize(obj: Any, context: WriteContext) { + context.writer.writeFloat(obj as Double) + } + + override fun deserialize(context: ReadContext) = context.reader.doubleValue() + + override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) { + val writer = context.writer + writer.setFieldName(property.name) + writer.writeFloat(property.readFloat(hostObject).toDouble()) + } + + override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { + property.setFloat(hostObject, context.reader.doubleValue().toFloat()) + } +} + +private class DoubleBinding : NestedBinding, RootBinding { + override fun serialize(obj: Any, context: WriteContext) { + context.writer.writeFloat(obj as Double) + } + + override fun deserialize(context: ReadContext) = context.reader.doubleValue() + + override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) { + val writer = context.writer + writer.setFieldName(property.name) + writer.writeFloat(property.readDouble(hostObject)) + } + + override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) { + property.setDouble(hostObject, context.reader.doubleValue()) + } +} + +private class StringBinding : RootBinding { + override fun deserialize(context: ReadContext): Any { + return context.reader.stringValue() + } + + override fun serialize(obj: Any, context: WriteContext) { + val s = obj as String + if (s.length < 64) { + context.writer.writeSymbol(s) + } + else { + context.writer.writeString(s) + } + } +} \ No newline at end of file diff --git a/platform/object-serializer/testSnapshots/interface_type_for_field___allowSubTypes.ion b/platform/object-serializer/testSnapshots/interface_type_for_field___allowSubTypes.ion new file mode 100644 index 000000000000..e5f8b28fa90a --- /dev/null +++ b/platform/object-serializer/testSnapshots/interface_type_for_field___allowSubTypes.ion @@ -0,0 +1,7 @@ +{ + '@id':0, + shape:'com.intellij.serialization.Circle'::{ + '@id':1, + name:null + } +} \ No newline at end of file diff --git a/platform/object-serializer/testSnapshots/interface_type_for_map_value___allowSubTypes.ion b/platform/object-serializer/testSnapshots/interface_type_for_map_value___allowSubTypes.ion new file mode 100644 index 000000000000..e175c9839fe3 --- /dev/null +++ b/platform/object-serializer/testSnapshots/interface_type_for_map_value___allowSubTypes.ion @@ -0,0 +1,10 @@ +{ + '@id':0, + shape:[ + first, + 'com.intellij.serialization.Circle'::{ + '@id':1, + name:null + } + ] +} \ No newline at end of file diff --git a/platform/object-serializer/testSrc/ListTest.kt b/platform/object-serializer/testSrc/ListTest.kt index a06c2591220f..ed5bc7728991 100644 --- a/platform/object-serializer/testSrc/ListTest.kt +++ b/platform/object-serializer/testSrc/ListTest.kt @@ -78,7 +78,7 @@ class ListTest { assertThat(file.file.readChars().trim()).isEqualTo(""" { version:42, - formatVersion:0, + formatVersion:1, data:[ foo, bar diff --git a/platform/object-serializer/testSrc/ObjectSerializerTest.kt b/platform/object-serializer/testSrc/ObjectSerializerTest.kt index 47fdec41f8c5..f35391f108ec 100644 --- a/platform/object-serializer/testSrc/ObjectSerializerTest.kt +++ b/platform/object-serializer/testSrc/ObjectSerializerTest.kt @@ -3,6 +3,7 @@ package com.intellij.serialization import com.intellij.openapi.util.SystemInfoRt import com.intellij.testFramework.assertions.Assertions.assertThat +import gnu.trove.THashMap import org.junit.Assume.assumeTrue import org.junit.Rule import org.junit.Test @@ -16,7 +17,9 @@ class ObjectSerializerTest { @JvmField val testName = TestName() - private fun test(bean: Any) = test(bean, testName) + private fun test(bean: T, writeConfiguration: WriteConfiguration? = null): T { + return test(bean, testName, writeConfiguration) + } @Test fun `same bean binding regardless of type parameters`() { @@ -137,6 +140,30 @@ class ObjectSerializerTest { bean.shape = Circle() test(bean) } + + @Test + fun `interface type for map value - allowSubTypes`() { + class TestInterfaceBean { + @JvmField + val shape: MutableMap = THashMap() + } + + val bean = TestInterfaceBean() + bean.shape.put("first", Circle()) + test(bean, WriteConfiguration(allowAnySubTypes = true, binary = false)) + } + + @Test + fun `interface type for field - allowSubTypes`() { + class TestInterfaceBean { + @JvmField + var shape: Shape? = null + } + + val bean = TestInterfaceBean() + bean.shape = Circle() + test(bean, WriteConfiguration(allowAnySubTypes = true, binary = false)) + } } private interface Shape diff --git a/platform/object-serializer/testSrc/ObjectSerializerTestSuite.kt b/platform/object-serializer/testSrc/ObjectSerializerTestSuite.kt index 797ca5c95d23..37db0a1ef268 100644 --- a/platform/object-serializer/testSrc/ObjectSerializerTestSuite.kt +++ b/platform/object-serializer/testSrc/ObjectSerializerTestSuite.kt @@ -34,7 +34,7 @@ internal fun test(bean: T, testName: TestName, _writeConfiguration: Wr val out = BufferExposingByteArrayOutputStream(8 * 1024) // just to test binary - objectSerializer.write(bean, out, WriteConfiguration(binary = true)) + objectSerializer.write(bean, out, WriteConfiguration(binary = true, allowAnySubTypes = _writeConfiguration?.allowAnySubTypes ?: false)) assertThat(out.size() > 0) out.reset() @@ -44,7 +44,7 @@ internal fun test(bean: T, testName: TestName, _writeConfiguration: Wr val ionText = out.toString() out.reset() - val deserializedBean = objectSerializer.read(bean.javaClass, ionText) + val deserializedBean = objectSerializer.read(bean.javaClass, ionText, configuration = ReadConfiguration(allowAnySubTypes = writeConfiguration.allowAnySubTypes)) objectSerializer.write(deserializedBean, out, writeConfiguration) assertThat(out.toString()).isEqualTo(ionText) diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalSourceDirectorySet.java b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalSourceDirectorySet.java index ec4f26ce5ea3..92d207d74577 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalSourceDirectorySet.java +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultExternalSourceDirectorySet.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// 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 org.jetbrains.plugins.gradle.model; import org.jetbrains.annotations.NotNull; @@ -27,130 +13,130 @@ public class DefaultExternalSourceDirectorySet implements ExternalSourceDirector private static final long serialVersionUID = 1L; @NotNull - private String myName; + private String name; @NotNull - private Set mySrcDirs; - private File myOutputDir; - private final List myGradleOutputDirs; - private final FilePatternSet myPatterns; + private Set srcDirs; + private File outputDir; + private final List gradleOutputDirs; + private final FilePatternSet patterns; @NotNull - private List myFilters; + private List filters; - private boolean myInheritedCompilerOutput; + private boolean inheritedCompilerOutput; public DefaultExternalSourceDirectorySet() { - mySrcDirs = new HashSet(); - myFilters = new ArrayList(); - myGradleOutputDirs = new ArrayList(); - myPatterns = new FilePatternSetImpl(new LinkedHashSet(), new LinkedHashSet()); + srcDirs = new HashSet(); + filters = new ArrayList(); + gradleOutputDirs = new ArrayList(); + patterns = new FilePatternSetImpl(new LinkedHashSet(), new LinkedHashSet()); } public DefaultExternalSourceDirectorySet(ExternalSourceDirectorySet sourceDirectorySet) { this(); - myName = sourceDirectorySet.getName(); - mySrcDirs = new HashSet(sourceDirectorySet.getSrcDirs()); - myOutputDir = sourceDirectorySet.getOutputDir(); - myGradleOutputDirs.addAll(sourceDirectorySet.getGradleOutputDirs()); + name = sourceDirectorySet.getName(); + srcDirs = new HashSet(sourceDirectorySet.getSrcDirs()); + outputDir = sourceDirectorySet.getOutputDir(); + gradleOutputDirs.addAll(sourceDirectorySet.getGradleOutputDirs()); - myPatterns.getIncludes().addAll(sourceDirectorySet.getPatterns().getIncludes()); - myPatterns.getExcludes().addAll(sourceDirectorySet.getPatterns().getExcludes()); + patterns.getIncludes().addAll(sourceDirectorySet.getPatterns().getIncludes()); + patterns.getExcludes().addAll(sourceDirectorySet.getPatterns().getExcludes()); for (ExternalFilter filter : sourceDirectorySet.getFilters()) { - myFilters.add(new DefaultExternalFilter(filter)); + filters.add(new DefaultExternalFilter(filter)); } - myInheritedCompilerOutput = sourceDirectorySet.isCompilerOutputPathInherited(); + inheritedCompilerOutput = sourceDirectorySet.isCompilerOutputPathInherited(); } @NotNull @Override public String getName() { - return myName; + return name; } public void setName(@NotNull String name) { - myName = name; + this.name = name; } @NotNull @Override public Set getSrcDirs() { - return mySrcDirs; + return srcDirs; } public void setSrcDirs(@NotNull Set srcDirs) { - mySrcDirs = srcDirs; + this.srcDirs = srcDirs; } @NotNull @Override public File getOutputDir() { - return myOutputDir; + return outputDir; } public void setOutputDir(@NotNull File outputDir) { - myOutputDir = outputDir; + this.outputDir = outputDir; } @NotNull @Override public File getGradleOutputDir() { - assert myGradleOutputDirs.size() > 0; - return myGradleOutputDirs.get(0); + assert gradleOutputDirs.size() > 0; + return gradleOutputDirs.get(0); } @NotNull @Override public Collection getGradleOutputDirs() { - return myGradleOutputDirs; + return gradleOutputDirs; } public void addGradleOutputDir(@NotNull File outputDir) { - myGradleOutputDirs.add(outputDir); + gradleOutputDirs.add(outputDir); } @Override public boolean isCompilerOutputPathInherited() { - return myInheritedCompilerOutput; + return inheritedCompilerOutput; } @NotNull @Override public Set getExcludes() { - return myPatterns.getExcludes(); + return patterns.getExcludes(); } public void setExcludes(Set excludes) { - myPatterns.getExcludes().clear(); - myPatterns.getExcludes().addAll(excludes); + patterns.getExcludes().clear(); + patterns.getExcludes().addAll(excludes); } @NotNull @Override public Set getIncludes() { - return myPatterns.getIncludes(); + return patterns.getIncludes(); } public void setIncludes(Set includes) { - myPatterns.getIncludes().clear(); - myPatterns.getIncludes().addAll(includes); + patterns.getIncludes().clear(); + patterns.getIncludes().addAll(includes); } @NotNull @Override public FilePatternSet getPatterns() { - return myPatterns; + return patterns; } public void setInheritedCompilerOutput(boolean inheritedCompilerOutput) { - myInheritedCompilerOutput = inheritedCompilerOutput; + this.inheritedCompilerOutput = inheritedCompilerOutput; } @NotNull @Override public List getFilters() { - return myFilters; + return filters; } public void setFilters(@NotNull List filters) { - myFilters = filters; + this.filters = filters; } } diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleConfiguration.java b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleConfiguration.java index 2694b1bff047..aeed28ad2770 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleConfiguration.java +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleConfiguration.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// 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 org.jetbrains.plugins.gradle.model; import org.jetbrains.annotations.NotNull; @@ -23,20 +9,29 @@ import org.jetbrains.annotations.Nullable; */ public class DefaultGradleConfiguration implements GradleConfiguration { private static final long serialVersionUID = 1L; - private final String myName; - private final String myDescription; - private final boolean myVisible; - private final boolean myScriptClasspathConfiguration; + + private final String name; + private final String description; + private final boolean visible; + private final boolean scriptClasspathConfiguration; public DefaultGradleConfiguration(String name, String description, boolean visible) { this(name, description, visible, false); } + @SuppressWarnings("unused") + private DefaultGradleConfiguration() { + name = ""; + description = ""; + visible = false; + scriptClasspathConfiguration = false; + } + public DefaultGradleConfiguration(@NotNull String name, @Nullable String description, boolean visible, boolean scriptClasspathConfiguration) { - myName = name; - myDescription = description; - myVisible = visible; - myScriptClasspathConfiguration = scriptClasspathConfiguration; + this.name = name; + this.description = description; + this.visible = visible; + this.scriptClasspathConfiguration = scriptClasspathConfiguration; } public DefaultGradleConfiguration(GradleConfiguration configuration) { @@ -47,22 +42,22 @@ public class DefaultGradleConfiguration implements GradleConfiguration { @NotNull @Override public String getName() { - return myName; + return name; } @Nullable @Override public String getDescription() { - return myDescription; + return description; } @Override public boolean isVisible() { - return myVisible; + return visible; } @Override public boolean isScriptClasspathConfiguration() { - return myScriptClasspathConfiguration; + return scriptClasspathConfiguration; } } diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleConvention.java b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleConvention.java index e5893bf6222c..ace59536a427 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleConvention.java +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleConvention.java @@ -14,6 +14,11 @@ public class DefaultGradleConvention extends DefaultGradleProperty implements Gr super(name, typeFqn, null); } + @SuppressWarnings("unused") + private DefaultGradleConvention() { + super(); + } + public DefaultGradleConvention(GradleConvention convention) { this(convention.getName(), convention.getTypeFqn()); } diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleExtension.java b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleExtension.java index 54155caf2ce6..c7b2bec17ac6 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleExtension.java +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleExtension.java @@ -14,6 +14,11 @@ public class DefaultGradleExtension extends DefaultGradleProperty implements Gra super(name, typeFqn, null); } + @SuppressWarnings("unused") + private DefaultGradleExtension() { + super(); + } + public DefaultGradleExtension(GradleExtension extension) { this(extension.getName(), extension.getTypeFqn()); } diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleProperty.java b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleProperty.java index 4de73ad72200..0cb80d29a18d 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleProperty.java +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/DefaultGradleProperty.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// 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 org.jetbrains.plugins.gradle.model; import org.jetbrains.annotations.NotNull; @@ -24,18 +10,24 @@ import java.io.Serializable; * @author Vladislav.Soroka */ public class DefaultGradleProperty implements GradleProperty { - private static final long serialVersionUID = 1L; @NotNull - private final String myName; + private final String name; @NotNull - private final String myRootTypeFqn; + private final String rootTypeFqn; @Nullable - private final Serializable myValue; + private final Serializable value; public DefaultGradleProperty(@NotNull String name, @Nullable String typeFqn, @Nullable Serializable value) { - myName = name; - myRootTypeFqn = typeFqn == null ? "Object" : typeFqn; - myValue = value; + this.name = name; + rootTypeFqn = typeFqn == null ? "Object" : typeFqn; + this.value = value; + } + + @SuppressWarnings("unused") + protected DefaultGradleProperty() { + this.name = ""; + rootTypeFqn = "Object"; + this.value = null; } public DefaultGradleProperty(GradleProperty property) { @@ -45,18 +37,18 @@ public class DefaultGradleProperty implements GradleProperty { @NotNull @Override public String getName() { - return myName; + return name; } @NotNull @Override public String getTypeFqn() { - return myRootTypeFqn; + return rootTypeFqn; } @Nullable @Override public Serializable getValue() { - return myValue; + return value; } } diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/FilePatternSetImpl.java b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/FilePatternSetImpl.java index 46c8f8f2c07c..0739a643ec89 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/FilePatternSetImpl.java +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/model/FilePatternSetImpl.java @@ -1,6 +1,7 @@ -// Copyright 2000-2018 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. +// 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 org.jetbrains.plugins.gradle.model; +import java.util.HashSet; import java.util.Set; /** @@ -15,6 +16,12 @@ public class FilePatternSetImpl implements FilePatternSet { this.excludes = excludes; } + @SuppressWarnings("unused") + private FilePatternSetImpl() { + includes = new HashSet(); + excludes = new HashSet(); + } + @Override public Set getIncludes() { return includes;