Update generators

This commit is contained in:
Andrea Cavalli 2023-01-21 01:15:05 +01:00
parent 2bf847224a
commit 784c86456c
16 changed files with 1288 additions and 46 deletions

View File

@ -41,4 +41,8 @@ public sealed interface ComputedType permits VersionedComputedType, ComputedType
*/ */
Stream<ComputedType> getDependents(); Stream<ComputedType> getDependents();
default boolean isPrimitive() {
return false;
}
} }

View File

@ -1,10 +1,7 @@
package it.cavallium.data.generator.plugin; package it.cavallium.data.generator.plugin;
import static it.cavallium.data.generator.plugin.DataModel.joinPackage;
import com.squareup.javapoet.ClassName; import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeName;
import it.cavallium.data.generator.nativedata.Serializers;
import it.cavallium.data.generator.plugin.ComputedType.VersionedComputedType; import it.cavallium.data.generator.plugin.ComputedType.VersionedComputedType;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Objects; import java.util.Objects;
@ -101,7 +98,7 @@ public final class ComputedTypeBase implements VersionedComputedType {
} }
@Override @Override
public TypeName getJTypeName(String basePackageName) { public ClassName getJTypeName(String basePackageName) {
return ClassName.get(getVersion().getDataPackage(basePackageName), type.type()); return ClassName.get(getVersion().getDataPackage(basePackageName), type.type());
} }

View File

@ -1,7 +1,5 @@
package it.cavallium.data.generator.plugin; package it.cavallium.data.generator.plugin;
import static it.cavallium.data.generator.plugin.DataModel.joinPackage;
import com.squareup.javapoet.ClassName; import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeName;
import it.cavallium.data.generator.nativedata.Int52Serializer; import it.cavallium.data.generator.nativedata.Int52Serializer;
@ -9,16 +7,21 @@ import it.cavallium.data.generator.nativedata.Serializers;
import it.cavallium.data.generator.nativedata.StringSerializer; import it.cavallium.data.generator.nativedata.StringSerializer;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
public final class ComputedTypeNative implements ComputedType { public final class ComputedTypeNative implements ComputedType {
private static final Set<String> PRIMITIVE_TYPES = Set.of("boolean", "short", "char", "int", "long", "float", "double", "byte");
private final String type; private final String type;
private final ComputedTypeSupplier computedTypeSupplier; private final ComputedTypeSupplier computedTypeSupplier;
private boolean primitive;
public ComputedTypeNative(String type, ComputedTypeSupplier computedTypeSupplier) { public ComputedTypeNative(String type, ComputedTypeSupplier computedTypeSupplier) {
this.type = type; this.type = type;
this.computedTypeSupplier = computedTypeSupplier; this.computedTypeSupplier = computedTypeSupplier;
this.primitive = PRIMITIVE_TYPES.contains(type);
} }
public String getName() { public String getName() {
@ -109,4 +112,8 @@ public final class ComputedTypeNative implements ComputedType {
public String toString() { public String toString() {
return type + " (native)"; return type + " (native)";
} }
public boolean isPrimitive() {
return primitive;
}
} }

View File

@ -1,7 +1,5 @@
package it.cavallium.data.generator.plugin; package it.cavallium.data.generator.plugin;
import static it.cavallium.data.generator.plugin.DataModel.joinPackage;
import com.squareup.javapoet.ClassName; import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeName;
import it.cavallium.data.generator.plugin.ComputedType.VersionedComputedType; import it.cavallium.data.generator.plugin.ComputedType.VersionedComputedType;
@ -94,7 +92,7 @@ public final class ComputedTypeSuper implements VersionedComputedType {
} }
@Override @Override
public TypeName getJTypeName(String basePackageName) { public ClassName getJTypeName(String basePackageName) {
return ClassName.get(getVersion().getDataPackage(basePackageName), type.type()); return ClassName.get(getVersion().getDataPackage(basePackageName), type.type());
} }

View File

@ -672,6 +672,16 @@ public class DataModel {
.filter(t -> t instanceof ComputedTypeBase).map(t -> (ComputedTypeBase) t); .filter(t -> t instanceof ComputedTypeBase).map(t -> (ComputedTypeBase) t);
} }
public Stream<ComputedTypeArray> getArrayTypesComputed(ComputedVersion version) {
return this.computedTypes.get(version.getVersion()).values().stream()
.filter(t -> t instanceof ComputedTypeArray).map(t -> (ComputedTypeArray) t);
}
public Stream<ComputedTypeNullable> getNullableTypesComputed(ComputedVersion version) {
return this.computedTypes.get(version.getVersion()).values().stream()
.filter(t -> t instanceof ComputedTypeNullable).map(t -> (ComputedTypeNullable) t);
}
public Optional<ComputedVersion> getNextVersion(ComputedVersion versionConfiguration) { public Optional<ComputedVersion> getNextVersion(ComputedVersion versionConfiguration) {
var nextVersion = versions.get(versionConfiguration.getVersion() + 1); var nextVersion = versions.get(versionConfiguration.getVersion() + 1);
return Optional.ofNullable(nextVersion); return Optional.ofNullable(nextVersion);
@ -743,7 +753,7 @@ public class DataModel {
if (prevVersion != null) { if (prevVersion != null) {
return versions.get(prevVersion.getVersion().getVersion() + 1); return versions.get(prevVersion.getVersion().getVersion() + 1);
} else { } else {
return type.getVersion(); return versions.get(0);
} }
} }
@ -800,8 +810,29 @@ public class DataModel {
} }
} }
public Stream<ComputedTypeSuper> getSuperTypesOf(VersionedComputedType baseType) { /**
return getSuperTypesComputed(baseType.getVersion()).filter(type -> type.subTypes().contains(baseType)); * @param includeMulti Includes all used super type versions
*/
public Stream<ComputedTypeSuper> getSuperTypesOf(VersionedComputedType baseType, boolean includeMulti) {
ComputedVersion lowestBaseVersion = getTypeFirstSameVersion(baseType);
if (lowestBaseVersion == null) {
return Stream.of();
}
return getSuperTypesComputed(baseType.getVersion())
.filter(type -> type.subTypes().contains(baseType))
.mapMulti((superType, cons) -> {
if (includeMulti) {
while (superType != null) {
ComputedVersion lowestSuperVersion = Objects.requireNonNull(getTypeFirstSameVersion(superType));
if (lowestSuperVersion.compareTo(lowestBaseVersion) >= 0) {
cons.accept(superType);
}
superType = getPrevVersion(superType);
}
} else {
cons.accept(superType);
}
});
} }
public Stream<ComputedTypeSuper> getExtendsInterfaces(ComputedTypeSuper superType) { public Stream<ComputedTypeSuper> getExtendsInterfaces(ComputedTypeSuper superType) {

View File

@ -24,6 +24,10 @@ import it.cavallium.data.generator.plugin.classgen.GenINullableSuperType;
import it.cavallium.data.generator.plugin.classgen.GenIType; import it.cavallium.data.generator.plugin.classgen.GenIType;
import it.cavallium.data.generator.plugin.classgen.GenIVersion; import it.cavallium.data.generator.plugin.classgen.GenIVersion;
import it.cavallium.data.generator.plugin.classgen.GenNullableX; import it.cavallium.data.generator.plugin.classgen.GenNullableX;
import it.cavallium.data.generator.plugin.classgen.GenSerializerArrayX;
import it.cavallium.data.generator.plugin.classgen.GenSerializerBaseX;
import it.cavallium.data.generator.plugin.classgen.GenSerializerNullableX;
import it.cavallium.data.generator.plugin.classgen.GenSerializerSuperX;
import it.cavallium.data.generator.plugin.classgen.GenSuperType; import it.cavallium.data.generator.plugin.classgen.GenSuperType;
import it.cavallium.data.generator.plugin.classgen.GenVersion; import it.cavallium.data.generator.plugin.classgen.GenVersion;
import it.cavallium.data.generator.plugin.classgen.GenVersions; import it.cavallium.data.generator.plugin.classgen.GenVersions;
@ -175,6 +179,14 @@ public class SourcesGenerator {
new GenDataSuperX(genParams).run(); new GenDataSuperX(genParams).run();
new GenSerializerSuperX(genParams).run();
new GenSerializerBaseX(genParams).run();
new GenSerializerArrayX(genParams).run();
new GenSerializerNullableX(genParams).run();
// Update the hash at the end // Update the hash at the end
Files.writeString(hashPath, basePackageName + '\n' + useRecordBuilders + '\n' + curHash + '\n', Files.writeString(hashPath, basePackageName + '\n' + useRecordBuilders + '\n' + curHash + '\n',
StandardCharsets.UTF_8, TRUNCATE_EXISTING, WRITE, CREATE); StandardCharsets.UTF_8, TRUNCATE_EXISTING, WRITE, CREATE);

View File

@ -156,7 +156,7 @@ public class GenCurrentVersion extends ClassGenerator {
.addStatement( .addStatement(
"data = " + versionConfiguration.getPackage(basePackageName) "data = " + versionConfiguration.getPackage(basePackageName)
+ ".Version.upgradeToNextVersion(($T) data)", + ".Version.upgradeToNextVersion(($T) data)",
ClassName.get(versionConfiguration.getDataPackage(basePackageName), "IBaseType") ClassName.get(versionConfiguration.getPackage(basePackageName), "IBaseType")
); );
} }
} }
@ -178,7 +178,7 @@ public class GenCurrentVersion extends ClassGenerator {
var baseTypeClassName = ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType"); var baseTypeClassName = ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType");
methodBuilder.addParameter(baseTypeClassName, "type"); methodBuilder.addParameter(baseTypeClassName, "type");
var iBaseTypeClassName = ClassName.get(version.getDataPackage(basePackageName), "IBaseType"); var iBaseTypeClassName = ClassName.get(version.getPackage(basePackageName), "IBaseType");
methodBuilder.returns(ParameterizedTypeName.get(ClassName.get(Class.class), WildcardTypeName.subtypeOf(iBaseTypeClassName))); methodBuilder.returns(ParameterizedTypeName.get(ClassName.get(Class.class), WildcardTypeName.subtypeOf(iBaseTypeClassName)));
methodBuilder.beginControlFlow("return switch (type)"); methodBuilder.beginControlFlow("return switch (type)");

View File

@ -38,12 +38,17 @@ public class GenDataBaseX extends ClassGenerator {
classBuilder.addModifiers(Modifier.PUBLIC); classBuilder.addModifiers(Modifier.PUBLIC);
var baseTypeClass = ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType"); var baseTypeClass = ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType");
var iTypeClass = ClassName.get(version.getPackage(basePackageName), "IBaseType");
dataModel.getTypeSameVersions(base).forEach(v -> {
var iTypeClass = ClassName.get(v.getPackage(basePackageName), "IBaseType");
classBuilder.addSuperinterface(iTypeClass); classBuilder.addSuperinterface(iTypeClass);
});
dataModel.getSuperTypesOf(base).forEach(superType -> { dataModel.getSuperTypesOf(base, true).forEach(superType -> {
classBuilder.addSuperinterface(superType.getJTypeName(basePackageName)); classBuilder.addSuperinterface(superType.getJTypeName(basePackageName));
});
dataModel.getSuperTypesOf(base, false).forEach(superType -> {
classBuilder.addMethod(MethodSpec classBuilder.addMethod(MethodSpec
.methodBuilder("getMetaId$" + superType.getName()) .methodBuilder("getMetaId$" + superType.getName())
.addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addModifiers(Modifier.PUBLIC, Modifier.FINAL)

View File

@ -36,9 +36,10 @@ public class GenDataSuperX extends ClassGenerator {
classBuilder.addModifiers(Modifier.PUBLIC); classBuilder.addModifiers(Modifier.PUBLIC);
var baseTypeClass = ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType"); dataModel.getTypeSameVersions(typeSuper).forEach(v -> {
var iBaseTypeClass = ClassName.get(version.getPackage(basePackageName), "IBaseType"); var iTypeClass = ClassName.get(v.getPackage(basePackageName), "IBaseType");
classBuilder.addSuperinterface(iBaseTypeClass); classBuilder.addSuperinterface(iTypeClass);
});
dataModel.getExtendsInterfaces(typeSuper).forEach(superType -> { dataModel.getExtendsInterfaces(typeSuper).forEach(superType -> {
classBuilder.addSuperinterface(superType.getJTypeName(basePackageName)); classBuilder.addSuperinterface(superType.getJTypeName(basePackageName));
@ -70,7 +71,7 @@ public class GenDataSuperX extends ClassGenerator {
classBuilder.addMethod(setter.build()); classBuilder.addMethod(setter.build());
}); });
dataModel.getSuperTypesOf(typeSuper).forEach(superType -> { dataModel.getSuperTypesOf(typeSuper, true).forEach(superType -> {
classBuilder.addSuperinterface(superType.getJTypeName(basePackageName)); classBuilder.addSuperinterface(superType.getJTypeName(basePackageName));
}); });

View File

@ -7,10 +7,8 @@ import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName; import com.squareup.javapoet.TypeVariableName;
import com.squareup.javapoet.WildcardTypeName;
import it.cavallium.data.generator.DataSerializer; import it.cavallium.data.generator.DataSerializer;
import it.cavallium.data.generator.plugin.ClassGenerator; import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ClassGenerator.ClassGeneratorParams;
import java.io.IOException; import java.io.IOException;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
@ -27,21 +25,6 @@ public class GenIVersion extends ClassGenerator {
iVersionClass.addModifiers(Modifier.PUBLIC); iVersionClass.addModifiers(Modifier.PUBLIC);
iVersionClass.addTypeVariable(TypeVariableName.get("B")); iVersionClass.addTypeVariable(TypeVariableName.get("B"));
// Add getClass method
{
var getClassMethodBuilder = MethodSpec
.methodBuilder("getClass")
.addModifiers(Modifier.PUBLIC)
.addModifiers(Modifier.ABSTRACT)
.returns(ParameterizedTypeName.get(ClassName.get(Class.class),
WildcardTypeName.subtypeOf(TypeVariableName.get("B"))
))
.addParameter(ParameterSpec
.builder(ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType"), "type")
.build());
iVersionClass.addMethod(getClassMethodBuilder.build());
}
// Add getSerializer method // Add getSerializer method
{ {
var getSerializerMethodBuilder = MethodSpec var getSerializerMethodBuilder = MethodSpec

View File

@ -0,0 +1,119 @@
package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.ArrayTypeName;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeSpec.Builder;
import it.cavallium.data.generator.DataSerializer;
import it.cavallium.data.generator.nativedata.ImmutableWrappedArrayList;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedTypeArrayVersioned;
import it.cavallium.data.generator.plugin.ComputedVersion;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.NotSerializableException;
import java.util.Objects;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
import org.jetbrains.annotations.NotNull;
public class GenSerializerArrayX extends ClassGenerator {
public GenSerializerArrayX(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
return dataModel.getVersionsSet().parallelStream().flatMap(this::generateVersionClasses);
}
private Stream<GeneratedClass> generateVersionClasses(ComputedVersion version) {
return dataModel
.getArrayTypesComputed(version)
.filter(type -> type instanceof ComputedTypeArrayVersioned versioned && versioned.getVersion().equals(version))
.map(type -> generateTypeVersioned(version, (ComputedTypeArrayVersioned) type));
}
private GeneratedClass generateTypeVersioned(ComputedVersion version, ComputedTypeArrayVersioned typeArray) {
ClassName serializerClassName = typeArray.getJSerializerName(basePackageName);
var typeArrayClassName = typeArray.getJTypeName(basePackageName);
var classBuilder = TypeSpec.classBuilder(serializerClassName.simpleName());
classBuilder.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
classBuilder.addSuperinterface(ParameterizedTypeName.get(ClassName.get(DataSerializer.class), typeArrayClassName));
generateSerialize(version, typeArray, classBuilder);
generateDeserialize(version, typeArray, classBuilder);
return new GeneratedClass(serializerClassName.packageName(), classBuilder);
}
private void generateSerialize(ComputedVersion version, ComputedTypeArrayVersioned typeArray, Builder classBuilder) {
var method = MethodSpec.methodBuilder("serialize");
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
method.addException(IOException.class);
method.addParameter(ParameterSpec.builder(DataOutput.class, "out").build());
method.addParameter(ParameterSpec
.builder(typeArray.getJTypeName(basePackageName), "data")
.addAnnotation(NotNull.class)
.build());
if (version.isCurrent()) {
method.addStatement("$T.requireNonNull(data)", Objects.class);
method.addCode("\n");
method.addStatement("final int sz = data.size()");
method.addStatement("out.writeInt(sz)");
method.addCode("\n");
method.beginControlFlow("for (int i = 0; i < sz; ++i)");
var baseSerializerInstance = typeArray.getBase().getJSerializerInstance(basePackageName);
method.addStatement("$T.$N.serialize(out, ($T) data.get(i))",
baseSerializerInstance.className(),
baseSerializerInstance.fieldName(),
typeArray.getBase().getJTypeName(basePackageName)
);
method.endControlFlow();
} else {
method.addStatement("throw new $T()", NotSerializableException.class);
}
classBuilder.addMethod(method.build());
}
private void generateDeserialize(ComputedVersion version, ComputedTypeArrayVersioned typeArray, Builder classBuilder) {
var method = MethodSpec.methodBuilder("deserialize");
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
method.addException(IOException.class);
var typeArrayClassName = typeArray.getJTypeName(basePackageName);
method.returns(typeArrayClassName);
method.addAnnotation(NotNull.class);
method.addParameter(ParameterSpec.builder(DataInput.class, "in").build());
method.addStatement("int sz = in.readInt()");
var arrayTypeName = ArrayTypeName.of(typeArray.getBase().getJTypeName(basePackageName));
method.addStatement("$T a = new $T[sz]", arrayTypeName, arrayTypeName.componentType);
method.addCode("\n");
method.beginControlFlow("for (int i = 0; i < sz; ++i)");
var baseSerializerInstance = typeArray.getBase().getJSerializerInstance(basePackageName);
method.addStatement("a[i] = $T.$N.deserialize(in)", baseSerializerInstance.className(), baseSerializerInstance.fieldName());
method.endControlFlow();
method.addCode("\n");
method.addStatement("return new $T(a)", ParameterizedTypeName.get(ClassName.get(ImmutableWrappedArrayList.class),
typeArray.getBase().getJTypeName(basePackageName)));
classBuilder.addMethod(method.build());
}
}

View File

@ -0,0 +1,124 @@
package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeSpec.Builder;
import it.cavallium.data.generator.DataSerializer;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedTypeBase;
import it.cavallium.data.generator.plugin.ComputedVersion;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.NotSerializableException;
import java.util.Objects;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
public class GenSerializerBaseX extends ClassGenerator {
public GenSerializerBaseX(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
return dataModel.getVersionsSet().parallelStream().flatMap(this::generateVersionClasses);
}
private Stream<GeneratedClass> generateVersionClasses(ComputedVersion version) {
return dataModel
.getBaseTypesComputed(version)
.filter(type -> type.getVersion().equals(version))
.map(type -> generateTypeVersioned(version, type));
}
private GeneratedClass generateTypeVersioned(ComputedVersion version, ComputedTypeBase typeBase) {
ClassName serializerClassName = typeBase.getJSerializerName(basePackageName);
ClassName typeBaseClassName = typeBase.getJTypeName(basePackageName);
var classBuilder = TypeSpec.classBuilder(serializerClassName.simpleName());
classBuilder.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
classBuilder.addSuperinterface(ParameterizedTypeName.get(ClassName.get(DataSerializer.class), typeBaseClassName));
generateSerialize(version, typeBase, classBuilder);
generateDeserialize(version, typeBase, classBuilder);
return new GeneratedClass(serializerClassName.packageName(), classBuilder);
}
private void generateSerialize(ComputedVersion version, ComputedTypeBase typeBase, Builder classBuilder) {
var method = MethodSpec.methodBuilder("serialize");
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
method.addException(IOException.class);
method.addParameter(ParameterSpec.builder(DataOutput.class, "out").build());
method.addParameter(ParameterSpec
.builder(typeBase.getJTypeName(basePackageName), "data")
.addAnnotation(NotNull.class)
.build());
if (version.isCurrent()) {
method.addStatement("$T.requireNonNull(data)", Objects.class);
method.addCode("\n");
typeBase.getData().forEach((fieldName, fieldType) -> {
if (fieldType.isPrimitive()) {
method.addStatement("out.write$N(data.$N())", StringUtils.capitalize(fieldType.getName()), fieldName);
} else {
var serializerInstance = fieldType.getJSerializerInstance(basePackageName);
method.addStatement("$T.$N.serialize(out, data.$N())",
serializerInstance.className(),
serializerInstance.fieldName(),
fieldName
);
}
});
} else {
method.addStatement("throw new $T()", NotSerializableException.class);
}
classBuilder.addMethod(method.build());
}
private void generateDeserialize(ComputedVersion version, ComputedTypeBase typeBase, Builder classBuilder) {
var method = MethodSpec.methodBuilder("deserialize");
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
method.addException(IOException.class);
ClassName typeBaseClassName = typeBase.getJTypeName(basePackageName);
method.returns(typeBaseClassName);
method.addAnnotation(NotNull.class);
method.addParameter(ParameterSpec.builder(DataInput.class, "in").build());
method.addCode("return new $T(\n$>", typeBaseClassName);
typeBase.getData().entrySet().stream().flatMap(entry -> {
var fieldType = entry.getValue();
if (fieldType.isPrimitive()) {
return Stream.of(CodeBlock.of(",\n"), CodeBlock.of("in.read$N()", StringUtils.capitalize(fieldType.getName())));
} else {
var serializerInstance = fieldType.getJSerializerInstance(basePackageName);
return Stream.of(CodeBlock.of(",\n"), CodeBlock.of("$T.$N.deserialize(in)",
serializerInstance.className(),
serializerInstance.fieldName()
));
}
}).skip(1).forEach(method::addCode);
method.addCode("$<\n");
method.addStatement(")");
classBuilder.addMethod(method.build());
}
}

View File

@ -0,0 +1,118 @@
package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeSpec.Builder;
import it.cavallium.data.generator.DataSerializer;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedTypeNullableVersioned;
import it.cavallium.data.generator.plugin.ComputedVersion;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.NotSerializableException;
import java.util.Objects;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
import org.jetbrains.annotations.NotNull;
public class GenSerializerNullableX extends ClassGenerator {
public GenSerializerNullableX(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
return dataModel.getVersionsSet().parallelStream().flatMap(this::generateVersionClasses);
}
private Stream<GeneratedClass> generateVersionClasses(ComputedVersion version) {
return dataModel
.getNullableTypesComputed(version)
.filter(type -> type instanceof ComputedTypeNullableVersioned versioned && versioned.getVersion().equals(version))
.map(type -> generateTypeVersioned(version, (ComputedTypeNullableVersioned) type));
}
private GeneratedClass generateTypeVersioned(ComputedVersion version, ComputedTypeNullableVersioned typeNullable) {
ClassName serializerClassName = typeNullable.getJSerializerName(basePackageName);
var typeNullableClassName = typeNullable.getJTypeName(basePackageName);
var classBuilder = TypeSpec.classBuilder(serializerClassName.simpleName());
classBuilder.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
classBuilder.addSuperinterface(ParameterizedTypeName.get(ClassName.get(DataSerializer.class), typeNullableClassName));
generateSerialize(version, typeNullable, classBuilder);
generateDeserialize(version, typeNullable, classBuilder);
return new GeneratedClass(serializerClassName.packageName(), classBuilder);
}
private void generateSerialize(ComputedVersion version, ComputedTypeNullableVersioned typeNullable, Builder classBuilder) {
var method = MethodSpec.methodBuilder("serialize");
var base = typeNullable.getBase();
var baseTypeName = base.getJTypeName(basePackageName);
var baseSerializerInstance = base.getJSerializerInstance(basePackageName);
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
method.addException(IOException.class);
method.addParameter(ParameterSpec.builder(DataOutput.class, "out").build());
method.addParameter(ParameterSpec
.builder(typeNullable.getJTypeName(basePackageName), "data")
.addAnnotation(NotNull.class)
.build());
if (version.isCurrent()) {
method.addStatement("$T.requireNonNull(data)", Objects.class);
method.addCode("\n");
method.addStatement("boolean notEmpty = !data.isEmpty()");
method.addStatement("out.writeBoolean(notEmpty)");
method.beginControlFlow("if (notEmpty)");
method.addStatement("$T.$N.serialize(out, ($T) data.getNullable())",
baseSerializerInstance.className(),
baseSerializerInstance.fieldName(),
baseTypeName
);
method.endControlFlow();
} else {
method.addStatement("throw new $T()", NotSerializableException.class);
}
classBuilder.addMethod(method.build());
}
private void generateDeserialize(ComputedVersion version, ComputedTypeNullableVersioned typeNullable, Builder classBuilder) {
var method = MethodSpec.methodBuilder("deserialize");
var base = typeNullable.getBase();
var baseTypeName = base.getJTypeName(basePackageName);
var baseSerializerInstance = base.getJSerializerInstance(basePackageName);
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
method.addException(IOException.class);
var typeNullableClassName = typeNullable.getJTypeName(basePackageName);
method.returns(typeNullableClassName);
method.addAnnotation(NotNull.class);
method.addParameter(ParameterSpec.builder(DataInput.class, "in").build());
method.addStatement("return in.readBoolean() ? new $T(($T) $T.$N.deserialize(in)) : $T.empty()",
typeNullableClassName,
baseTypeName,
baseSerializerInstance.className(),
baseSerializerInstance.fieldName(),
typeNullableClassName
);
classBuilder.addMethod(method.build());
}
}

View File

@ -0,0 +1,153 @@
package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeSpec.Builder;
import it.cavallium.data.generator.DataSerializer;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedType;
import it.cavallium.data.generator.plugin.ComputedTypeSuper;
import it.cavallium.data.generator.plugin.ComputedVersion;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.NotSerializableException;
import java.util.Objects;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
import org.jetbrains.annotations.NotNull;
public class GenSerializerSuperX extends ClassGenerator {
public GenSerializerSuperX(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
return dataModel.getVersionsSet().parallelStream().flatMap(this::generateVersionClasses);
}
private Stream<GeneratedClass> generateVersionClasses(ComputedVersion version) {
return dataModel
.getSuperTypesComputed(version)
.filter(type -> type.getVersion().equals(version))
.map(type -> generateTypeVersioned(version, type));
}
private GeneratedClass generateTypeVersioned(ComputedVersion version, ComputedTypeSuper typeSuper) {
ClassName serializerClassName = typeSuper.getJSerializerName(basePackageName);
ClassName typeSuperClassName = typeSuper.getJTypeName(basePackageName);
var classBuilder = TypeSpec.classBuilder(serializerClassName.simpleName());
classBuilder.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
classBuilder.addSuperinterface(ParameterizedTypeName.get(ClassName.get(DataSerializer.class), typeSuperClassName));
generateCheckIdValidity(version, typeSuper, classBuilder);
generateSerialize(version, typeSuper, classBuilder);
generateDeserialize(version, typeSuper, classBuilder);
return new GeneratedClass(serializerClassName.packageName(), classBuilder);
}
private void generateCheckIdValidity(ComputedVersion version, ComputedTypeSuper typeSuper, Builder classBuilder) {
int max = typeSuper.subTypes().size();
var method = MethodSpec.methodBuilder("checkIdValidity");
method.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL);
method.addException(IOException.class);
method.addParameter(ParameterSpec.builder(int.class, "id").build());
method.beginControlFlow("if (id < 0 || id >= $L)", max);
method.addStatement("throw new $T(new $T(id))", IOException.class, IndexOutOfBoundsException.class);
method.endControlFlow();
classBuilder.addMethod(method.build());
}
private void generateSerialize(ComputedVersion version, ComputedTypeSuper typeSuper, Builder classBuilder) {
var method = MethodSpec.methodBuilder("serialize");
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
method.addException(IOException.class);
method.addParameter(ParameterSpec.builder(DataOutput.class, "out").build());
method.addParameter(ParameterSpec
.builder(typeSuper.getJTypeName(basePackageName), "data")
.addAnnotation(NotNull.class)
.build());
if (version.isCurrent()) {
method.addStatement("$T.requireNonNull(data)", Objects.class);
method.addStatement("int id = data.getMetaId$$$N()", typeSuper.getName());
method.addStatement("out.writeByte(id)");
method.beginControlFlow("switch (id)");
var subTypes = typeSuper.subTypes().toArray(ComputedType[]::new);
int max = subTypes.length;
for (int i = 0; i < max; i++) {
var subType = subTypes[i];
var subSerializerInstance = subType.getJSerializerInstance(basePackageName);
method.addStatement("case $L -> $T.$N.serialize(out, ($T) data)",
i,
subSerializerInstance.className(),
subSerializerInstance.fieldName(),
subType.getJTypeName(basePackageName)
);
}
method.beginControlFlow("default ->");
method.addStatement("checkIdValidity(id)");
method.addComment("Not reachable:");
method.addStatement("throw new $T()", IllegalStateException.class);
method.endControlFlow();
method.endControlFlow();
} else {
method.addStatement("throw new $T()", NotSerializableException.class);
}
classBuilder.addMethod(method.build());
}
private void generateDeserialize(ComputedVersion version, ComputedTypeSuper typeSuper, Builder classBuilder) {
var method = MethodSpec.methodBuilder("deserialize");
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
method.addException(IOException.class);
ClassName typeSuperClassName = typeSuper.getJTypeName(basePackageName);
method.returns(typeSuperClassName);
method.addAnnotation(NotNull.class);
method.addParameter(ParameterSpec.builder(DataInput.class, "in").build());
method.addStatement("int id = in.readUnsignedByte()");
method.beginControlFlow("return switch (id)");
var subTypes = typeSuper.subTypes().toArray(ComputedType[]::new);
int max = subTypes.length;
for (int i = 0; i < max; i++) {
var subType = subTypes[i];
var subSerializerInstance = subType.getJSerializerInstance(basePackageName);
method.addStatement("case $L -> ($T) $T.$N.deserialize(in)",
i,
subType.getJTypeName(basePackageName),
subSerializerInstance.className(),
subSerializerInstance.fieldName()
);
}
method.beginControlFlow("default ->");
method.addStatement("checkIdValidity(id)");
method.addComment("Not reachable:");
method.addStatement("throw new $T()", IllegalStateException.class);
method.endControlFlow();
method.addCode("$<};");
classBuilder.addMethod(method.build());
}
}

View File

@ -10,11 +10,13 @@ import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeSpec.Builder; import com.squareup.javapoet.TypeSpec.Builder;
import com.squareup.javapoet.TypeVariableName; import com.squareup.javapoet.TypeVariableName;
import it.cavallium.data.generator.DataSerializer; import it.cavallium.data.generator.DataSerializer;
import it.cavallium.data.generator.DataUpgrader;
import it.cavallium.data.generator.plugin.ClassGenerator; import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedType.VersionedComputedType; import it.cavallium.data.generator.plugin.ComputedType.VersionedComputedType;
import it.cavallium.data.generator.plugin.ComputedTypeCustom; import it.cavallium.data.generator.plugin.ComputedTypeCustom;
import it.cavallium.data.generator.plugin.ComputedVersion; import it.cavallium.data.generator.plugin.ComputedVersion;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
@ -37,7 +39,7 @@ public class GenVersion extends ClassGenerator {
classBuilder.addModifiers(Modifier.PUBLIC, Modifier.FINAL); classBuilder.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
var iVersionClassName = ClassName.get(dataModel.getRootPackage(basePackageName), "IVersion"); var iVersionClassName = ClassName.get(dataModel.getRootPackage(basePackageName), "IVersion");
var iBaseTypeClassName = ClassName.get(version.getDataPackage(basePackageName), "IBaseType"); var iBaseTypeClassName = ClassName.get(version.getPackage(basePackageName), "IBaseType");
classBuilder.addSuperinterface(ParameterizedTypeName.get(iVersionClassName, iBaseTypeClassName)); classBuilder.addSuperinterface(ParameterizedTypeName.get(iVersionClassName, iBaseTypeClassName));
generateVersionField(version, classBuilder); generateVersionField(version, classBuilder);
@ -101,10 +103,10 @@ public class GenVersion extends ClassGenerator {
methodBuilder.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL); methodBuilder.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL);
methodBuilder.addException(ClassName.get(IOException.class)); methodBuilder.addException(ClassName.get(IOException.class));
var nextIBaseType = ClassName.get(nextVersion.getDataPackage(basePackageName), "IBaseType"); var nextIBaseType = ClassName.get(nextVersion.getPackage(basePackageName), "IBaseType");
methodBuilder.returns(nextIBaseType); methodBuilder.returns(nextIBaseType);
var iBaseTypeClassName = ClassName.get(version.getDataPackage(basePackageName), "IBaseType"); var iBaseTypeClassName = ClassName.get(version.getPackage(basePackageName), "IBaseType");
methodBuilder.addParameter(iBaseTypeClassName, "oldData"); methodBuilder.addParameter(iBaseTypeClassName, "oldData");
methodBuilder.beginControlFlow( "return switch (oldData.getBaseType$$())"); methodBuilder.beginControlFlow( "return switch (oldData.getBaseType$$())");
@ -169,19 +171,24 @@ public class GenVersion extends ClassGenerator {
var versionClassType = ClassName.get(version.getPackage(basePackageName), "Version"); var versionClassType = ClassName.get(version.getPackage(basePackageName), "Version");
dataModel.getComputedTypes(version).forEach((typeName, type) -> { dataModel.getComputedTypes(version).forEach((typeName, type) -> {
boolean shouldCreateInstanceField = type instanceof VersionedComputedType versionedComputedType boolean shouldCreateInstanceField = type instanceof VersionedComputedType versionedComputedType
&& versionedComputedType.getVersion().equals(version); && versionedComputedType.getVersion().equals(version) && !version.isCurrent();
if (!shouldCreateInstanceField) { if (!shouldCreateInstanceField) {
return; return;
} }
var nextVersion = Objects.requireNonNull(dataModel.getNextVersion(type));
var upgraderFieldLocation = type.getJUpgraderInstance(basePackageName); var upgraderFieldLocation = type.getJUpgraderInstance(basePackageName);
if (!versionClassType.equals(upgraderFieldLocation.className())) { if (!versionClassType.equals(upgraderFieldLocation.className())) {
return; return;
} }
var genericClassName = ParameterizedTypeName.get(ClassName.get(DataUpgrader.class),
type.getJTypeName(basePackageName), nextVersion.getJTypeName(basePackageName)
);
var upgraderClassName = type.getJUpgraderName(basePackageName); var upgraderClassName = type.getJUpgraderName(basePackageName);
var fieldBuilder = FieldSpec.builder(upgraderClassName, var fieldBuilder = FieldSpec.builder(genericClassName,
upgraderFieldLocation.fieldName(), upgraderFieldLocation.fieldName(),
Modifier.PUBLIC, Modifier.PUBLIC,
Modifier.STATIC, Modifier.STATIC,
@ -199,15 +206,16 @@ public class GenVersion extends ClassGenerator {
methodBuilder.addAnnotation(Override.class); methodBuilder.addAnnotation(Override.class);
methodBuilder.addException(ClassName.get(IOException.class)); methodBuilder.addException(ClassName.get(IOException.class));
var iBaseTypeClassName = ClassName.get(version.getDataPackage(basePackageName), "IBaseType"); var iBaseTypeClassName = ClassName.get(version.getPackage(basePackageName), "IBaseType");
methodBuilder.addTypeVariable(TypeVariableName.get("T", iBaseTypeClassName)); methodBuilder.addTypeVariable(TypeVariableName.get("T", iBaseTypeClassName));
var baseTypeClassName = ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType"); var baseTypeClassName = ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType");
methodBuilder.addParameter(baseTypeClassName, "type"); methodBuilder.addParameter(baseTypeClassName, "type");
methodBuilder.returns(ParameterizedTypeName.get(ClassName.get(DataSerializer.class), TypeVariableName.get("T"))); var returnType = ParameterizedTypeName.get(ClassName.get(DataSerializer.class), TypeVariableName.get("T"));
methodBuilder.returns(returnType);
methodBuilder.beginControlFlow("return switch (type)"); methodBuilder.beginControlFlow("return ($T) switch (type)", returnType);
dataModel.getBaseTypesComputed(version).forEach(baseType -> { dataModel.getBaseTypesComputed(version).forEach(baseType -> {
var field = baseType.getJSerializerInstance(basePackageName); var field = baseType.getJSerializerInstance(basePackageName);
methodBuilder.addStatement("case $N -> $T.$N", baseType.getName(), field.className(), field.fieldName()); methodBuilder.addStatement("case $N -> $T.$N", baseType.getName(), field.className(), field.fieldName());

View File

@ -0,0 +1,682 @@
/*
* Copyright (C) 2002-2022 Sebastiano Vigna
*
* 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.
*/
package it.cavallium.data.generator.nativedata;
import it.unimi.dsi.fastutil.objects.AbstractObjectList;
import it.unimi.dsi.fastutil.objects.ObjectArrays;
import it.unimi.dsi.fastutil.objects.ObjectIterators;
import it.unimi.dsi.fastutil.objects.ObjectList;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import it.unimi.dsi.fastutil.objects.ObjectSpliterator;
import it.unimi.dsi.fastutil.objects.ObjectSpliterators;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;
import java.util.function.Consumer;
import org.jetbrains.annotations.NotNull;
public class ImmutableWrappedArrayList<K> extends AbstractObjectList<K> implements RandomAccess {
/** The backing array. */
protected final K[] a;
/** The current actual size of the list (never greater than the backing-array length). */
protected final int size;
/**
* Creates a new array list and fills it with the elements of a given array.
*
* @param a an array whose elements will be used to fill the array list.
*/
public ImmutableWrappedArrayList(final K[] a) {
this.a = a;
this.size = a.length;
}
/**
* Creates an array list using an array of elements.
*
* @param init a the array the will become the new backing array of the array list.
* @return a new array list backed by the given array.
*/
@SafeVarargs
public static <K> ImmutableWrappedArrayList<K> of(final K... init) {
return new ImmutableWrappedArrayList<>(init);
}
private UnsupportedOperationException ex() {
return new UnsupportedOperationException("Immutable");
}
@Override
public void add(final int index, final K k) {
throw ex();
}
@Override
public boolean add(final K k) {
throw ex();
}
@Override
public K get(final int index) {
if (index >= size) throw new IndexOutOfBoundsException("Index (" + index + ") is greater than or equal to list size (" + size + ")");
return a[index];
}
@Override
public int indexOf(final Object k) {
for (int i = 0; i < size; i++) if (java.util.Objects.equals(k, a[i])) return i;
return -1;
}
@Override
public int lastIndexOf(final Object k) {
for (int i = size; i-- != 0;) if (java.util.Objects.equals(k, a[i])) return i;
return -1;
}
@Override
public K remove(final int index) {
throw ex();
}
@Override
public boolean remove(final Object k) {
throw ex();
}
@Override
public K set(final int index, final K k) {
throw ex();
}
@Override
public void clear() {
throw ex();
}
@Override
public int size() {
return size;
}
@Override
public void size(final int size) {
throw ex();
}
@Override
public boolean isEmpty() {
return size == 0;
}
private class SubList extends AbstractObjectList.ObjectRandomAccessSubList<K> {
protected SubList(int from, int to) {
super(ImmutableWrappedArrayList.this, from, to);
}
// Most of the inherited methods should be fine, but we can override a few of them for performance.
// Needed because we can't access the parent class' instance variables directly in a different
// instance of SubList.
private K[] getParentArray() {
return a;
}
@Override
public K get(int i) {
ensureRestrictedIndex(i);
return a[i + from];
}
private final class SubListIterator extends ObjectIterators.AbstractIndexBasedListIterator<K> {
// We are using pos == 0 to be 0 relative to SubList.from (meaning you need to do a[from + i] when
// accessing array).
SubListIterator(int index) {
super(0, index);
}
@Override
protected K get(int i) {
return a[from + i];
}
@Override
protected void add(int i, K k) {
SubList.this.add(i, k);
}
@Override
protected void set(int i, K k) {
SubList.this.set(i, k);
}
@Override
protected void remove(int i) {
SubList.this.remove(i);
}
@Override
protected int getMaxPos() {
return to - from;
}
@Override
public K next() {
if (!hasNext()) throw new NoSuchElementException();
return a[from + (lastReturned = pos++)];
}
@Override
public K previous() {
if (!hasPrevious()) throw new NoSuchElementException();
return a[from + (lastReturned = --pos)];
}
@Override
public void forEachRemaining(final Consumer<? super K> action) {
final int max = to - from;
while (pos < max) {
action.accept(a[from + (lastReturned = pos++)]);
}
}
}
@Override
public @NotNull ObjectListIterator<K> listIterator(int index) {
return new SubListIterator(index);
}
private final class SubListSpliterator extends ObjectSpliterators.LateBindingSizeIndexBasedSpliterator<K> {
// We are using pos == 0 to be 0 relative to real array 0
SubListSpliterator() {
super(from);
}
private SubListSpliterator(int pos, int maxPos) {
super(pos, maxPos);
}
@Override
protected int getMaxPosFromBackingStore() {
return to;
}
@Override
protected K get(int i) {
return a[i];
}
@Override
protected SubListSpliterator makeForSplit(int pos, int maxPos) {
return new SubListSpliterator(pos, maxPos);
}
@Override
public boolean tryAdvance(final Consumer<? super K> action) {
if (pos >= getMaxPos()) return false;
action.accept(a[pos++]);
return true;
}
@Override
public void forEachRemaining(final Consumer<? super K> action) {
final int max = getMaxPos();
while (pos < max) {
action.accept(a[pos++]);
}
}
}
@Override
public ObjectSpliterator<K> spliterator() {
return new SubListSpliterator();
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (o == null) return false;
if (!(o instanceof java.util.List)) return false;
if (o instanceof ImmutableWrappedArrayList) {
@SuppressWarnings("unchecked") ImmutableWrappedArrayList<K> other = (ImmutableWrappedArrayList<K>)o;
return Arrays.equals(a, from, to, other.a, 0, other.size());
}
if (o instanceof ImmutableWrappedArrayList.SubList) {
@SuppressWarnings("unchecked")
ImmutableWrappedArrayList<K>.SubList other = (ImmutableWrappedArrayList<K>.SubList)o;
return Arrays.equals(a, from, to, other.getParentArray(), other.from, other.to);
}
return super.equals(o);
}
@SuppressWarnings("unchecked")
int contentsCompareTo(K[] otherA, int otherAFrom, int otherATo) {
return Arrays.compare(a, from, to, otherA, otherAFrom, otherATo, (o1, o2) -> ((Comparable<K>)o1).compareTo(o2));
}
@SuppressWarnings("unchecked")
@Override
public int compareTo(final java.util.List<? extends K> l) {
if (l instanceof ImmutableWrappedArrayList) {
@SuppressWarnings("unchecked") ImmutableWrappedArrayList<K> other = (ImmutableWrappedArrayList<K>)l;
return contentsCompareTo(other.a, 0, other.size());
}
if (l instanceof ImmutableWrappedArrayList.SubList) {
@SuppressWarnings("unchecked")
ImmutableWrappedArrayList<K>.SubList other = (ImmutableWrappedArrayList<K>.SubList)l;
return contentsCompareTo(other.getParentArray(), other.from, other.to);
}
return super.compareTo(l);
}
// We don't override subList as we want AbstractList's "sub-sublist" nesting handling,
// which would be tricky to do here.
// TODO Do override it so array access isn't sent through N indirections.
// This will likely mean making this class static.
}
@Override
public ObjectList<K> subList(int from, int to) {
if (from == 0 && to == size()) return this;
ensureIndex(from);
ensureIndex(to);
if (from > to) throw new IndexOutOfBoundsException("Start index (" + from + ") is greater than end index (" + to + ")");
return new SubList(from, to);
}
/**
* Copies element of this type-specific list into the given array using optimized system calls.
*
* @param from the start index (inclusive).
* @param a the destination array.
* @param offset the offset into the destination array where to store the first element copied.
* @param length the number of elements to be copied.
*/
@Override
public void getElements(final int from, final Object[] a, final int offset, final int length) {
ObjectArrays.ensureOffsetLength(a, offset, length);
System.arraycopy(this.a, from, a, offset, length);
}
/**
* Removes elements of this type-specific list using optimized system calls.
*
* @param from the start index (inclusive).
* @param to the end index (exclusive).
*/
@Override
public void removeElements(final int from, final int to) {
throw ex();
}
/**
* Adds elements to this type-specific list using optimized system calls.
*
* @param index the index at which to add elements.
* @param a the array containing the elements.
* @param offset the offset of the first element to add.
* @param length the number of elements to add.
*/
@Override
public void addElements(final int index, final K[] a, final int offset, final int length) {
throw ex();
}
/**
* Sets elements to this type-specific list using optimized system calls.
*
* @param index the index at which to start setting elements.
* @param a the array containing the elements.
* @param offset the offset of the first element to add.
* @param length the number of elements to add.
*/
@Override
public void setElements(final int index, final K[] a, final int offset, final int length) {
ensureIndex(index);
ObjectArrays.ensureOffsetLength(a, offset, length);
if (index + length > size) throw new IndexOutOfBoundsException("End index (" + (index + length) + ") is greater than list size (" + size + ")");
System.arraycopy(a, offset, this.a, index, length);
}
@Override
public void forEach(final Consumer<? super K> action) {
for (int i = 0; i < size; ++i) {
action.accept(a[i]);
}
}
@Override
public boolean addAll(int index, final Collection<? extends K> c) {
throw ex();
}
@Override
public boolean addAll(final int index, final ObjectList<? extends K> l) {
throw ex();
}
@Override
public boolean removeAll(final @NotNull Collection<?> c) {
throw ex();
}
@Override
public Object[] toArray() {
final int size = size();
// A subtle part of the spec says the returned array must be Object[] exactly.
if (size == 0) return it.unimi.dsi.fastutil.objects.ObjectArrays.EMPTY_ARRAY;
return Arrays.copyOf(a, size, Object[].class);
}
@SuppressWarnings("unchecked")
@Override
public <K2> K2[] toArray(K2[] a) {
if (a == null) {
a = (K2[])new Object[size()];
} else if (a.length < size()) {
a = (K2[])Array.newInstance(a.getClass().getComponentType(), size());
}
//noinspection ReassignedVariable,SuspiciousSystemArraycopy
System.arraycopy(this.a, 0, a, 0, size());
if (a.length > size()) {
a[size()] = null;
}
return a;
}
@Override
public ObjectListIterator<K> listIterator(final int index) {
ensureIndex(index);
return new ObjectListIterator<>() {
int pos = index, last = -1;
@Override
public boolean hasNext() {
return pos < size;
}
@Override
public boolean hasPrevious() {
return pos > 0;
}
@Override
public K next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return a[last = pos++];
}
@Override
public K previous() {
if (!hasPrevious()) {
throw new NoSuchElementException();
}
return a[last = --pos];
}
@Override
public int nextIndex() {
return pos;
}
@Override
public int previousIndex() {
return pos - 1;
}
@Override
public void add(K k) {
ImmutableWrappedArrayList.this.add(pos++, k);
last = -1;
}
@Override
public void set(K k) {
if (last == -1) {
throw new IllegalStateException();
}
ImmutableWrappedArrayList.this.set(last, k);
}
@Override
public void remove() {
if (last == -1) {
throw new IllegalStateException();
}
ImmutableWrappedArrayList.this.remove(last);
/* If the last operation was a next(), we are removing an element *before* us, and we must decrease pos correspondingly. */
if (last < pos) {
pos--;
}
last = -1;
}
@Override
public void forEachRemaining(final Consumer<? super K> action) {
while (pos < size) {
action.accept(a[last = pos++]);
}
}
@Override
public int back(int n) {
if (n < 0) {
throw new IllegalArgumentException("Argument must be nonnegative: " + n);
}
final int remaining = size - pos;
if (n < remaining) {
pos -= n;
} else {
n = remaining;
pos = 0;
}
last = pos;
return n;
}
@Override
public int skip(int n) {
if (n < 0) {
throw new IllegalArgumentException("Argument must be nonnegative: " + n);
}
final int remaining = size - pos;
if (n < remaining) {
pos += n;
} else {
n = remaining;
pos = size;
}
last = pos - 1;
return n;
}
};
}
// If you update this, you will probably want to update ArraySet as well
private final class Spliterator implements ObjectSpliterator<K> {
// Until we split, we will track the size of the list.
// Once we split, then we stop updating on structural modifications.
// Aka, size is late-binding.
boolean hasSplit;
int pos, max;
public Spliterator() {
this(0, ImmutableWrappedArrayList.this.size, false);
}
private Spliterator(int pos, int max, boolean hasSplit) {
assert pos <= max : "pos " + pos + " must be <= max " + max;
this.pos = pos;
this.max = max;
this.hasSplit = hasSplit;
}
private int getWorkingMax() {
return hasSplit ? max : ImmutableWrappedArrayList.this.size;
}
@Override
public int characteristics() {
return ObjectSpliterators.LIST_SPLITERATOR_CHARACTERISTICS;
}
@Override
public long estimateSize() {
return getWorkingMax() - pos;
}
@Override
public boolean tryAdvance(final Consumer<? super K> action) {
if (pos >= getWorkingMax()) return false;
action.accept(a[pos++]);
return true;
}
@Override
public void forEachRemaining(final Consumer<? super K> action) {
for (final int max = getWorkingMax(); pos < max; ++pos) {
action.accept(a[pos]);
}
}
@Override
public long skip(long n) {
if (n < 0) throw new IllegalArgumentException("Argument must be nonnegative: " + n);
final int max = getWorkingMax();
if (pos >= max) return 0;
final int remaining = max - pos;
if (n < remaining) {
pos = it.unimi.dsi.fastutil.SafeMath.safeLongToInt(pos + n);
return n;
}
n = remaining;
pos = max;
return n;
}
@Override
public ObjectSpliterator<K> trySplit() {
final int max = getWorkingMax();
int retLen = (max - pos) >> 1;
if (retLen <= 1) return null;
// Update instance max with the last seen list size (if needed) before continuing
this.max = max;
int myNewPos = pos + retLen;
int oldPos = pos;
this.pos = myNewPos;
this.hasSplit = true;
return new Spliterator(oldPos, myNewPos, true);
}
}
/**
* {@inheritDoc}
*
* <p>
* The returned spliterator is late-binding; it will track structural changes after the current
* index, up until the first {@link java.util.Spliterator#trySplit() trySplit()}, at which point the
* maximum index will be fixed. <br>
* Structural changes before the current index or after the first
* {@link java.util.Spliterator#trySplit() trySplit()} will result in unspecified behavior.
*/
@Override
public ObjectSpliterator<K> spliterator() {
// If it wasn't for the possibility of the list being expanded or shrunk,
// we could return SPLITERATORS.wrap(a, 0, size).
return new Spliterator();
}
@Override
public void sort(final Comparator<? super K> comp) {
if (comp == null) {
ObjectArrays.stableSort(a, 0, size);
} else {
ObjectArrays.stableSort(a, 0, size, comp);
}
}
@Override
public void unstableSort(final Comparator<? super K> comp) {
if (comp == null) {
ObjectArrays.unstableSort(a, 0, size);
} else {
ObjectArrays.unstableSort(a, 0, size, comp);
}
}
/**
* Compares this type-specific array list to another one.
*
* @apiNote This method exists only for sake of efficiency. The implementation inherited from the
* abstract implementation would already work.
*
* @param l a type-specific array list.
* @return true if the argument contains the same elements of this type-specific array list.
*/
public boolean equals(final ImmutableWrappedArrayList<K> l) {
return Arrays.equals(a, 0, size(), l.a, 0, l.size());
}
@SuppressWarnings({ "unchecked", "unlikely-arg-type" })
@Override
public boolean equals(final Object o) {
if (o == this) return true;
if (o == null) return false;
if (!(o instanceof java.util.List)) return false;
if (o instanceof ImmutableWrappedArrayList) {
// Safe cast because we are only going to take elements from other list, never give them
return equals((ImmutableWrappedArrayList<K>)o);
}
if (o instanceof ImmutableWrappedArrayList.SubList) {
// Safe cast because we are only going to take elements from other list, never give them
// Sublist has an optimized sub-array based comparison, reuse that.
return o.equals(this);
}
return super.equals(o);
}
/**
* Compares this array list to another array list.
*
* @apiNote This method exists only for sake of efficiency. The implementation inherited from the
* abstract implementation would already work.
*
* @param l an array list.
* @return a negative integer, zero, or a positive integer as this list is lexicographically less
* than, equal to, or greater than the argument.
*/
@SuppressWarnings("unchecked")
public int compareTo(final ImmutableWrappedArrayList<? extends K> l) {
return Arrays.compare(a, 0, size(), l.a, 0, l.size(), (o1, o2) -> ((Comparable<K>) o1).compareTo(o2));
}
@SuppressWarnings("unchecked")
@Override
public int compareTo(final java.util.List<? extends K> l) {
if (l instanceof ImmutableWrappedArrayList) {
return compareTo((ImmutableWrappedArrayList<? extends K>)l);
}
if (l instanceof ImmutableWrappedArrayList.SubList) {
// Must negate because we are inverting the order of the comparison.
return -((ImmutableWrappedArrayList<K>.SubList)l).compareTo(this);
}
return super.compareTo(l);
}
}