mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
unsafe polymorphic types
GitOrigin-RevId: 4ff1cb0f480e018760fec4d934b6252a34b59a01
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1f10a8e56f
commit
b6e3ff604a
+2
-2
@@ -124,7 +124,7 @@ public class DataNode<T> implements UserDataHolderEx {
|
||||
try {
|
||||
MultiLoaderWrapper classLoader = new MultiLoaderWrapper(getClass().getClassLoader(), classLoaders);
|
||||
//noinspection unchecked
|
||||
data = ObjectSerializer.getInstance().read((Class<T>)classLoader.findClass(dataClassName), rawData, SerializationKt.getExternalSystemBeanConstructed());
|
||||
data = ObjectSerializer.getInstance().read((Class<T>)classLoader.findClass(dataClassName), rawData, SerializationKt.createDataNodeReadConfiguration(classLoader));
|
||||
assert data != null;
|
||||
clearRawData();
|
||||
}
|
||||
@@ -232,7 +232,7 @@ public class DataNode<T> implements UserDataHolderEx {
|
||||
else {
|
||||
LOG.assertTrue(!(data instanceof Proxy));
|
||||
dataClassName = data.getClass().getName();
|
||||
rawData = ObjectSerializer.getInstance().writeAsBytes(data);
|
||||
rawData = ObjectSerializer.getInstance().writeAsBytes(data, SerializationKt.getDataNodeWriteConfiguration());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-14
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createDataNodeReadConfiguration(classLoader: ClassLoader): ReadConfiguration {
|
||||
return ReadConfiguration(allowAnySubTypes = true, classLoader = classLoader, beanConstructed = externalSystemBeanConstructed)
|
||||
}
|
||||
|
||||
val dataNodeWriteConfiguration = WriteConfiguration(allowAnySubTypes = true)
|
||||
+2
-2
@@ -54,7 +54,7 @@ import static com.intellij.openapi.externalSystem.model.ProjectKeys.PROJECT;
|
||||
public class ExternalProjectsDataStorage implements SettingsSavingComponentJavaAdapter, PersistentStateComponent<ExternalProjectsDataStorage.State> {
|
||||
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 {
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<RootBinding>(), BindingInitializationContext {
|
||||
companion object {
|
||||
private val classToNestedBindingFactory = THashMap<Class<*>, NestedBindingFactory>(32, ContainerUtil.identityStrategy())
|
||||
private val classToRootBindingFactory = THashMap<Class<*>, 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<Type, RootBinding>): 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<out Enum<*>>)
|
||||
}
|
||||
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<out Enum<*>>)
|
||||
}
|
||||
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)
|
||||
}
|
||||
@@ -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 <T : Any> readVersioned(objectClass: Class<T>, file: Path, expectedVersion: Int, beanConstructed: BeanConstructed? = null, originalType: Type? = null): T? {
|
||||
fun <T : Any> readVersioned(objectClass: Class<T>, 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 <T> read(objectClass: Class<T>, reader: ValueReader, beanConstructed: BeanConstructed? = null, originalType: Type? = null): T {
|
||||
fun <T> read(objectClass: Class<T>, 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 <T> doRead(objectClass: Class<T>, originalType: Type?, reader: ValueReader, beanConstructed: BeanConstructed?): T {
|
||||
private fun <T> doRead(objectClass: Class<T>, 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 <T> readList(itemClass: Class<T>, reader: ValueReader, beanConstructed: BeanConstructed?): List<T> {
|
||||
fun <T> readList(itemClass: Class<T>, reader: ValueReader, configuration: ReadConfiguration): List<T> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return read(List::class.java, reader, beanConstructed, ParameterizedTypeImpl(List::class.java, itemClass)) as List<T>
|
||||
return read(List::class.java, reader, configuration, ParameterizedTypeImpl(List::class.java, itemClass)) as List<T>
|
||||
}
|
||||
|
||||
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<RootBinding>,
|
||||
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() }
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ annotation class PropertyMapping(val value: Array<String>)
|
||||
|
||||
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 <T> read(objectClass: Class<T>, bytes: ByteArray, beanConstructed: BeanConstructed? = null): T {
|
||||
return serializer.read(objectClass, readerBuilder.build(bytes), beanConstructed)
|
||||
fun <T> read(objectClass: Class<T>, bytes: ByteArray, configuration: ReadConfiguration = defaultReadConfiguration): T {
|
||||
return serializer.read(objectClass, readerBuilder.build(bytes), configuration)
|
||||
}
|
||||
|
||||
fun <T> read(objectClass: Class<T>, inputStream: InputStream): T {
|
||||
return serializer.read(objectClass, readerBuilder.build(inputStream))
|
||||
fun <T> read(objectClass: Class<T>, inputStream: InputStream, configuration: ReadConfiguration = defaultReadConfiguration): T {
|
||||
return serializer.read(objectClass, readerBuilder.build(inputStream), configuration)
|
||||
}
|
||||
|
||||
fun <T> read(objectClass: Class<T>, reader: Reader): T {
|
||||
return serializer.read(objectClass, readerBuilder.build(reader))
|
||||
fun <T> read(objectClass: Class<T>, reader: Reader, configuration: ReadConfiguration = defaultReadConfiguration): T {
|
||||
return serializer.read(objectClass, readerBuilder.build(reader), configuration)
|
||||
}
|
||||
|
||||
fun <T> read(objectClass: Class<T>, text: String): T {
|
||||
return serializer.read(objectClass, readerBuilder.build(text))
|
||||
fun <T> read(objectClass: Class<T>, text: String, configuration: ReadConfiguration = defaultReadConfiguration): T {
|
||||
return serializer.read(objectClass, readerBuilder.build(text), configuration)
|
||||
}
|
||||
|
||||
fun <T> readList(itemClass: Class<T>, reader: Reader, beanConstructed: BeanConstructed? = null): List<T> {
|
||||
return serializer.readList(itemClass, readerBuilder.build(reader), beanConstructed)
|
||||
fun <T> readList(itemClass: Class<T>, reader: Reader, configuration: ReadConfiguration = defaultReadConfiguration): List<T> {
|
||||
return serializer.readList(itemClass, readerBuilder.build(reader), configuration)
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun <T> readList(itemClass: Class<T>, bytes: ByteArray, beanConstructed: BeanConstructed? = null): List<T> {
|
||||
return serializer.readList(itemClass, readerBuilder.build(bytes), beanConstructed)
|
||||
fun <T> readList(itemClass: Class<T>, bytes: ByteArray, configuration: ReadConfiguration = defaultReadConfiguration): List<T> {
|
||||
return serializer.readList(itemClass, readerBuilder.build(bytes), configuration)
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun <T> readList(itemClass: Class<T>, input: InputStream, beanConstructed: BeanConstructed? = null): List<T> {
|
||||
return serializer.readList(itemClass, readerBuilder.build(input), beanConstructed)
|
||||
fun <T> readList(itemClass: Class<T>, input: InputStream, configuration: ReadConfiguration = defaultReadConfiguration): List<T> {
|
||||
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<RootBinding>)
|
||||
|
||||
interface ReadContext {
|
||||
val reader: ValueReader
|
||||
val objectIdReader: ObjectIdReader
|
||||
val bindingProducer: BindingProducer<RootBinding>
|
||||
|
||||
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<Any> = ObjectIntHashMap(TObjectHashingStrategy.IDENTITY)
|
||||
private var counter = 0
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,11 @@ data class VersionedFile(val file: Path, val version: Int) {
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun <T> readList(itemClass: Class<T>): List<T>? {
|
||||
@JvmOverloads
|
||||
fun <T> readList(itemClass: Class<T>, beanConstructed: BeanConstructed? = null): List<T>? {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return ObjectSerializer.instance.serializer.readVersioned(ArrayList::class.java, file, version, originalType = ParameterizedTypeImpl(ArrayList::class.java, itemClass)) as List<T>?
|
||||
return ObjectSerializer.instance.serializer.readVersioned(ArrayList::class.java, file, version,
|
||||
originalType = ParameterizedTypeImpl(ArrayList::class.java, itemClass),
|
||||
configuration = ReadConfiguration(beanConstructed = beanConstructed)) as List<T>?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<RootBinding>)
|
||||
|
||||
interface ReadContext {
|
||||
val reader: ValueReader
|
||||
val objectIdReader: ObjectIdReader
|
||||
val bindingProducer: BindingProducer<RootBinding>
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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<Class<*>, RootBindingFactory>, classToNestedBindingFactory: MutableMap<Class<*>, 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
'@id':0,
|
||||
shape:'com.intellij.serialization.Circle'::{
|
||||
'@id':1,
|
||||
name:null
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
'@id':0,
|
||||
shape:[
|
||||
first,
|
||||
'com.intellij.serialization.Circle'::{
|
||||
'@id':1,
|
||||
name:null
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -78,7 +78,7 @@ class ListTest {
|
||||
assertThat(file.file.readChars().trim()).isEqualTo("""
|
||||
{
|
||||
version:42,
|
||||
formatVersion:0,
|
||||
formatVersion:1,
|
||||
data:[
|
||||
foo,
|
||||
bar
|
||||
|
||||
@@ -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 <T : Any> 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<String, Shape> = 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
|
||||
|
||||
@@ -34,7 +34,7 @@ internal fun <T : Any> 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 <T : Any> 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)
|
||||
|
||||
|
||||
+41
-55
@@ -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<File> mySrcDirs;
|
||||
private File myOutputDir;
|
||||
private final List<File> myGradleOutputDirs;
|
||||
private final FilePatternSet myPatterns;
|
||||
private Set<File> srcDirs;
|
||||
private File outputDir;
|
||||
private final List<File> gradleOutputDirs;
|
||||
private final FilePatternSet patterns;
|
||||
@NotNull
|
||||
private List<ExternalFilter> myFilters;
|
||||
private List<ExternalFilter> filters;
|
||||
|
||||
private boolean myInheritedCompilerOutput;
|
||||
private boolean inheritedCompilerOutput;
|
||||
|
||||
public DefaultExternalSourceDirectorySet() {
|
||||
mySrcDirs = new HashSet<File>();
|
||||
myFilters = new ArrayList<ExternalFilter>();
|
||||
myGradleOutputDirs = new ArrayList<File>();
|
||||
myPatterns = new FilePatternSetImpl(new LinkedHashSet<String>(), new LinkedHashSet<String>());
|
||||
srcDirs = new HashSet<File>();
|
||||
filters = new ArrayList<ExternalFilter>();
|
||||
gradleOutputDirs = new ArrayList<File>();
|
||||
patterns = new FilePatternSetImpl(new LinkedHashSet<String>(), new LinkedHashSet<String>());
|
||||
}
|
||||
|
||||
public DefaultExternalSourceDirectorySet(ExternalSourceDirectorySet sourceDirectorySet) {
|
||||
this();
|
||||
myName = sourceDirectorySet.getName();
|
||||
mySrcDirs = new HashSet<File>(sourceDirectorySet.getSrcDirs());
|
||||
myOutputDir = sourceDirectorySet.getOutputDir();
|
||||
myGradleOutputDirs.addAll(sourceDirectorySet.getGradleOutputDirs());
|
||||
name = sourceDirectorySet.getName();
|
||||
srcDirs = new HashSet<File>(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<File> getSrcDirs() {
|
||||
return mySrcDirs;
|
||||
return srcDirs;
|
||||
}
|
||||
|
||||
public void setSrcDirs(@NotNull Set<File> 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<File> 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<String> getExcludes() {
|
||||
return myPatterns.getExcludes();
|
||||
return patterns.getExcludes();
|
||||
}
|
||||
|
||||
public void setExcludes(Set<String> excludes) {
|
||||
myPatterns.getExcludes().clear();
|
||||
myPatterns.getExcludes().addAll(excludes);
|
||||
patterns.getExcludes().clear();
|
||||
patterns.getExcludes().addAll(excludes);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<String> getIncludes() {
|
||||
return myPatterns.getIncludes();
|
||||
return patterns.getIncludes();
|
||||
}
|
||||
|
||||
public void setIncludes(Set<String> 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<ExternalFilter> getFilters() {
|
||||
return myFilters;
|
||||
return filters;
|
||||
}
|
||||
|
||||
public void setFilters(@NotNull List<ExternalFilter> filters) {
|
||||
myFilters = filters;
|
||||
this.filters = filters;
|
||||
}
|
||||
}
|
||||
|
||||
+22
-27
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -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());
|
||||
}
|
||||
|
||||
+5
@@ -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());
|
||||
}
|
||||
|
||||
+17
-25
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-1
@@ -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<String>();
|
||||
excludes = new HashSet<String>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getIncludes() {
|
||||
return includes;
|
||||
|
||||
Reference in New Issue
Block a user