Partial new generator

This commit is contained in:
Andrea Cavalli 2023-01-20 01:14:37 +01:00
parent b446dd682b
commit 8ab50ddefb
40 changed files with 2767 additions and 3261 deletions

1
.gitignore vendored
View File

@ -9,3 +9,4 @@
.flattened-pom.xml .flattened-pom.xml
/.idea/ /.idea/
/data-generator-plugin/src/test/java/it/test/

View File

@ -0,0 +1,87 @@
package it.cavallium.data.generator.plugin;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static java.nio.file.StandardOpenOption.WRITE;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeSpec.Builder;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class ClassGenerator {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final HashSet<Path> generatedFilesToDelete;
protected final DataModel dataModel;
protected final String basePackageName;
private final Path outPath;
protected final boolean deepCheckBeforeCreatingNewEqualInstances;
public ClassGenerator(ClassGeneratorParams params) {
this.generatedFilesToDelete = params.generatedFilesToDelete;
this.dataModel = params.dataModel;
this.basePackageName = params.basePackageName;
this.outPath = params.outPath;
this.deepCheckBeforeCreatingNewEqualInstances = params.deepCheckBeforeCreatingNewEqualInstances;
}
public void run() throws IOException {
for (GeneratedClass generatedClass : generateClasses().toList()) {
writeClass(generatedClass.packageName, generatedClass.content);
}
}
private void writeClass(String classPackage, Builder versionsClass) throws IOException {
var sb = new StringBuilder();
var typeSpec = versionsClass.build();
var outJavaFile = outPath;
for (String part : classPackage.split("\\.")) {
outJavaFile = outJavaFile.resolve(part);
}
if (Files.notExists(outJavaFile)) {
Files.createDirectories(outJavaFile);
}
outJavaFile = outJavaFile.resolve(typeSpec.name + ".java");
JavaFile.builder(classPackage, typeSpec).build().writeTo(sb);
String newFile = sb.toString();
boolean mustWrite;
if (Files.isRegularFile(outJavaFile) && Files.isReadable(outJavaFile)) {
String oldFile = Files.readString(outJavaFile, StandardCharsets.UTF_8);
mustWrite = !oldFile.equals(newFile);
} else {
mustWrite = true;
}
if (mustWrite) {
logger.debug("File {} changed", outJavaFile);
Files.writeString(outJavaFile, newFile, StandardCharsets.UTF_8, TRUNCATE_EXISTING, CREATE, WRITE);
} else {
logger.debug("File {} is the same, unchanged", outJavaFile);
}
markFileAsCreated(generatedFilesToDelete, outPath, outJavaFile);
}
private void markFileAsCreated(Set<Path> generatedFilesToDelete, Path basePath, Path filePath) {
generatedFilesToDelete.remove(basePath.relativize(filePath));
}
protected abstract Stream<GeneratedClass> generateClasses();
public record GeneratedClass(String packageName, TypeSpec.Builder content) {}
public record ClassGeneratorParams(HashSet<Path> generatedFilesToDelete,
DataModel dataModel,
String basePackageName,
Path outPath,
boolean deepCheckBeforeCreatingNewEqualInstances) {}
}

View File

@ -1,20 +1,34 @@
package it.cavallium.data.generator.plugin; package it.cavallium.data.generator.plugin;
import java.util.ArrayList; import com.squareup.javapoet.TypeName;
import java.util.LinkedHashMap; import it.cavallium.data.generator.plugin.ComputedType.VersionedComputedType;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream; import java.util.stream.Stream;
public sealed interface ComputedType { public sealed interface ComputedType permits VersionedComputedType, ComputedTypeArray, ComputedTypeCustom,
ComputedTypeNative, ComputedTypeNullable {
String getName(); String getName();
sealed interface VersionedComputedType extends ComputedType { TypeName getJTypeName(String basePackageName);
int getVersion(); TypeName getJSerializerName(String basePackageName);
ComputedType withChangeAtVersion(int version, VersionChangeChecker versionChangeChecker); FieldLocation getJSerializerInstance(String basePackageName);
TypeName getJUpgraderName(String basePackageName);
FieldLocation getJUpgraderInstance(String basePackageName);
sealed interface VersionedComputedType extends ComputedType permits ComputedTypeArrayVersioned, ComputedTypeBase,
ComputedTypeNullableVersioned, ComputedTypeSuper {
ComputedVersion getVersion();
default boolean shouldUpgradeAfter(ComputedVersion version) {
return !version.isCurrent() && version.getVersion() == this.getVersion().getVersion();
}
ComputedType withChangeAtVersion(ComputedVersion version, VersionChangeChecker versionChangeChecker);
} }
/** /**
@ -27,461 +41,4 @@ public sealed interface ComputedType {
*/ */
Stream<ComputedType> getDependents(); Stream<ComputedType> getDependents();
final class ComputedBaseType implements VersionedComputedType {
private final VersionedType type;
private final String stringRepresenter;
private final LinkedHashMap<String, VersionedType> data;
private LinkedHashMap<String, ComputedType> computedData;
private final ComputedTypeSupplier computedTypeSupplier;
public ComputedBaseType(VersionedType type,
String stringRepresenter,
LinkedHashMap<String, VersionedType> data,
ComputedTypeSupplier computedTypeSupplier) {
this.type = type;
if (type.type().startsWith("~") || type.type().startsWith("-")) {
throw new IllegalStateException();
}
this.computedTypeSupplier = computedTypeSupplier;
this.stringRepresenter = stringRepresenter;
this.data = data;
}
public String getType() {
return type.type();
}
@Override
public int getVersion() {
return type.version();
}
@Override
public ComputedBaseType withChangeAtVersion(int version, VersionChangeChecker versionChangeChecker) {
var newData = new LinkedHashMap<String, VersionedType>();
data.forEach((k, v) -> newData.put(k, v.withVersionIfChanged(version, versionChangeChecker)));
return new ComputedBaseType(type.withVersion(version), stringRepresenter, newData, computedTypeSupplier);
}
public String getStringRepresenter() {
return stringRepresenter;
}
public LinkedHashMap<String, ComputedType> getData() {
synchronized (this) {
if (computedData == null) {
computedData = new LinkedHashMap<>();
data.forEach((k, v) -> computedData.put(k, computedTypeSupplier.get(v)));
}
}
return computedData;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ComputedBaseType that = (ComputedBaseType) o;
if (!Objects.equals(type, that.type)) {
return false;
}
if (!Objects.equals(stringRepresenter, that.stringRepresenter)) {
return false;
}
return Objects.equals(data, that.data);
}
@Override
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (stringRepresenter != null ? stringRepresenter.hashCode() : 0);
result = 31 * result + (data != null ? data.hashCode() : 0);
return result;
}
@Override
public String getName() {
return type.type();
}
@Override
public Stream<ComputedType> getDependencies() {
return this.data.values().stream().map(computedTypeSupplier::get);
}
@Override
public Stream<ComputedType> getDependents() {
return computedTypeSupplier.getDependents(type);
}
}
final class ComputedCustomType implements ComputedType {
private final String type;
private final String javaClass;
private final String serializer;
private final ComputedTypeSupplier computedTypeSupplier;
public ComputedCustomType(String type, String javaClass, String serializer, ComputedTypeSupplier computedTypeSupplier) {
this.type = type;
this.javaClass = javaClass;
this.serializer = serializer;
this.computedTypeSupplier = computedTypeSupplier;
}
public String getType() {
return type;
}
public String getJavaClass() {
return javaClass;
}
public String getSerializer() {
return serializer;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ComputedCustomType that = (ComputedCustomType) o;
if (!Objects.equals(type, that.type)) {
return false;
}
if (!Objects.equals(javaClass, that.javaClass)) {
return false;
}
return Objects.equals(serializer, that.serializer);
}
@Override
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (javaClass != null ? javaClass.hashCode() : 0);
result = 31 * result + (serializer != null ? serializer.hashCode() : 0);
return result;
}
@Override
public String getName() {
return type;
}
@Override
public Stream<ComputedType> getDependencies() {
return Stream.of();
}
@Override
public Stream<ComputedType> getDependents() {
return computedTypeSupplier.getDependents(new VersionedType(type, 0));
}
}
final class ComputedNullableType implements VersionedComputedType {
private final VersionedType baseType;
private ComputedType computedChild;
private final ComputedTypeSupplier computedTypeSupplier;
public ComputedNullableType(VersionedType baseType, ComputedTypeSupplier computedTypeSupplier) {
this.baseType = baseType;
this.computedTypeSupplier = computedTypeSupplier;
}
public String getBaseType() {
return baseType.type();
}
@Override
public int getVersion() {
return baseType.version();
}
@Override
public ComputedNullableType withChangeAtVersion(int version, VersionChangeChecker versionChangeChecker) {
return new ComputedNullableType(baseType.withVersion(version), computedTypeSupplier);
}
public ComputedType child() {
synchronized (this) {
if (computedChild == null) {
computedChild = computedTypeSupplier.get(baseType);
}
}
if (computedChild instanceof ComputedNullableType) {
throw new IllegalStateException();
} else if (computedChild instanceof ComputedArrayType) {
throw new IllegalStateException();
}
return computedChild;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ComputedNullableType that = (ComputedNullableType) o;
return Objects.equals(baseType, that.baseType);
}
@Override
public int hashCode() {
return baseType != null ? baseType.hashCode() : 0;
}
@Override
public String getName() {
return "-" + baseType.type();
}
@Override
public Stream<ComputedType> getDependencies() {
return Stream.of(child());
}
@Override
public Stream<ComputedType> getDependents() {
return computedTypeSupplier.getDependents(new VersionedType(getName(), 0));
}
}
final class ComputedSuperType implements VersionedComputedType {
private final VersionedType type;
private final List<VersionedType> subTypes;
private List<ComputedType> computedSubTypes;
private final ComputedTypeSupplier computedTypeSupplier;
public ComputedSuperType(VersionedType type,
List<VersionedType> subType,
ComputedTypeSupplier computedTypeSupplier) {
this.type = type;
this.subTypes = subType;
this.computedTypeSupplier = computedTypeSupplier;
}
public String getType() {
return type.type();
}
@Override
public int getVersion() {
return type.version();
}
@Override
public ComputedSuperType withChangeAtVersion(int version, VersionChangeChecker versionChangeChecker) {
return new ComputedSuperType(type.withVersion(version),
subTypes.stream().map(subType -> subType.withVersionIfChanged(version, versionChangeChecker)).toList(),
computedTypeSupplier
);
}
public List<ComputedType> subTypes() {
synchronized (this) {
if (computedSubTypes == null) {
computedSubTypes = new ArrayList<>();
for (VersionedType subType : subTypes) {
computedSubTypes.add(computedTypeSupplier.get(subType));
}
}
}
return computedSubTypes;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ComputedSuperType that = (ComputedSuperType) o;
if (!Objects.equals(type, that.type)) {
return false;
}
return Objects.equals(subTypes, that.subTypes);
}
@Override
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (subTypes != null ? subTypes.hashCode() : 0);
return result;
}
@Override
public Stream<ComputedType> getDependencies() {
return subTypes().stream();
}
@Override
public Stream<ComputedType> getDependents() {
return computedTypeSupplier.getDependents(new VersionedType(getName(), 0));
}
@Override
public String getName() {
return type.type();
}
}
final class ComputedArrayType implements VersionedComputedType {
private final VersionedType baseType;
private ComputedType computedChild;
private final ComputedTypeSupplier computedTypeSupplier;
public ComputedArrayType(VersionedType baseType, ComputedTypeSupplier computedTypeSupplier) {
this.baseType = baseType;
this.computedTypeSupplier = computedTypeSupplier;
}
public String getBaseType() {
return baseType.type();
}
@Override
public int getVersion() {
return baseType.version();
}
@Override
public ComputedArrayType withChangeAtVersion(int version, VersionChangeChecker versionChangeChecker) {
return new ComputedArrayType(baseType.withVersion(version), computedTypeSupplier);
}
public ComputedType child() {
synchronized (this) {
if (computedChild == null) {
computedChild = computedTypeSupplier.get(baseType);
}
}
if (computedChild instanceof ComputedNullableType) {
throw new IllegalStateException();
} else if (computedChild instanceof ComputedArrayType) {
throw new IllegalStateException();
}
return computedChild;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ComputedArrayType that = (ComputedArrayType) o;
return Objects.equals(baseType, that.baseType);
}
@Override
public int hashCode() {
return baseType != null ? baseType.hashCode() : 0;
}
@Override
public String getName() {
return "§" + baseType.type();
}
@Override
public Stream<ComputedType> getDependencies() {
return Stream.of(child());
}
@Override
public Stream<ComputedType> getDependents() {
return computedTypeSupplier.getDependents(new VersionedType(getName(), 0));
}
}
final class ComputedNativeType implements ComputedType {
private final String type;
private final ComputedTypeSupplier computedTypeSupplier;
public ComputedNativeType(String type, ComputedTypeSupplier computedTypeSupplier) {
this.type = type;
this.computedTypeSupplier = computedTypeSupplier;
}
public String getName() {
return type;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ComputedNativeType that = (ComputedNativeType) o;
return Objects.equals(type, that.type);
}
@Override
public int hashCode() {
return type != null ? type.hashCode() : 0;
}
@Override
public Stream<ComputedType> getDependencies() {
return Stream.of();
}
@Override
public Stream<ComputedType> getDependents() {
return computedTypeSupplier.getDependents(new VersionedType(getName(), 0));
}
public static List<ComputedNativeType> get(ComputedTypeSupplier computedTypeSupplier) {
return Stream.of("String",
"boolean",
"short",
"char",
"int",
"long",
"float",
"double",
"byte",
"Int52").map(name -> new ComputedNativeType(name, computedTypeSupplier)).toList();
}
}
} }

View File

@ -0,0 +1,7 @@
package it.cavallium.data.generator.plugin;
public sealed interface ComputedTypeArray extends ComputedType permits ComputedTypeArrayNative,
ComputedTypeArrayVersioned {
ComputedType getBase();
}

View File

@ -0,0 +1,147 @@
package it.cavallium.data.generator.plugin;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import it.cavallium.data.generator.nativedata.ArrayInt52Serializer;
import it.cavallium.data.generator.nativedata.ArrayStringSerializer;
import it.cavallium.data.generator.nativedata.ArraybooleanSerializer;
import it.cavallium.data.generator.nativedata.ArraybyteSerializer;
import it.cavallium.data.generator.nativedata.ArraycharSerializer;
import it.cavallium.data.generator.nativedata.ArraydoubleSerializer;
import it.cavallium.data.generator.nativedata.ArrayfloatSerializer;
import it.cavallium.data.generator.nativedata.ArrayintSerializer;
import it.cavallium.data.generator.nativedata.ArraylongSerializer;
import it.cavallium.data.generator.nativedata.ArrayshortSerializer;
import it.cavallium.data.generator.nativedata.Serializers;
import it.unimi.dsi.fastutil.booleans.BooleanList;
import it.unimi.dsi.fastutil.bytes.ByteList;
import it.unimi.dsi.fastutil.chars.CharList;
import it.unimi.dsi.fastutil.doubles.DoubleList;
import it.unimi.dsi.fastutil.floats.FloatList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.longs.LongList;
import it.unimi.dsi.fastutil.shorts.ShortList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
public final class ComputedTypeArrayNative implements ComputedTypeArray {
private final String baseType;
private ComputedTypeNative computedChild;
private final ComputedTypeSupplier computedTypeSupplier;
public ComputedTypeArrayNative(String baseType, ComputedTypeSupplier computedTypeSupplier) {
this.baseType = baseType;
this.computedTypeSupplier = computedTypeSupplier;
}
public ComputedType getBase() {
return child();
}
public ComputedTypeNative child() {
synchronized (this) {
if (computedChild == null) {
var computedChild = computedTypeSupplier.get(baseType);
if (computedChild instanceof ComputedTypeNative computedTypeNative) {
this.computedChild = computedTypeNative;
} else {
throw new IllegalStateException();
}
}
}
return computedChild;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ComputedTypeArrayNative that = (ComputedTypeArrayNative) o;
return Objects.equals(baseType, that.baseType);
}
@Override
public int hashCode() {
return baseType != null ? baseType.hashCode() : 0;
}
@Override
public String getName() {
return "§" + baseType;
}
@Override
public TypeName getJTypeName(String basePackageName) {
return switch (baseType) {
case "boolean" -> ClassName.get(BooleanList.class);
case "byte" -> ClassName.get(ByteList.class);
case "short" -> ClassName.get(ShortList.class);
case "char" -> ClassName.get(CharList.class);
case "int" -> ClassName.get(IntList.class);
case "long" -> ClassName.get(LongList.class);
case "float" -> ClassName.get(FloatList.class);
case "double" -> ClassName.get(DoubleList.class);
default -> ParameterizedTypeName.get(ClassName.get(List.class),
computedTypeSupplier.get(baseType).getJTypeName(basePackageName));
};
}
@Override
public ClassName getJSerializerName(String basePackageName) {
return switch (baseType) {
case "boolean" -> ClassName.get(ArraybooleanSerializer.class);
case "byte" -> ClassName.get(ArraybyteSerializer.class);
case "short" -> ClassName.get(ArrayshortSerializer.class);
case "char" -> ClassName.get(ArraycharSerializer.class);
case "int" -> ClassName.get(ArrayintSerializer.class);
case "long" -> ClassName.get(ArraylongSerializer.class);
case "float" -> ClassName.get(ArrayfloatSerializer.class);
case "double" -> ClassName.get(ArraydoubleSerializer.class);
case "String" -> ClassName.get(ArrayStringSerializer.class);
case "Int52" -> ClassName.get(ArrayInt52Serializer.class);
default -> throw new UnsupportedOperationException();
};
}
@Override
public FieldLocation getJSerializerInstance(String basePackageName) {
var className = ClassName.get(Serializers.class);
var serializerFieldName = "Array" + baseType + "SerializerInstance";
return new FieldLocation(className, serializerFieldName);
}
@Override
public TypeName getJUpgraderName(String basePackageName) {
throw new UnsupportedOperationException("Not upgradable");
}
@Override
public FieldLocation getJUpgraderInstance(String basePackageName) {
throw new UnsupportedOperationException("Not upgradable");
}
@Override
public Stream<ComputedType> getDependencies() {
return Stream.of(child());
}
@Override
public Stream<ComputedType> getDependents() {
return computedTypeSupplier.getDependents(getName());
}
@Override
public String toString() {
return baseType + "[]";
}
}

View File

@ -0,0 +1,126 @@
package it.cavallium.data.generator.plugin;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import it.cavallium.data.generator.plugin.ComputedType.VersionedComputedType;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
public final class ComputedTypeArrayVersioned implements VersionedComputedType, ComputedTypeArray {
private final VersionedType baseType;
private VersionedComputedType computedChild;
private final ComputedTypeSupplier computedTypeSupplier;
public ComputedTypeArrayVersioned(VersionedType baseType, ComputedTypeSupplier computedTypeSupplier) {
this.baseType = baseType;
this.computedTypeSupplier = computedTypeSupplier;
}
public VersionedComputedType getBase() {
return child();
}
@Override
public ComputedVersion getVersion() {
return baseType.version();
}
@Override
public ComputedTypeArrayVersioned withChangeAtVersion(ComputedVersion version,
VersionChangeChecker versionChangeChecker) {
return new ComputedTypeArrayVersioned(baseType.withVersion(version),
computedTypeSupplier
);
}
public VersionedComputedType child() {
synchronized (this) {
if (computedChild == null) {
var computedChild = computedTypeSupplier.get(baseType);
if (computedChild instanceof ComputedTypeNullableVersioned) {
throw new IllegalStateException();
} else if (computedChild instanceof ComputedTypeArrayVersioned) {
throw new IllegalStateException();
} else if (computedChild instanceof VersionedComputedType versionedComputedType) {
this.computedChild = versionedComputedType;
} else {
throw new IllegalStateException();
}
}
}
return computedChild;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ComputedTypeArrayVersioned that = (ComputedTypeArrayVersioned) o;
return Objects.equals(baseType, that.baseType);
}
@Override
public int hashCode() {
return baseType != null ? baseType.hashCode() : 0;
}
@Override
public String getName() {
return "§" + baseType.type();
}
@Override
public TypeName getJTypeName(String basePackageName) {
return ParameterizedTypeName.get(ClassName.get(List.class),
computedTypeSupplier.get(baseType).getJTypeName(basePackageName));
}
@Override
public ClassName getJSerializerName(String basePackageName) {
return ClassName.get(baseType.version().getSerializersPackage(basePackageName), "Array" + baseType.type() + "Serializer");
}
@Override
public FieldLocation getJSerializerInstance(String basePackageName) {
var className = ClassName.get(baseType.version().getPackage(basePackageName), "Version");
var serializerFieldName = "Array" + baseType.type() + "SerializerInstance";
return new FieldLocation(className, serializerFieldName);
}
@Override
public TypeName getJUpgraderName(String basePackageName) {
return ClassName.get(baseType.version().getUpgradersPackage(basePackageName), "Array" + baseType.type() + "Upgrader");
}
@Override
public FieldLocation getJUpgraderInstance(String basePackageName) {
var className = ClassName.get(baseType.version().getPackage(basePackageName), "Version");
var upgraderFieldName = "Array" + baseType.type() + "UpgraderInstance";
return new FieldLocation(className, upgraderFieldName);
}
@Override
public Stream<ComputedType> getDependencies() {
return Stream.of(child());
}
@Override
public Stream<ComputedType> getDependents() {
return computedTypeSupplier.getDependents(getName());
}
@Override
public String toString() {
return baseType.type() + "[] (v" + getVersion() + ")";
}
}

View File

@ -0,0 +1,146 @@
package it.cavallium.data.generator.plugin;
import static it.cavallium.data.generator.plugin.DataModel.joinPackage;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;
import it.cavallium.data.generator.nativedata.Serializers;
import it.cavallium.data.generator.plugin.ComputedType.VersionedComputedType;
import java.util.LinkedHashMap;
import java.util.Objects;
import java.util.stream.Stream;
public final class ComputedTypeBase implements VersionedComputedType {
private final VersionedType type;
private final String stringRepresenter;
private final LinkedHashMap<String, VersionedType> data;
private LinkedHashMap<String, ComputedType> computedData;
private final ComputedTypeSupplier computedTypeSupplier;
public ComputedTypeBase(VersionedType type,
String stringRepresenter,
LinkedHashMap<String, VersionedType> data,
ComputedTypeSupplier computedTypeSupplier) {
this.type = type;
if (type.type().startsWith("~") || type.type().startsWith("-")) {
throw new IllegalStateException();
}
this.computedTypeSupplier = computedTypeSupplier;
this.stringRepresenter = stringRepresenter;
this.data = data;
}
public String getType() {
return type.type();
}
@Override
public ComputedVersion getVersion() {
return type.version();
}
@Override
public ComputedTypeBase withChangeAtVersion(ComputedVersion version,
VersionChangeChecker versionChangeChecker) {
var newData = new LinkedHashMap<String, VersionedType>();
data.forEach((k, v) -> newData.put(k, v.withVersionIfChanged(version, versionChangeChecker)));
return new ComputedTypeBase(type.withVersion(version),
stringRepresenter,
newData,
computedTypeSupplier
);
}
public String getStringRepresenter() {
return stringRepresenter;
}
public LinkedHashMap<String, ComputedType> getData() {
synchronized (this) {
if (computedData == null) {
computedData = new LinkedHashMap<>();
data.forEach((k, v) -> computedData.put(k, computedTypeSupplier.get(v)));
}
}
return computedData;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ComputedTypeBase that = (ComputedTypeBase) o;
if (!Objects.equals(type, that.type)) {
return false;
}
if (!Objects.equals(stringRepresenter, that.stringRepresenter)) {
return false;
}
return Objects.equals(data, that.data);
}
@Override
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (stringRepresenter != null ? stringRepresenter.hashCode() : 0);
result = 31 * result + (data != null ? data.hashCode() : 0);
return result;
}
@Override
public String getName() {
return type.type();
}
@Override
public TypeName getJTypeName(String basePackageName) {
return ClassName.get(getVersion().getDataPackage(basePackageName), type.type());
}
@Override
public ClassName getJSerializerName(String basePackageName) {
return ClassName.get(type.version().getSerializersPackage(basePackageName), type.type() + "Serializer");
}
@Override
public FieldLocation getJSerializerInstance(String basePackageName) {
var className = ClassName.get(type.version().getPackage(basePackageName), "Version");
var serializerFieldName = type.type() + "SerializerInstance";
return new FieldLocation(className, serializerFieldName);
}
@Override
public TypeName getJUpgraderName(String basePackageName) {
return ClassName.get(type.version().getUpgradersPackage(basePackageName), type.type() + "Upgrader");
}
@Override
public FieldLocation getJUpgraderInstance(String basePackageName) {
var className = ClassName.get(type.version().getPackage(basePackageName), "Version");
var upgraderFieldName = type.type() + "UpgraderInstance";
return new FieldLocation(className, upgraderFieldName);
}
@Override
public Stream<ComputedType> getDependencies() {
return this.data.values().stream().map(computedTypeSupplier::get);
}
@Override
public Stream<ComputedType> getDependents() {
return computedTypeSupplier.getDependents(type);
}
@Override
public String toString() {
return type.type() + " (base, v" + getVersion() + ")";
}
}

View File

@ -0,0 +1,130 @@
package it.cavallium.data.generator.plugin;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import java.util.Objects;
import java.util.stream.Stream;
public final class ComputedTypeCustom implements ComputedType {
private final String type;
private final ComputedVersion latestVersion;
private final String javaClass;
private final String serializer;
private final ComputedTypeSupplier computedTypeSupplier;
private final TypeName typeName;
public ComputedTypeCustom(String type,
String javaClass,
String serializer,
ComputedTypeSupplier computedTypeSupplier,
ComputedVersion latestVersion) {
this.type = type;
this.latestVersion = latestVersion;
this.javaClass = javaClass;
this.serializer = serializer;
this.computedTypeSupplier = computedTypeSupplier;
{
int indexOfGeneric;
if ((indexOfGeneric = javaClass.indexOf("<")) == -1) {
this.typeName = ClassName.bestGuess(javaClass.trim());
} else {
var rawTypesArray = javaClass.substring(indexOfGeneric + 1, javaClass.length() - 1).split(",");
var genericsResult = new TypeName[rawTypesArray.length];
int i = 0;
for (String rawType : rawTypesArray) {
genericsResult[i] = ClassName.bestGuess(rawType.trim());
i++;
}
var base = ClassName.bestGuess(javaClass.substring(0, indexOfGeneric).trim());
this.typeName = ParameterizedTypeName.get(base, genericsResult);
}
}
}
public String getType() {
return type;
}
public String getJavaClass() {
return javaClass;
}
public String getSerializer() {
return serializer;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
it.cavallium.data.generator.plugin.ComputedTypeCustom that = (it.cavallium.data.generator.plugin.ComputedTypeCustom) o;
if (!Objects.equals(type, that.type)) {
return false;
}
if (!Objects.equals(javaClass, that.javaClass)) {
return false;
}
return Objects.equals(serializer, that.serializer);
}
@Override
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (javaClass != null ? javaClass.hashCode() : 0);
result = 31 * result + (serializer != null ? serializer.hashCode() : 0);
return result;
}
@Override
public String getName() {
return type;
}
@Override
public TypeName getJTypeName(String basePackageName) {
return typeName;
}
@Override
public TypeName getJSerializerName(String basePackageName) {
return ClassName.bestGuess(serializer);
}
@Override
public FieldLocation getJSerializerInstance(String basePackageName) {
return new FieldLocation(ClassName.get(latestVersion.getPackage(basePackageName), "Version"), type + "SerializerInstance");
}
@Override
public TypeName getJUpgraderName(String basePackageName) {
throw new UnsupportedOperationException("Not upgradable");
}
@Override
public FieldLocation getJUpgraderInstance(String basePackageName) {
throw new UnsupportedOperationException("Not upgradable");
}
@Override
public Stream<ComputedType> getDependencies() {
return Stream.of();
}
@Override
public Stream<ComputedType> getDependents() {
return computedTypeSupplier.getDependents(type);
}
@Override
public String toString() {
return type + " (custom)";
}
}

View File

@ -0,0 +1,112 @@
package it.cavallium.data.generator.plugin;
import static it.cavallium.data.generator.plugin.DataModel.joinPackage;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;
import it.cavallium.data.generator.nativedata.Int52Serializer;
import it.cavallium.data.generator.nativedata.Serializers;
import it.cavallium.data.generator.nativedata.StringSerializer;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
public final class ComputedTypeNative implements ComputedType {
private final String type;
private final ComputedTypeSupplier computedTypeSupplier;
public ComputedTypeNative(String type, ComputedTypeSupplier computedTypeSupplier) {
this.type = type;
this.computedTypeSupplier = computedTypeSupplier;
}
public String getName() {
return type;
}
@Override
public TypeName getJTypeName(String basePackageName) {
return switch (type) {
case "String" -> ClassName.get(String.class);
case "boolean" -> TypeName.BOOLEAN;
case "short" -> TypeName.SHORT;
case "char" -> TypeName.CHAR;
case "int" -> TypeName.INT;
case "long" -> TypeName.LONG;
case "float" -> TypeName.FLOAT;
case "double" -> TypeName.DOUBLE;
case "byte" -> TypeName.BYTE;
case "Int52" -> ClassName.get(it.cavallium.data.generator.nativedata.Int52.class);
default -> throw new UnsupportedOperationException(type + " is not a known native type");
};
}
@Override
public TypeName getJSerializerName(String basePackageName) {
return switch (type) {
case "String" -> ClassName.get(StringSerializer.class);
case "boolean", "byte", "short", "char", "int", "long", "float", "double" ->
throw new UnsupportedOperationException("Type " + type
+ " is a native type, so it doesn't have a serializer");
case "Int52" -> ClassName.get(Int52Serializer.class);
default -> throw new UnsupportedOperationException(type + " is not a known native type");
};
}
@Override
public FieldLocation getJSerializerInstance(String basePackageName) {
return new FieldLocation(ClassName.get(Serializers.class), type + "SerializerInstance");
}
@Override
public TypeName getJUpgraderName(String basePackageName) {
throw new UnsupportedOperationException("Type " + type + " is a native type, so it doesn't have a upgrader");
}
@Override
public FieldLocation getJUpgraderInstance(String basePackageName) {
throw new UnsupportedOperationException("Type " + type + " is a native type, so it doesn't have a upgrader");
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
it.cavallium.data.generator.plugin.ComputedTypeNative that = (it.cavallium.data.generator.plugin.ComputedTypeNative) o;
return Objects.equals(type, that.type);
}
@Override
public int hashCode() {
return type != null ? type.hashCode() : 0;
}
@Override
public Stream<ComputedType> getDependencies() {
return Stream.of();
}
@Override
public Stream<ComputedType> getDependents() {
return computedTypeSupplier.getDependents(getName());
}
public static List<it.cavallium.data.generator.plugin.ComputedTypeNative> get(ComputedTypeSupplier computedTypeSupplier) {
return Stream
.of("String", "boolean", "short", "char", "int", "long", "float", "double", "byte", "Int52")
.map(name -> new it.cavallium.data.generator.plugin.ComputedTypeNative(name, computedTypeSupplier))
.toList();
}
@Override
public String toString() {
return type + " (native)";
}
}

View File

@ -0,0 +1,7 @@
package it.cavallium.data.generator.plugin;
public sealed interface ComputedTypeNullable extends ComputedType permits ComputedTypeNullableNative,
ComputedTypeNullableVersioned {
ComputedType getBase();
}

View File

@ -0,0 +1,154 @@
package it.cavallium.data.generator.plugin;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;
import it.cavallium.data.generator.nativedata.NullableInt52;
import it.cavallium.data.generator.nativedata.NullableInt52Serializer;
import it.cavallium.data.generator.nativedata.NullableString;
import it.cavallium.data.generator.nativedata.NullableStringSerializer;
import it.cavallium.data.generator.nativedata.Nullableboolean;
import it.cavallium.data.generator.nativedata.NullablebooleanSerializer;
import it.cavallium.data.generator.nativedata.Nullablebyte;
import it.cavallium.data.generator.nativedata.NullablebyteSerializer;
import it.cavallium.data.generator.nativedata.Nullablechar;
import it.cavallium.data.generator.nativedata.NullablecharSerializer;
import it.cavallium.data.generator.nativedata.Nullabledouble;
import it.cavallium.data.generator.nativedata.NullabledoubleSerializer;
import it.cavallium.data.generator.nativedata.Nullablefloat;
import it.cavallium.data.generator.nativedata.NullablefloatSerializer;
import it.cavallium.data.generator.nativedata.Nullableint;
import it.cavallium.data.generator.nativedata.NullableintSerializer;
import it.cavallium.data.generator.nativedata.Nullablelong;
import it.cavallium.data.generator.nativedata.NullablelongSerializer;
import it.cavallium.data.generator.nativedata.Nullableshort;
import it.cavallium.data.generator.nativedata.NullableshortSerializer;
import it.cavallium.data.generator.nativedata.Serializers;
import java.util.Objects;
import java.util.stream.Stream;
public final class ComputedTypeNullableNative implements ComputedTypeNullable {
private final String baseType;
private final ComputedVersion latestVersion;
private ComputedTypeNative computedChild;
private final ComputedTypeSupplier computedTypeSupplier;
public ComputedTypeNullableNative(String baseType, ComputedVersion latestVersion, ComputedTypeSupplier computedTypeSupplier) {
this.baseType = baseType;
this.latestVersion = latestVersion;
this.computedTypeSupplier = computedTypeSupplier;
}
public ComputedTypeNative getBase() {
return child();
}
public ComputedTypeNative child() {
synchronized (this) {
if (computedChild == null) {
var computedChild = computedTypeSupplier.get(baseType);
if (computedChild instanceof ComputedTypeNative computedTypeNative) {
this.computedChild = computedTypeNative;
} else {
throw new IllegalStateException();
}
}
}
return computedChild;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ComputedTypeNullableNative that = (ComputedTypeNullableNative) o;
return Objects.equals(baseType, that.baseType);
}
@Override
public int hashCode() {
return baseType != null ? baseType.hashCode() : 0;
}
@Override
public String getName() {
return "-" + baseType;
}
@Override
public TypeName getJTypeName(String basePackageName) {
return switch (baseType) {
case "boolean" -> ClassName.get(Nullableboolean.class);
case "byte" -> ClassName.get(Nullablebyte.class);
case "short" -> ClassName.get(Nullableshort.class);
case "char" -> ClassName.get(Nullablechar.class);
case "int" -> ClassName.get(Nullableint.class);
case "long" -> ClassName.get(Nullablelong.class);
case "float" -> ClassName.get(Nullablefloat.class);
case "double" -> ClassName.get(Nullabledouble.class);
case "String" -> ClassName.get(NullableString.class);
case "Int52" -> ClassName.get(NullableInt52.class);
default -> ClassName.get(latestVersion.getDataNullablesPackage(basePackageName), "Nullable" + baseType);
};
}
@Override
public ClassName getJSerializerName(String basePackageName) {
return switch (baseType) {
case "boolean" -> ClassName.get(NullablebooleanSerializer.class);
case "byte" -> ClassName.get(NullablebyteSerializer.class);
case "short" -> ClassName.get(NullableshortSerializer.class);
case "char" -> ClassName.get(NullablecharSerializer.class);
case "int" -> ClassName.get(NullableintSerializer.class);
case "long" -> ClassName.get(NullablelongSerializer.class);
case "float" -> ClassName.get(NullablefloatSerializer.class);
case "double" -> ClassName.get(NullabledoubleSerializer.class);
case "String" -> ClassName.get(NullableStringSerializer.class);
case "Int52" -> ClassName.get(NullableInt52Serializer.class);
default -> throw new UnsupportedOperationException();
};
}
@Override
public FieldLocation getJSerializerInstance(String basePackageName) {
var className = switch (baseType) {
case "boolean", "byte", "short", "char", "int", "long", "float", "double", "String", "Int52" ->
ClassName.get(Serializers.class);
default -> throw new UnsupportedOperationException();
};
var serializerFieldName = "Nullable" + baseType + "SerializerInstance";
return new FieldLocation(className, serializerFieldName);
}
@Override
public TypeName getJUpgraderName(String basePackageName) {
throw new UnsupportedOperationException("Type " + baseType + " is a native type, so it doesn't have a upgrader");
}
@Override
public FieldLocation getJUpgraderInstance(String basePackageName) {
throw new UnsupportedOperationException("Type " + baseType + " is a native type, so it doesn't have a upgrader");
}
@Override
public Stream<ComputedType> getDependencies() {
return Stream.of(child());
}
@Override
public Stream<ComputedType> getDependents() {
return computedTypeSupplier.getDependents(getName());
}
@Override
public String toString() {
return "-" + baseType;
}
}

View File

@ -0,0 +1,122 @@
package it.cavallium.data.generator.plugin;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;
import it.cavallium.data.generator.plugin.ComputedType.VersionedComputedType;
import java.util.Objects;
import java.util.stream.Stream;
public final class ComputedTypeNullableVersioned implements ComputedTypeNullable, VersionedComputedType {
private final VersionedType baseType;
private ComputedType computedChild;
private final ComputedTypeSupplier computedTypeSupplier;
public ComputedTypeNullableVersioned(VersionedType baseType, ComputedTypeSupplier computedTypeSupplier) {
this.baseType = baseType;
this.computedTypeSupplier = computedTypeSupplier;
}
public VersionedComputedType getBase() {
return (VersionedComputedType) computedTypeSupplier.get(baseType);
}
@Override
public ComputedVersion getVersion() {
return baseType.version();
}
@Override
public ComputedTypeNullableVersioned withChangeAtVersion(ComputedVersion version,
VersionChangeChecker versionChangeChecker) {
return new ComputedTypeNullableVersioned(baseType.withVersion(version),
computedTypeSupplier
);
}
public ComputedType child() {
synchronized (this) {
if (computedChild == null) {
computedChild = computedTypeSupplier.get(baseType);
}
}
if (computedChild instanceof ComputedTypeNullableVersioned) {
throw new IllegalStateException();
} else if (computedChild instanceof ComputedTypeArrayVersioned) {
throw new IllegalStateException();
}
return computedChild;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ComputedTypeNullableVersioned that = (ComputedTypeNullableVersioned) o;
return Objects.equals(baseType, that.baseType);
}
@Override
public int hashCode() {
return baseType != null ? baseType.hashCode() : 0;
}
@Override
public String getName() {
return "-" + baseType.type();
}
@Override
public TypeName getJTypeName(String basePackageName) {
return ClassName.get(baseType.version().getDataNullablesPackage(basePackageName),
"Nullable" + baseType.type());
}
@Override
public ClassName getJSerializerName(String basePackageName) {
return ClassName.get(baseType.version().getSerializersPackage(basePackageName),
"Nullable" + baseType.type() + "Serializer");
}
@Override
public FieldLocation getJSerializerInstance(String basePackageName) {
var className = ClassName.get(baseType.version().getPackage(basePackageName), "Version");
var serializerFieldName = "Nullable" + baseType.type() + "SerializerInstance";
return new FieldLocation(className, serializerFieldName);
}
@Override
public TypeName getJUpgraderName(String basePackageName) {
return ClassName.get(baseType.version().getSerializersPackage(basePackageName),
"Nullable" + baseType.type() + "Upgrader");
}
@Override
public FieldLocation getJUpgraderInstance(String basePackageName) {
var className = ClassName.get(baseType.version().getPackage(basePackageName), "Version");
var upgraderFieldName = "Nullable" + baseType.type() + "UpgraderInstance";
return new FieldLocation(className, upgraderFieldName);
}
@Override
public Stream<ComputedType> getDependencies() {
return Stream.of(child());
}
@Override
public Stream<ComputedType> getDependents() {
return computedTypeSupplier.getDependents(getName());
}
@Override
public String toString() {
return "-" + baseType.type() + " (v" + getVersion() + ")";
}
}

View File

@ -0,0 +1,129 @@
package it.cavallium.data.generator.plugin;
import static it.cavallium.data.generator.plugin.DataModel.joinPackage;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;
import it.cavallium.data.generator.plugin.ComputedType.VersionedComputedType;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
public final class ComputedTypeSuper implements VersionedComputedType {
private final VersionedType type;
private final List<VersionedType> subTypes;
private List<ComputedType> computedSubTypes;
private final ComputedTypeSupplier computedTypeSupplier;
public ComputedTypeSuper(VersionedType type, List<VersionedType> subType, ComputedTypeSupplier computedTypeSupplier) {
this.type = type;
this.subTypes = subType;
this.computedTypeSupplier = computedTypeSupplier;
}
public String getType() {
return type.type();
}
@Override
public ComputedVersion getVersion() {
return type.version();
}
@Override
public it.cavallium.data.generator.plugin.ComputedTypeSuper withChangeAtVersion(ComputedVersion version,
VersionChangeChecker versionChangeChecker) {
return new it.cavallium.data.generator.plugin.ComputedTypeSuper(type.withVersion(version),
subTypes.stream().map(subType -> subType.withVersionIfChanged(version, versionChangeChecker)).toList(),
computedTypeSupplier
);
}
public List<ComputedType> subTypes() {
synchronized (this) {
if (computedSubTypes == null) {
computedSubTypes = new ArrayList<>();
for (VersionedType subType : subTypes) {
computedSubTypes.add(computedTypeSupplier.get(subType));
}
}
}
return computedSubTypes;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
it.cavallium.data.generator.plugin.ComputedTypeSuper that = (it.cavallium.data.generator.plugin.ComputedTypeSuper) o;
if (!Objects.equals(type, that.type)) {
return false;
}
return Objects.equals(subTypes, that.subTypes);
}
@Override
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (subTypes != null ? subTypes.hashCode() : 0);
return result;
}
@Override
public Stream<ComputedType> getDependencies() {
return subTypes().stream();
}
@Override
public Stream<ComputedType> getDependents() {
return computedTypeSupplier.getDependents(getName());
}
@Override
public String getName() {
return type.type();
}
@Override
public TypeName getJTypeName(String basePackageName) {
return ClassName.get(getVersion().getDataPackage(basePackageName), type.type());
}
@Override
public ClassName getJSerializerName(String basePackageName) {
return ClassName.get(type.version().getSerializersPackage(basePackageName), type.type() + "Serializer");
}
@Override
public FieldLocation getJSerializerInstance(String basePackageName) {
var className = ClassName.get(type.version().getPackage(basePackageName), "Version");
var serializerFieldName = type.type() + "SerializerInstance";
return new FieldLocation(className, serializerFieldName);
}
@Override
public TypeName getJUpgraderName(String basePackageName) {
return ClassName.get(type.version().getSerializersPackage(basePackageName), type.type() + "Upgrader");
}
@Override
public FieldLocation getJUpgraderInstance(String basePackageName) {
var className = ClassName.get(type.version().getPackage(basePackageName), "Version");
var upgraderFieldName = type.type() + "UpgraderInstance";
return new FieldLocation(className, upgraderFieldName);
}
@Override
public String toString() {
return type.type() + " (super, v" + getVersion() + ")";
}
}

View File

@ -2,6 +2,7 @@ package it.cavallium.data.generator.plugin;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -12,31 +13,45 @@ public class ComputedTypeSupplier {
private final Int2ObjectMap<Map<String, ComputedType>> computedTypeMap; private final Int2ObjectMap<Map<String, ComputedType>> computedTypeMap;
private final Int2ObjectMap<Map<String, List<ComputedType>>> computedTypeDependentsCacheMap = new Int2ObjectOpenHashMap<>(); private final Int2ObjectMap<Map<String, List<ComputedType>>> computedTypeDependentsCacheMap = new Int2ObjectOpenHashMap<>();
private final ComputedVersion currentVersion;
public ComputedTypeSupplier(Int2ObjectMap<Map<String, ComputedType>> computedTypeMap) { public ComputedTypeSupplier(Int2ObjectMap<Map<String, ComputedType>> computedTypeMap, ComputedVersion currentVersion) {
this.computedTypeMap = computedTypeMap; this.computedTypeMap = computedTypeMap;
this.currentVersion = currentVersion;
} }
public ComputedType get(VersionedType type) { public ComputedType get(VersionedType type) {
var computedType = computedTypeMap.get(type.version()).get(type.type()); var computedType = computedTypeMap.get(type.version().getVersion()).get(type.type());
if (computedType == null) { if (computedType == null) {
throw new IllegalStateException("Type " + type + " does not exist"); throw new IllegalStateException("Type " + type + " does not exist");
} }
return computedType; return computedType;
} }
public ComputedType get(String type) {
return get(new VersionedType(type, currentVersion));
}
public Stream<ComputedType> getDependencies(VersionedType type) { public Stream<ComputedType> getDependencies(VersionedType type) {
return computedTypeMap.get(type.version()).get(type.type()).getDependencies(); return computedTypeMap.get(type.version().getVersion()).get(type.type()).getDependencies();
} }
public Stream<ComputedType> getDependents(VersionedType type) { public Stream<ComputedType> getDependents(VersionedType type) {
synchronized (computedTypeDependentsCacheMap) { synchronized (computedTypeDependentsCacheMap) {
return computedTypeDependentsCacheMap return computedTypeDependentsCacheMap
.computeIfAbsent(type.version(), x -> new HashMap<>()) .computeIfAbsent(type.version().getVersion(), x -> new HashMap<>())
.computeIfAbsent(type.type(), .computeIfAbsent(type.type(),
typeName -> computedTypeMap.get(type.version()).values().stream().filter(computedType -> typeName -> Objects.requireNonNull(computedTypeMap.get(type.version().getVersion()), () -> "Version " + type.version() + " does not exist")
computedType.getDependencies().anyMatch(y -> Objects.equals(y.getName(), typeName))).toList()) .values().stream().filter(computedType ->
computedType.getDependencies().anyMatch(y -> Objects.equals(y.getName(), typeName))).toList())
.stream(); .stream();
} }
} }
/**
* Get dependents from the current version
*/
public Stream<ComputedType> getDependents(String type) {
return getDependents(new VersionedType(type, currentVersion));
}
} }

View File

@ -1,25 +1,27 @@
package it.cavallium.data.generator.plugin; package it.cavallium.data.generator.plugin;
import static it.cavallium.data.generator.plugin.DataModel.joinPackage;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.StringJoiner;
import org.jetbrains.annotations.NotNull;
public class ComputedVersion { public class ComputedVersion implements Comparable<ComputedVersion> {
private final String name; private final String name;
private final Map<String, ParsedClass> classMap;
private final int version; private final int version;
private final boolean current; private final boolean current;
public DetailsConfiguration details; public DetailsConfiguration details;
public List<VersionTransformation> transformations; public List<VersionTransformation> transformations;
public ComputedVersion(ParsedVersion value, int version, boolean current, String versionName, Map<String, ParsedClass> classMap) { public ComputedVersion(ParsedVersion value, int version, boolean current, String versionName) {
this.details = value.details; this.details = value.details;
this.transformations = value.transformations; this.transformations = value.transformations;
this.name = versionName;
this.version = version; this.version = version;
this.current = current; this.current = current;
this.name = versionName;
this.classMap = classMap;
} }
public int getVersion() { public int getVersion() {
@ -30,10 +32,6 @@ public class ComputedVersion {
return name; return name;
} }
public Map<String, ParsedClass> getClassMap() {
return classMap;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) {
@ -43,46 +41,65 @@ public class ComputedVersion {
return false; return false;
} }
ComputedVersion that = (ComputedVersion) o; ComputedVersion that = (ComputedVersion) o;
return Objects.equals(details, that.details) return Objects.equals(version, that.version);
&& Objects.equals(transformations, that.transformations);
} }
@Override @Override
public int hashCode() { public int hashCode() {
int hash = 0; return ConfigUtils.hashCode(version);
hash += ConfigUtils.hashCode(details);
hash += ConfigUtils.hashCode(transformations);
return hash;
} }
public String getPackage(String basePackageName) { public String getPackage(String basePackageName) {
if (current) { if (current) {
return joinPackage(basePackageName, "current"); return joinPackage(basePackageName, "current");
} else { } else {
return joinPackage(basePackageName, "v" + getVersionCompleteInt()); return joinPackage(basePackageName, "v" + version);
} }
} }
public String getDataPackage(String basePackageName) {
return joinPackage(getPackage(basePackageName), "data");
}
public String getUpgradersPackage(String basePackageName) {
return joinPackage(getPackage(basePackageName), "upgraders");
}
public String getSerializersPackage(String basePackageName) {
return joinPackage(getPackage(basePackageName), "serializers");
}
public String getDataNullablesPackage(String basePackageName) {
return joinPackage(getDataPackage(basePackageName), "nullables");
}
public String getVersionVarName() { public String getVersionVarName() {
return "V" + version; return "V" + version;
} }
private String getVersionCompleteInt() { public String getVersionShortInt() {
return Integer.toString(version); return Integer.toString(version);
} }
private String joinPackage(String basePackageName, String packageName) {
if (basePackageName.isBlank()) {
basePackageName = "org.generated";
}
if (packageName.isBlank()) {
return basePackageName;
} else {
return basePackageName + "." + packageName;
}
}
public boolean isCurrent() { public boolean isCurrent() {
return current; return current;
} }
@Override
public int compareTo(@NotNull ComputedVersion o) {
return Integer.compare(version, o.version);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(version);
sb.append(" (");
sb.append(name);
if (current) {
sb.append(", current");
}
sb.append(")");
return sb.toString();
}
} }

View File

@ -14,23 +14,6 @@ public final class CustomTypesConfiguration {
this.javaClass = javaClass; this.javaClass = javaClass;
} }
public TypeName getJavaClassType() {
int indexOfGeneric;
if ((indexOfGeneric = javaClass.indexOf("<")) == -1) {
return ClassName.bestGuess(javaClass.trim());
} else {
var rawTypesArray = javaClass.substring(indexOfGeneric + 1, javaClass.length() - 1).split(",");
var genericsResult = new TypeName[rawTypesArray.length];
int i = 0;
for (String rawType : rawTypesArray) {
genericsResult[i] = ClassName.bestGuess(rawType.trim());
i++;
}
var base = ClassName.bestGuess(javaClass.substring(0, indexOfGeneric).trim());
return ParameterizedTypeName.get(base, genericsResult);
}
}
public String getJavaClassString() { public String getJavaClassString() {
return javaClass; return javaClass;
} }

View File

@ -3,20 +3,14 @@ package it.cavallium.data.generator.plugin;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static java.util.function.Function.identity; import static java.util.function.Function.identity;
import it.cavallium.data.generator.plugin.ComputedType.ComputedArrayType;
import it.cavallium.data.generator.plugin.ComputedType.ComputedBaseType;
import it.cavallium.data.generator.plugin.ComputedType.ComputedCustomType;
import it.cavallium.data.generator.plugin.ComputedType.ComputedNativeType;
import it.cavallium.data.generator.plugin.ComputedType.ComputedNullableType;
import it.cavallium.data.generator.plugin.ComputedType.ComputedSuperType;
import it.cavallium.data.generator.plugin.ComputedType.VersionedComputedType; import it.cavallium.data.generator.plugin.ComputedType.VersionedComputedType;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectCollection; import it.unimi.dsi.fastutil.objects.ObjectCollection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -30,6 +24,7 @@ import java.util.concurrent.atomic.LongAdder;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collector; import java.util.stream.Collector;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -47,7 +42,7 @@ public class DataModel {
"Int52" "Int52"
); );
private final int currentVersion; private final ComputedVersion currentVersion;
private final int hash; private final int hash;
private final Map<String, ParsedInterface> interfacesData; private final Map<String, ParsedInterface> interfacesData;
private final Int2ObjectMap<ComputedVersion> versions; private final Int2ObjectMap<ComputedVersion> versions;
@ -107,7 +102,6 @@ public class DataModel {
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)); .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
// Build versions sequence // Build versions sequence
List<String> rawVersionsSequence = new ArrayList<>();
int versionsCount = 0; int versionsCount = 0;
Int2ObjectMap<String> versionToName = new Int2ObjectLinkedOpenHashMap<>(); Int2ObjectMap<String> versionToName = new Int2ObjectLinkedOpenHashMap<>();
Object2IntMap<String> nameToVersion = new Object2IntOpenHashMap<>(); Object2IntMap<String> nameToVersion = new Object2IntOpenHashMap<>();
@ -115,7 +109,6 @@ public class DataModel {
String lastVersion = null; String lastVersion = null;
String nextVersion = rootVersion; String nextVersion = rootVersion;
while (nextVersion != null) { while (nextVersion != null) {
rawVersionsSequence.add(nextVersion);
lastVersion = nextVersion; lastVersion = nextVersion;
versionToName.put(versionsCount, nextVersion); versionToName.put(versionsCount, nextVersion);
nameToVersion.put(nextVersion, versionsCount); nameToVersion.put(nextVersion, versionsCount);
@ -157,8 +150,8 @@ public class DataModel {
}); });
// Compute the numeric versions map // Compute the numeric versions map
Int2ObjectMap<ParsedVersion> versions = new Int2ObjectLinkedOpenHashMap<>(); Int2ObjectMap<ParsedVersion> parsedVersions = new Int2ObjectLinkedOpenHashMap<>();
rawVersions.forEach((k, v) -> versions.put(nameToVersion.getInt(k), new ParsedVersion(v))); rawVersions.forEach((k, v) -> parsedVersions.put(nameToVersion.getInt(k), new ParsedVersion(v)));
Int2ObjectMap<Map<String, ParsedClass>> computedClassConfig = new Int2ObjectLinkedOpenHashMap<>(); Int2ObjectMap<Map<String, ParsedClass>> computedClassConfig = new Int2ObjectLinkedOpenHashMap<>();
for (int versionIndex = 0; versionIndex < versionsCount; versionIndex++) { for (int versionIndex = 0; versionIndex < versionsCount; versionIndex++) {
@ -168,7 +161,7 @@ public class DataModel {
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)) .collect(Collectors.toMap(Entry::getKey, Entry::getValue))
); );
} else { } else {
var version = versions.get(versionIndex); var version = parsedVersions.get(versionIndex);
Map<String, ParsedClass> prevVersionConfiguration Map<String, ParsedClass> prevVersionConfiguration
= requireNonNull(computedClassConfig.get(versionIndex - 1)); = requireNonNull(computedClassConfig.get(versionIndex - 1));
Map<String, ParsedClass> newVersionConfiguration = prevVersionConfiguration.entrySet().stream() Map<String, ParsedClass> newVersionConfiguration = prevVersionConfiguration.entrySet().stream()
@ -194,7 +187,7 @@ public class DataModel {
throw new IllegalArgumentException(transformCoordinate + " refers to an unknown type: " throw new IllegalArgumentException(transformCoordinate + " refers to an unknown type: "
+ t.transformClass); + t.transformClass);
} }
transformClass.changed = true; transformClass.differentThanPrev = true;
var definition = removeAndGetIndex(transformClass.data, t.from); var definition = removeAndGetIndex(transformClass.data, t.from);
if (definition.isEmpty()) { if (definition.isEmpty()) {
throw new IllegalArgumentException(transformCoordinate + " refers to an unknown field: " + t.from); throw new IllegalArgumentException(transformCoordinate + " refers to an unknown field: " + t.from);
@ -217,7 +210,7 @@ public class DataModel {
throw new IllegalArgumentException(transformCoordinate + " refers to an unknown type: " throw new IllegalArgumentException(transformCoordinate + " refers to an unknown type: "
+ t.transformClass); + t.transformClass);
} }
transformClass.changed = true; transformClass.differentThanPrev = true;
if (!allTypes.contains(extractTypeName(t.type))) { if (!allTypes.contains(extractTypeName(t.type))) {
throw new IllegalArgumentException(transformCoordinate + " refers to an unknown type: " + t.type); throw new IllegalArgumentException(transformCoordinate + " refers to an unknown type: " + t.type);
} }
@ -240,7 +233,7 @@ public class DataModel {
throw new IllegalArgumentException(transformCoordinate + " refers to an unknown type: " throw new IllegalArgumentException(transformCoordinate + " refers to an unknown type: "
+ t.transformClass); + t.transformClass);
} }
transformClass.changed = true; transformClass.differentThanPrev = true;
var prevDef = transformClass.data.remove(t.from); var prevDef = transformClass.data.remove(t.from);
if (prevDef == null) { if (prevDef == null) {
throw new IllegalArgumentException(transformCoordinate + " tries to remove the nonexistent field \"" throw new IllegalArgumentException(transformCoordinate + " tries to remove the nonexistent field \""
@ -254,7 +247,7 @@ public class DataModel {
throw new IllegalArgumentException(transformCoordinate + " refers to an unknown type: " throw new IllegalArgumentException(transformCoordinate + " refers to an unknown type: "
+ t.transformClass); + t.transformClass);
} }
transformClass.changed = true; transformClass.differentThanPrev = true;
if (!allTypes.contains(extractTypeName(t.type))) { if (!allTypes.contains(extractTypeName(t.type))) {
throw new IllegalArgumentException(transformCoordinate + " refers to an unknown type: " + t.type); throw new IllegalArgumentException(transformCoordinate + " refers to an unknown type: " + t.type);
} }
@ -271,84 +264,141 @@ public class DataModel {
} }
} }
// Compute the versions
var computedVersions = parsedVersions
.int2ObjectEntrySet()
.stream()
.collect(Collectors.toMap(Int2ObjectMap.Entry::getIntKey,
e -> new ComputedVersion(e.getValue(),
e.getIntKey(),
e.getIntKey() == latestVersion,
versionToName.get(e.getIntKey())
),
(a, b) -> {
throw new IllegalStateException();
},
Int2ObjectLinkedOpenHashMap::new
));
// Compute the types // Compute the types
Int2ObjectMap<Map<String, ComputedType>> computedTypes = new Int2ObjectLinkedOpenHashMap<>(); Int2ObjectMap<Map<String, ComputedType>> computedTypes = new Int2ObjectLinkedOpenHashMap<>();
ComputedTypeSupplier computedTypeSupplier = new ComputedTypeSupplier(computedTypes); Int2ObjectMap<Map<String, ComputedType>> randomComputedTypes = new Int2ObjectOpenHashMap<>();
for (int versionIndex = 0; versionIndex < versionsCount; versionIndex++) { ComputedTypeSupplier computedTypeSupplier = new ComputedTypeSupplier(randomComputedTypes, computedVersions.get(latestVersion));
int versionIndexF = versionIndex; {
if (versionIndexF == 0) { for (int versionNumber = latestVersion - 1; versionNumber >= 0; versionNumber--) {
// Compute base types var version = computedClassConfig.get(versionNumber);
List<ComputedType> versionBaseTypes = computedClassConfig.get(versionIndexF).entrySet().stream() computedClassConfig.get(versionNumber + 1).forEach((type, typeConfig) -> {
.map(e -> { if (typeConfig.differentThanPrev) {
var data = new LinkedHashMap<String, VersionedType>(); version.get(type).differentThanNext = true;
e.getValue().getData().forEach((key, value) -> data.put(key, new VersionedType(value, versionIndexF)));
return new ComputedBaseType(new VersionedType(e.getKey(), versionIndexF),
e.getValue().stringRepresenter, data, computedTypeSupplier);
}).collect(Collectors.toList());
// Compute custom types
customTypesData.forEach((name, data) -> versionBaseTypes.add(new ComputedCustomType(name,
data.getJavaClassString(), data.serializer, computedTypeSupplier)));
// Compute super types
superTypesData.forEach((key, data) -> {
List<VersionedType> subTypes = data.stream().map(x -> new VersionedType(x, versionIndexF)).toList();
versionBaseTypes.add(new ComputedSuperType(new VersionedType(key, versionIndexF), subTypes, computedTypeSupplier));
});
// Compute nullable types
computedClassConfig.values().stream()
.flatMap(x -> x.values().stream())
.flatMap(x -> x.getData().values().stream())
.filter(x -> x.startsWith("-"))
.map(nullableName -> new VersionedType(nullableName.substring(1), versionIndexF))
.map(baseType -> new ComputedNullableType(baseType, computedTypeSupplier))
.forEach(versionBaseTypes::add);
// Compute array types
computedClassConfig.values().stream()
.flatMap(x -> x.values().stream())
.flatMap(x -> x.getData().values().stream())
.filter(x -> x.startsWith("§"))
.map(nullableName -> new VersionedType(nullableName.substring(1), versionIndexF))
.map(baseType -> new ComputedArrayType(baseType, computedTypeSupplier))
.forEach(versionBaseTypes::add);
// Compute native types
versionBaseTypes.addAll(ComputedNativeType.get(computedTypeSupplier));
computedTypes.put(versionIndexF,
versionBaseTypes.stream().distinct().collect(Collectors.toMap(ComputedType::getName, identity())));
} else {
Set<String> changedTypes = computedTypes.get(versionIndexF - 1).values().stream()
.filter(prevType -> prevType instanceof ComputedBaseType prevBaseType
&& computedClassConfig.get(versionIndexF).get(prevBaseType.getName()).changed)
.map(ComputedType::getName)
.collect(Collectors.toSet());
{
boolean addedMoreTypes;
do {
var newChangedTypes = changedTypes
.parallelStream()
.flatMap(changedType -> computedTypes.get(versionIndexF - 1).get(changedType).getDependents())
.map(ComputedType::getName)
.distinct()
.toList();
addedMoreTypes = changedTypes.addAll(newChangedTypes);
} while (addedMoreTypes);
}
Map<String, ComputedType> currentVersionComputedTypes = new HashMap<>();
var versionChangeChecker = new VersionChangeChecker(changedTypes);
computedTypes.get(versionIndexF - 1).forEach((name, type) -> {
if (!changedTypes.contains(name)) {
currentVersionComputedTypes.put(name, type);
} else {
if (type instanceof VersionedComputedType versionedComputedType) {
ComputedType newType = versionedComputedType.withChangeAtVersion(versionIndexF, versionChangeChecker);
currentVersionComputedTypes.put(name, newType);
} else {
throw new IllegalStateException();
}
} }
}); });
computedTypes.put(versionIndexF, currentVersionComputedTypes); }
for (int versionIndex = latestVersion; versionIndex >= 0; versionIndex--) {
int versionIndexF = versionIndex;
var version = computedVersions.get(versionIndexF);
if (versionIndexF == latestVersion) {
// Compute base types
List<ComputedType> versionBaseTypes = computedClassConfig.get(versionIndexF).entrySet().stream()
.map(e -> {
var data = new LinkedHashMap<String, VersionedType>();
e.getValue().getData().forEach((key, value) -> data.put(key, new VersionedType(value, version)));
return new ComputedTypeBase(new VersionedType(e.getKey(), version),
e.getValue().stringRepresenter, data, computedTypeSupplier);
}).collect(Collectors.toList());
// Compute custom types
customTypesData.forEach((name, data) -> versionBaseTypes.add(new ComputedTypeCustom(name,
data.getJavaClassString(), data.serializer, computedTypeSupplier, computedVersions.get(latestVersion))));
// Compute super types
superTypesData.forEach((key, data) -> {
List<VersionedType> subTypes = data.stream().map(x -> new VersionedType(x, version)).toList();
versionBaseTypes.add(new ComputedTypeSuper(new VersionedType(key, version), subTypes, computedTypeSupplier));
});
// Compute nullable types
{
var nullableRawTypes = computedClassConfig.values().stream()
.flatMap(x -> x.values().stream())
.flatMap(x -> x.getData().values().stream())
.filter(x -> x.startsWith("-"))
.map(nullableName -> nullableName.substring(1))
.toList();
// Compute nullable base types
nullableRawTypes.stream()
.filter(nullableName -> !NATIVE_TYPES.contains(nullableName))
.map(nullableName -> new VersionedType(nullableName, version))
.map(baseType -> new ComputedTypeNullableVersioned(baseType, computedTypeSupplier))
.forEach(versionBaseTypes::add);
// Compute nullable native types
nullableRawTypes.stream()
.filter(NATIVE_TYPES::contains)
.map(baseType ->
new ComputedTypeNullableNative(baseType, computedVersions.get(latestVersion), computedTypeSupplier))
.forEach(versionBaseTypes::add);
}
// Compute array types
{
var arrayRawTypes = computedClassConfig.values().stream()
.flatMap(x -> x.values().stream())
.flatMap(x -> x.getData().values().stream())
.filter(x -> x.startsWith("§"))
.map(nullableName -> nullableName.substring(1))
.toList();
// Compute array base types
arrayRawTypes.stream()
.filter(nullableName -> !NATIVE_TYPES.contains(nullableName))
.map(nullableName -> new VersionedType(nullableName, version))
.map(baseType -> new ComputedTypeArrayVersioned(baseType, computedTypeSupplier))
.forEach(versionBaseTypes::add);
// Compute array native types
arrayRawTypes.stream()
.filter(NATIVE_TYPES::contains)
.map(baseType ->
new ComputedTypeArrayNative(baseType, computedTypeSupplier))
.forEach(versionBaseTypes::add);
}
// Compute native types
versionBaseTypes.addAll(ComputedTypeNative.get(computedTypeSupplier));
randomComputedTypes.put(versionIndexF,
versionBaseTypes.stream().distinct().collect(Collectors.toMap(ComputedType::getName, identity())));
} else {
Set<String> changedTypes = randomComputedTypes.get(versionIndexF + 1).values().stream()
.filter(prevType -> prevType instanceof ComputedTypeBase prevBaseType
&& computedClassConfig.get(versionIndexF).get(prevBaseType.getName()).differentThanNext)
.map(ComputedType::getName)
.collect(Collectors.toSet());
{
boolean addedMoreTypes;
do {
var newChangedTypes = changedTypes
.parallelStream()
.flatMap(changedType -> randomComputedTypes.get(versionIndexF + 1).get(changedType).getDependents())
.map(ComputedType::getName)
.distinct()
.toList();
addedMoreTypes = changedTypes.addAll(newChangedTypes);
} while (addedMoreTypes);
}
Map<String, ComputedType> currentVersionComputedTypes = new HashMap<>();
var versionChangeChecker = new VersionChangeChecker(changedTypes, versionIndexF, latestVersion);
randomComputedTypes.get(versionIndexF + 1).forEach((name, type) -> {
if (!changedTypes.contains(name)) {
currentVersionComputedTypes.put(name, type);
} else {
if (type instanceof VersionedComputedType versionedComputedType) {
ComputedType newType = versionedComputedType.withChangeAtVersion(version, versionChangeChecker);
currentVersionComputedTypes.put(name, newType);
} else {
throw new IllegalStateException();
}
}
});
randomComputedTypes.put(versionIndexF, currentVersionComputedTypes);
}
}
for (int i = 0; i < versionsCount; i++) {
computedTypes.put(i, Objects.requireNonNull(randomComputedTypes.get(i)));
} }
} }
// All types, including arrays, nullables, primitives, etc // All types, including arrays, nullables, primitives, etc
@ -366,7 +416,7 @@ public class DataModel {
.collect(Collectors.toMap(Entry::getKey, e -> e .collect(Collectors.toMap(Entry::getKey, e -> e
.getValue() .getValue()
.stream() .stream()
.sorted(Comparator.comparingInt(VersionedComputedType::getVersion)) .sorted(Comparator.comparingInt(x -> x.getVersion().getVersion()))
.map(x -> new VersionedType(e.getKey(), x.getVersion())) .map(x -> new VersionedType(e.getKey(), x.getVersion()))
.toList())); .toList()));
versionedTypeVersions.forEach((type, versionsList) -> { versionedTypeVersions.forEach((type, versionsList) -> {
@ -434,43 +484,31 @@ public class DataModel {
|__BaseGroupId v1 |__BaseGroupId v1
*/ */
this.versions = computedVersions;
this.interfacesData = interfacesData.entrySet().stream() this.interfacesData = interfacesData.entrySet().stream()
.map(e -> Map.entry(e.getKey(), new ParsedInterface(e.getValue()))) .map(e -> Map.entry(e.getKey(), new ParsedInterface(e.getValue())))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)); .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
this.versions = versions
.int2ObjectEntrySet()
.stream()
.collect(Collectors.toMap(Int2ObjectMap.Entry::getIntKey,
e -> new ComputedVersion(e.getValue(),
e.getIntKey(),
e.getIntKey() == latestVersion,
versionToName.get(e.getIntKey()),
computedClassConfig.get(e.getIntKey())
),
(a, b) -> {
throw new IllegalStateException();
},
Int2ObjectLinkedOpenHashMap::new
));
LongAdder unchangedTot = new LongAdder(); LongAdder unchangedTot = new LongAdder();
LongAdder changedTot = new LongAdder(); LongAdder changedTot = new LongAdder();
computedTypes.forEach((version, types) -> { computedTypes.forEach((version, types) -> {
System.out.println("Version: " + version); System.out.println("Version: " + version);
System.out.println("\tTypes: " + types.size()); System.out.println("\tTypes: " + types.size());
System.out.println("\tVersioned types: " + types.values().stream().filter(t -> (t instanceof VersionedComputedType)).count()); System.out.println("\tVersioned types: " + types.values().stream().filter(t -> (t instanceof VersionedComputedType)).count());
var unchanged = types.values().stream().filter(t -> (t instanceof VersionedComputedType versionedComputedType && versionedComputedType.getVersion() < version)).count(); var unchanged = types.values().stream().filter(t -> (t instanceof VersionedComputedType versionedComputedType
var changed = types.values().stream().filter(t -> (t instanceof VersionedComputedType versionedComputedType && versionedComputedType.getVersion() == version)).count(); && versionedComputedType.getVersion().getVersion() != version)).count();
var changed = types.values().stream().filter(t -> (t instanceof VersionedComputedType versionedComputedType
&& versionedComputedType.getVersion().getVersion() == version)).count();
unchangedTot.add(unchanged); unchangedTot.add(unchanged);
changedTot.add(changed); changedTot.add(changed);
System.out.println("\t\tUnchanged: " + unchanged + " (" + (unchanged * 100 / (changed + unchanged)) + "%)"); System.out.println("\t\tUnchanged: " + unchanged + " (" + (unchanged * 100 / Math.max(changed + unchanged, 1)) + "%)");
System.out.println("\t\tChanged: " + changed + " (" + (changed * 100 / (changed + unchanged)) + "%)"); System.out.println("\t\tChanged: " + changed + " (" + (changed * 100 / Math.max(changed + unchanged, 1)) + "%)");
}); });
System.out.println("Result:"); System.out.println("Result:");
var unchanged = unchangedTot.sum(); var unchanged = unchangedTot.sum();
var changed = changedTot.sum(); var changed = changedTot.sum();
System.out.println("\tAvoided type versions: " + unchanged + " (" + (unchanged * 100 / (changed + unchanged)) + "%)"); System.out.println("\tAvoided type versions: " + unchanged + " (" + (unchanged * 100 / (changed + unchanged)) + "%)");
System.out.println("\tType versions: " + changed + " (" + (changed * 100 / (changed + unchanged)) + "%)"); System.out.println("\tType versions: " + changed + " (" + (changed * 100 / (changed + unchanged)) + "%)");
this.currentVersion = versionsCount - 1; this.currentVersion = computedVersions.get(versionsCount - 1);
this.superTypes = superTypesData; this.superTypes = superTypesData;
this.customTypes = customTypesData; this.customTypes = customTypesData;
this.computedTypes = computedTypes; this.computedTypes = computedTypes;
@ -604,17 +642,36 @@ public class DataModel {
} }
public int getCurrentVersionNumber() { public int getCurrentVersionNumber() {
return currentVersion; return currentVersion.getVersion();
} }
public ComputedVersion getCurrentVersion() { public ComputedVersion getCurrentVersion() {
return versions.get(currentVersion); return currentVersion;
} }
public Map<String, Set<String>> getSuperTypes() { @Deprecated
public Map<String, Set<String>> getSuperTypesRaw() {
return this.superTypes; return this.superTypes;
} }
public Stream<ComputedTypeSuper> getSuperTypesComputed() {
return getSuperTypesComputed(currentVersion);
}
public Stream<ComputedTypeSuper> getSuperTypesComputed(ComputedVersion version) {
return this.computedTypes.get(version.getVersion()).values().stream()
.filter(t -> t instanceof ComputedTypeSuper).map(t -> (ComputedTypeSuper) t);
}
public Stream<ComputedTypeBase> getBaseTypesComputed() {
return getBaseTypesComputed(currentVersion);
}
public Stream<ComputedTypeBase> getBaseTypesComputed(ComputedVersion version) {
return this.computedTypes.get(version.getVersion()).values().stream()
.filter(t -> t instanceof ComputedTypeBase).map(t -> (ComputedTypeBase) 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);
@ -628,6 +685,7 @@ public class DataModel {
return interfacesData; return interfacesData;
} }
@Deprecated
public Map<String, CustomTypesConfiguration> getCustomTypes() { public Map<String, CustomTypesConfiguration> getCustomTypes() {
return customTypes; return customTypes;
} }
@ -636,6 +694,10 @@ public class DataModel {
return computedTypes; return computedTypes;
} }
public Map<String, ComputedType> getComputedTypes(ComputedVersion version) {
return computedTypes.get(version.getVersion());
}
public VersionedType getNextVersion(VersionedType type) { public VersionedType getNextVersion(VersionedType type) {
return versionedTypeNextVersion.get(type); return versionedTypeNextVersion.get(type);
} }
@ -644,27 +706,134 @@ public class DataModel {
return versionedTypePrevVersion.get(type); return versionedTypePrevVersion.get(type);
} }
public ComputedType getNextVersion(ComputedType type) { public <T extends ComputedType> T getNextVersion(T type) {
if (type instanceof VersionedComputedType versionedComputedType) { if (type instanceof VersionedComputedType versionedComputedType) {
var result = versionedTypeNextVersion.get(new VersionedType(versionedComputedType.getName(), versionedComputedType.getVersion())); var result = versionedTypeNextVersion.get(new VersionedType(versionedComputedType.getName(), versionedComputedType.getVersion()));
if (result == null) { if (result == null) {
return null; return null;
} }
return this.computedTypes.get(result.version()).get(result.type()); //noinspection unchecked
return (T) this.computedTypes.get(result.version().getVersion()).get(result.type());
} else { } else {
return null; return null;
} }
} }
public ComputedType getPrevVersion(ComputedType type) { public <T extends ComputedType> T getPrevVersion(T type) {
if (type instanceof VersionedComputedType versionedComputedType) { if (type instanceof VersionedComputedType versionedComputedType) {
var result = versionedTypePrevVersion.get(new VersionedType(versionedComputedType.getName(), versionedComputedType.getVersion())); var result = versionedTypePrevVersion.get(new VersionedType(versionedComputedType.getName(), versionedComputedType.getVersion()));
if (result == null) { if (result == null) {
return null; return null;
} }
return this.computedTypes.get(result.version()).get(result.type()); //noinspection unchecked
return (T) this.computedTypes.get(result.version().getVersion()).get(result.type());
} else { } else {
return null; return null;
} }
} }
public boolean isTypeForVersion(ComputedVersion versionConfiguration, String key) {
var type = getComputedTypes(versionConfiguration).get(key);
return type instanceof VersionedComputedType versionedComputedType
&& versionedComputedType.getVersion().getVersion() == versionConfiguration.getVersion();
}
public ComputedVersion getTypeFirstSameVersion(VersionedComputedType type) {
var prevVersion = getPrevVersion(type);
if (prevVersion != null) {
return versions.get(prevVersion.getVersion().getVersion() + 1);
} else {
return type.getVersion();
}
}
public Stream<ComputedVersion> getTypeSameVersions(VersionedComputedType type) {
var initialVersion = getTypeFirstSameVersion(type);
var lastVersion = type.getVersion();
return getVersionRange(initialVersion, lastVersion);
}
@Deprecated
public ComputedVersion getVersion(int version) {
return Objects.requireNonNull(versions.get(version));
}
public ComputedVersion getVersion(VersionedComputedType versionedComputedType) {
return Objects.requireNonNull(versions.get(versionedComputedType.getVersion().getVersion()));
}
public Stream<ComputedVersion> getVersionRange(ComputedVersion initialVersionInclusive,
ComputedVersion lastVersionInclusive) {
if (initialVersionInclusive.getVersion() > lastVersionInclusive.getVersion()) {
throw new IllegalArgumentException();
}
return IntStream
.rangeClosed(initialVersionInclusive.getVersion(), lastVersionInclusive.getVersion())
.mapToObj(versions::get);
}
public String getVersionPackage(ComputedVersion version, String basePackageName) {
return version.getPackage(basePackageName);
}
public String getVersionDataPackage(ComputedVersion version, String basePackageName) {
return version.getDataPackage(basePackageName);
}
@Deprecated
public String getVersionDataPackage(VersionedComputedType type, String basePackageName) {
return type.getVersion().getDataPackage(basePackageName);
}
public String getRootPackage(String basePackageName) {
return joinPackage(basePackageName, "");
}
public static String joinPackage(String basePackageName, String packageName) {
if (basePackageName.isBlank()) {
basePackageName = "org.generated";
}
if (packageName.isBlank()) {
return basePackageName;
} else {
return basePackageName + "." + packageName;
}
}
public Stream<ComputedTypeSuper> getSuperTypesOf(VersionedComputedType baseType) {
return getSuperTypesComputed(baseType.getVersion()).filter(type -> type.subTypes().contains(baseType));
}
public Stream<ComputedTypeSuper> getExtendsInterfaces(ComputedTypeSuper superType) {
if (superType.getVersion().isCurrent()) {
var interfaces = interfacesData.get(superType.getName());
if (interfaces != null) {
return interfaces.extendInterfaces.stream()
.map(name -> (ComputedTypeSuper) this.computedTypes.get(currentVersion.getVersion()).get(name));
}
}
return Stream.of();
}
public Stream<Entry<String, ComputedType>> getCommonInterfaceGetters(ComputedTypeSuper superType) {
if (superType.getVersion().isCurrent()) {
var interfaces = interfacesData.get(superType.getName());
if (interfaces != null) {
return interfaces.commonGetters.entrySet().stream().map(x ->
Map.entry(x.getKey(), this.computedTypes.get(currentVersion.getVersion()).get(x.getValue())));
}
}
return Stream.of();
}
public Stream<Entry<String, ComputedType>> getCommonInterfaceData(ComputedTypeSuper superType) {
if (superType.getVersion().isCurrent()) {
var interfaces = interfacesData.get(superType.getName());
if (interfaces != null) {
return interfaces.commonData.entrySet().stream().map(x ->
Map.entry(x.getKey(), this.computedTypes.get(currentVersion.getVersion()).get(x.getValue())));
}
}
return Stream.of();
}
} }

View File

@ -0,0 +1,5 @@
package it.cavallium.data.generator.plugin;
import com.squareup.javapoet.TypeName;
public record FieldLocation(TypeName className, String fieldName) {}

View File

@ -20,6 +20,9 @@ public class MavenPlugin extends AbstractMojo {
@Parameter( required = true) @Parameter( required = true)
private String basePackageName; private String basePackageName;
@Parameter( required = true, defaultValue = "true")
private String deepCheckBeforeCreatingNewEqualInstances;
@Parameter( required = true, defaultValue = "false") @Parameter( required = true, defaultValue = "false")
private String useRecordBuilder; private String useRecordBuilder;
@ -39,7 +42,7 @@ public class MavenPlugin extends AbstractMojo {
Path outPath = genRecordsPath.resolve("java"); Path outPath = genRecordsPath.resolve("java");
this.project.addCompileSourceRoot(outPath.toString()); this.project.addCompileSourceRoot(outPath.toString());
sourcesGenerator.generateSources(basePackageName, outPath, Boolean.parseBoolean(useRecordBuilder)); sourcesGenerator.generateSources(basePackageName, outPath, Boolean.parseBoolean(useRecordBuilder), false, Boolean.parseBoolean(deepCheckBeforeCreatingNewEqualInstances));
} catch (IOException e) { } catch (IOException e) {
throw new MojoExecutionException("Exception while generating classes", e); throw new MojoExecutionException("Exception while generating classes", e);
} }

View File

@ -2,7 +2,6 @@ package it.cavallium.data.generator.plugin;
import static it.cavallium.data.generator.plugin.DataModel.fixType; import static it.cavallium.data.generator.plugin.DataModel.fixType;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -14,7 +13,8 @@ public final class ParsedClass {
public String stringRepresenter; public String stringRepresenter;
public LinkedHashMap<String, String> data; public LinkedHashMap<String, String> data;
public boolean changed; public boolean differentThanPrev;
public boolean differentThanNext;
public ParsedClass(ClassConfiguration baseTypesData) { public ParsedClass(ClassConfiguration baseTypesData) {
this.stringRepresenter = baseTypesData.stringRepresenter; this.stringRepresenter = baseTypesData.stringRepresenter;

View File

@ -7,6 +7,6 @@ public class Standalone {
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
SourcesGenerator sourcesGenerator = SourcesGenerator.load(Paths.get(args[0])); SourcesGenerator sourcesGenerator = SourcesGenerator.load(Paths.get(args[0]));
sourcesGenerator.generateSources(args[1], Paths.get(args[2]), Boolean.parseBoolean(args[3])); sourcesGenerator.generateSources(args[1], Paths.get(args[2]), Boolean.parseBoolean(args[3]), false, true);
} }
} }

View File

@ -5,12 +5,24 @@ import java.util.Set;
public class VersionChangeChecker { public class VersionChangeChecker {
private final Set<String> changedTypes; private final Set<String> changedTypes;
private final int version;
private final int latestVersion;
public VersionChangeChecker(Set<String> changedTypes) { public VersionChangeChecker(Set<String> changedTypes, int version, int latestVersion) {
this.changedTypes = changedTypes; this.changedTypes = changedTypes;
this.version = version;
this.latestVersion = latestVersion;
} }
public boolean checkChanged(String name) { public boolean checkChanged(String name) {
return changedTypes.contains(name); return changedTypes.contains(name);
} }
public int getVersion() {
return version;
}
public int getLatestVersion() {
return latestVersion;
}
} }

View File

@ -1,15 +1,17 @@
package it.cavallium.data.generator.plugin; package it.cavallium.data.generator.plugin;
public record VersionedType(String type, int version) { import java.util.Objects;
public VersionedType withVersion(int version) { public record VersionedType(String type, ComputedVersion version) {
if (version == this.version) {
public VersionedType withVersion(ComputedVersion version) {
if (Objects.equals(version, this.version)) {
return this; return this;
} }
return new VersionedType(type, version); return new VersionedType(type, version);
} }
public VersionedType withVersionIfChanged(int version, VersionChangeChecker versionChangeChecker) { public VersionedType withVersionIfChanged(ComputedVersion version, VersionChangeChecker versionChangeChecker) {
if (versionChangeChecker.checkChanged(this.type)) { if (versionChangeChecker.checkChanged(this.type)) {
return withVersion(version); return withVersion(version);
} }

View File

@ -0,0 +1,21 @@
package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.TypeSpec;
import it.cavallium.data.generator.plugin.ClassGenerator;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
public class GenBaseType extends ClassGenerator {
public GenBaseType(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
var baseTypeClass = TypeSpec.enumBuilder("BaseType");
baseTypeClass.addModifiers(Modifier.PUBLIC);
dataModel.getBaseTypesComputed().forEach(baseType -> baseTypeClass.addEnumConstant(baseType.getName()));
return Stream.of(new GeneratedClass(dataModel.getRootPackage(basePackageName), baseTypeClass));
}
}

View File

@ -0,0 +1,191 @@
package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeSpec.Builder;
import com.squareup.javapoet.TypeVariableName;
import com.squareup.javapoet.WildcardTypeName;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedType;
import it.cavallium.data.generator.plugin.ComputedVersion;
import java.io.DataInput;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
public class GenCurrentVersion extends ClassGenerator {
public GenCurrentVersion(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
var currentVersionDataPackage = dataModel.getCurrentVersion().getDataPackage(basePackageName);
var currentVersionClass = TypeSpec.classBuilder("CurrentVersion");
currentVersionClass.addModifiers(Modifier.PUBLIC);
currentVersionClass.addModifiers(Modifier.FINAL);
// Add a static variable for the current version
{
var versionNumberField = FieldSpec.builder(ClassName
.get(dataModel.getCurrentVersion().getPackage(basePackageName),
"Version"), "VERSION").addModifiers(Modifier.PUBLIC).addModifiers(Modifier.STATIC)
.addModifiers(Modifier.FINAL).initializer("new " + dataModel.getCurrentVersion().getPackage(basePackageName)
+ ".Version()").build();
currentVersionClass.addField(versionNumberField);
}
// Check latest version method
{
var isLatestVersionMethod = MethodSpec.methodBuilder("isLatestVersion").addModifiers(Modifier.PUBLIC)
.addModifiers(Modifier.FINAL).addModifiers(Modifier.STATIC).returns(TypeName.BOOLEAN)
.addParameter(ParameterSpec.builder(TypeName.INT, "version").build())
.addCode("return version == VERSION.getVersion();").build();
currentVersionClass.addMethod(isLatestVersionMethod);
}
// Get super type classes method
{
var getSuperTypeClasses = MethodSpec.methodBuilder("getSuperTypeClasses").addModifiers(Modifier.PUBLIC)
.addModifiers(Modifier.FINAL).addModifiers(Modifier.STATIC)
.returns(ParameterizedTypeName.get(ClassName.get(Set.class),
ParameterizedTypeName.get(ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(currentVersionDataPackage, "IType")))))
.addCode("return $T.of(\n", Set.class);
AtomicBoolean isFirst = new AtomicBoolean(true);
dataModel.getSuperTypesComputed(dataModel.getCurrentVersion()).forEach(superType -> {
if (!isFirst.getAndSet(false)) {
getSuperTypeClasses.addCode(",\n");
}
getSuperTypeClasses.addCode("$T.class",
ClassName.get(dataModel.getVersion(superType).getDataPackage(basePackageName), superType.getName())
);
});
getSuperTypeClasses.addCode("\n);");
currentVersionClass.addMethod(getSuperTypeClasses.build());
}
// Get super type subtypes classes method
{
var getSuperTypeSubtypesClasses = MethodSpec.methodBuilder("getSuperTypeSubtypesClasses").addModifiers(Modifier.PUBLIC)
.addModifiers(Modifier.FINAL).addModifiers(Modifier.STATIC)
.returns(ParameterizedTypeName.get(ClassName.get(Set.class),
ParameterizedTypeName.get(ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(currentVersionDataPackage, "IBaseType")))));
getSuperTypeSubtypesClasses
.addParameter(ParameterSpec.builder(ParameterizedTypeName.get(ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(currentVersionDataPackage, "IType"))
), "superTypeClass").build());
getSuperTypeSubtypesClasses.beginControlFlow("return switch (superTypeClass.getCanonicalName())");
dataModel.getSuperTypesComputed(dataModel.getCurrentVersion()).forEach(superType -> {
getSuperTypeSubtypesClasses.addCode("case \"" + ClassName
.get(currentVersionDataPackage, superType.getName())
.canonicalName() + "\" -> $T.of(\n", Set.class);
getSuperTypeSubtypesClasses.addCode("$>");
AtomicBoolean isFirst = new AtomicBoolean(true);
for (ComputedType subType : superType.subTypes()) {
if (!isFirst.getAndSet(false)) {
getSuperTypeSubtypesClasses.addCode(",\n");
}
getSuperTypeSubtypesClasses.addCode("$T.class",
ClassName.get(currentVersionDataPackage, subType.getName())
);
}
getSuperTypeSubtypesClasses.addCode("$<");
getSuperTypeSubtypesClasses.addCode("\n);\n");
});
getSuperTypeSubtypesClasses.addStatement("default -> throw new $T()", IllegalArgumentException.class);
getSuperTypeSubtypesClasses.addCode(CodeBlock.of("$<};"));
currentVersionClass.addMethod(getSuperTypeSubtypesClasses.build());
}
// UpgradeDataToLatestVersion1 Method
{
var upgradeDataToLatestVersion1MethodBuilder = MethodSpec.methodBuilder("upgradeDataToLatestVersion")
.addTypeVariable(TypeVariableName.get("U", ClassName.get(currentVersionDataPackage, "IBaseType")))
.addModifiers(Modifier.PUBLIC).addModifiers(Modifier.STATIC).addModifiers(Modifier.FINAL).returns(TypeVariableName.get("U"))
.addParameter(ParameterSpec.builder(TypeName.INT, "oldVersion").build()).addParameter(
ParameterSpec.builder(ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType"), "type").build())
.addParameter(ParameterSpec.builder(DataInput.class, "oldDataInput").build())
.addException(IOException.class).beginControlFlow("return upgradeDataToLatestVersion(oldVersion, switch (oldVersion)");
for (var versionConfiguration : dataModel.getVersionsSet()) {
// Add a case in which the data version deserializes the serialized data and upgrades it
var versions = ClassName.get(dataModel.getRootPackage(basePackageName), "Versions");
upgradeDataToLatestVersion1MethodBuilder.addStatement("case $T.$N -> $T.INSTANCE.getSerializer(type).deserialize(oldDataInput)",
versions,
versionConfiguration.getVersionVarName(),
ClassName.get(versionConfiguration.getPackage(basePackageName), "Version")
);
}
var upgradeDataToLatestVersion1Method = upgradeDataToLatestVersion1MethodBuilder
.addStatement("default -> throw new $T(\"Unknown version: \" + oldVersion)", IOException.class)
.addCode(CodeBlock.of("$<});"))
.build();
currentVersionClass.addMethod(upgradeDataToLatestVersion1Method);
}
// UpgradeDataToLatestVersion2 Method
{
var versionsClassName = ClassName.get(dataModel.getRootPackage(basePackageName), "Versions");
var upgradeDataToLatestVersion2MethodBuilder = MethodSpec.methodBuilder("upgradeDataToLatestVersion")
.addModifiers(Modifier.PUBLIC).addModifiers(Modifier.STATIC).addModifiers(Modifier.FINAL).addTypeVariable(TypeVariableName.get("T"))
.addTypeVariable(TypeVariableName.get("U", ClassName.get(currentVersionDataPackage, "IBaseType")))
.returns(TypeVariableName.get("U"))
.addParameter(ParameterSpec.builder(TypeName.INT, "oldVersion").build())
.addParameter(ParameterSpec.builder(TypeVariableName.get("T"), "oldData").build())
.addException(IOException.class)
.addStatement("$T data = oldData", Object.class);
upgradeDataToLatestVersion2MethodBuilder.beginControlFlow("switch (oldVersion)");
for (var versionConfiguration : dataModel.getVersionsSet()) {
// Add a case in which the data version deserializes the serialized data and upgrades it
upgradeDataToLatestVersion2MethodBuilder.addCode("case $T.$N: ",
versionsClassName,
versionConfiguration.getVersionVarName()
);
if (versionConfiguration.isCurrent()) {
// This is the latest version, don't upgrade.
upgradeDataToLatestVersion2MethodBuilder.addStatement("return ($T) data", TypeVariableName.get("U"));
} else {
// Upgrade
ComputedVersion computedVersion = dataModel.getNextVersionOrThrow(versionConfiguration);
upgradeDataToLatestVersion2MethodBuilder
.addStatement(
"data = " + versionConfiguration.getPackage(basePackageName)
+ ".Version.upgradeToNextVersion(($T) data)",
ClassName.get(versionConfiguration.getDataPackage(basePackageName), "IBaseType")
);
}
}
upgradeDataToLatestVersion2MethodBuilder.addStatement("default: throw new $T(\"Unknown version: \" + oldVersion)", IOException.class);
upgradeDataToLatestVersion2MethodBuilder.endControlFlow();
currentVersionClass.addMethod(upgradeDataToLatestVersion2MethodBuilder.build());
}
generateGetClass(dataModel.getCurrentVersion(), currentVersionClass);
return Stream.of(new GeneratedClass(dataModel.getCurrentVersion().getPackage(basePackageName), currentVersionClass));
}
private void generateGetClass(ComputedVersion version, Builder classBuilder) {
var methodBuilder = MethodSpec.methodBuilder("getClass");
methodBuilder.addModifiers(Modifier.PUBLIC);
var baseTypeClassName = ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType");
methodBuilder.addParameter(baseTypeClassName, "type");
var iBaseTypeClassName = ClassName.get(version.getDataPackage(basePackageName), "IBaseType");
methodBuilder.returns(ParameterizedTypeName.get(ClassName.get(Class.class), WildcardTypeName.subtypeOf(iBaseTypeClassName)));
methodBuilder.beginControlFlow("return switch (type)");
dataModel.getBaseTypesComputed(version).forEach(baseType -> {
methodBuilder.addStatement("case $N -> $T.class", baseType.getName(), baseType.getJTypeName(basePackageName));
});
methodBuilder.addCode(CodeBlock.of("$<};"));
classBuilder.addMethod(methodBuilder.build());
}
}

View File

@ -0,0 +1,110 @@
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.TypeSpec;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedTypeBase;
import it.cavallium.data.generator.plugin.ComputedVersion;
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 GenDataBaseX extends ClassGenerator {
public GenDataBaseX(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 base) {
var type = (ClassName) base.getJTypeName(basePackageName);
var classBuilder = TypeSpec.recordBuilder(type.simpleName());
classBuilder.addModifiers(Modifier.PUBLIC);
var baseTypeClass = ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType");
var iTypeClass = ClassName.get(version.getPackage(basePackageName), "IBaseType");
classBuilder.addSuperinterface(iTypeClass);
dataModel.getSuperTypesOf(base).forEach(superType -> {
classBuilder.addSuperinterface(superType.getJTypeName(basePackageName));
classBuilder.addMethod(MethodSpec
.methodBuilder("getMetaId$" + superType.getName())
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addAnnotation(Override.class)
.returns(int.class)
.addStatement("return " + superType.subTypes().indexOf(base))
.build());
});
var ofMethod = MethodSpec
.methodBuilder("of")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL);
base.getData().forEach((fieldName, fieldType) -> {
var fieldTypeName = fieldType.getJTypeName(basePackageName);
var param = ParameterSpec
.builder(fieldTypeName, fieldName)
.addAnnotation(NotNull.class)
.build();
ofMethod.addParameter(param);
classBuilder.addRecordComponent(param);
var setter = MethodSpec
.methodBuilder("set" + StringUtils.capitalize(fieldName))
.addModifiers(Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(fieldTypeName, fieldName).addAnnotation(NotNull.class).build())
.addAnnotation(NotNull.class)
.returns(type);
if (!fieldTypeName.isPrimitive()) {
setter.addStatement("$T.requireNonNull($N)", Objects.class, fieldName);
}
if (fieldTypeName.isPrimitive() || !deepCheckBeforeCreatingNewEqualInstances) {
setter.addCode("return $N == this.$N ? this : new $T(", fieldName, fieldName, type);
setter.addCode(String.join(", ", base.getData().keySet()));
setter.addStatement(")");
} else {
setter.addCode("return $T.equals($N, this.$N) ? this : new $T(", Objects.class, fieldName, fieldName, type);
setter.addCode(String.join(", ", base.getData().keySet()));
setter.addStatement(")");
}
classBuilder.addMethod(setter.build());
});
classBuilder.addMethod(MethodSpec
.methodBuilder("getBaseType$")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addAnnotation(Override.class)
.returns(baseTypeClass)
.addStatement("return $T.$N", baseTypeClass, base.getName())
.build());
ofMethod.addCode("return new $T(", type);
ofMethod.addCode(String.join(", ", base.getData().keySet()));
ofMethod.addStatement(")");
ofMethod.returns(type);
classBuilder.addMethod(ofMethod.build());
return new GeneratedClass(type.packageName(), classBuilder);
}
}

View File

@ -0,0 +1,85 @@
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.TypeSpec;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedTypeSuper;
import it.cavallium.data.generator.plugin.ComputedVersion;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
public class GenDataSuperX extends ClassGenerator {
public GenDataSuperX(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) {
var type = (ClassName) typeSuper.getJTypeName(basePackageName);
var classBuilder = TypeSpec.interfaceBuilder(type.simpleName());
classBuilder.addModifiers(Modifier.PUBLIC);
var baseTypeClass = ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType");
var iBaseTypeClass = ClassName.get(version.getPackage(basePackageName), "IBaseType");
classBuilder.addSuperinterface(iBaseTypeClass);
dataModel.getExtendsInterfaces(typeSuper).forEach(superType -> {
classBuilder.addSuperinterface(superType.getJTypeName(basePackageName));
});
Stream
.concat(dataModel.getCommonInterfaceData(typeSuper), dataModel.getCommonInterfaceGetters(typeSuper))
.forEach(superType -> {
var returnType = superType.getValue().getJTypeName(basePackageName);
var getter = MethodSpec
.methodBuilder(superType.getKey())
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.returns(returnType);
if (!returnType.isPrimitive()) {
getter.addAnnotation(NotNull.class);
}
classBuilder.addMethod(getter.build());
});
dataModel.getCommonInterfaceData(typeSuper).forEach(superType -> {
var returnType = superType.getValue().getJTypeName(basePackageName);
var setter = MethodSpec
.methodBuilder("set" + StringUtils.capitalize(superType.getKey()))
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.addParameter(ParameterSpec.builder(returnType, "value").addAnnotation(NotNull.class).build())
.addAnnotation(NotNull.class)
.returns(type);
classBuilder.addMethod(setter.build());
});
dataModel.getSuperTypesOf(typeSuper).forEach(superType -> {
classBuilder.addSuperinterface(superType.getJTypeName(basePackageName));
});
classBuilder.addMethod(MethodSpec
.methodBuilder("getMetaId$" + typeSuper.getName())
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.returns(int.class)
.build());
return new GeneratedClass(type.packageName(), classBuilder);
}
}

View File

@ -0,0 +1,39 @@
package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedVersion;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
public class GenIBaseType extends ClassGenerator {
public GenIBaseType(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
return dataModel.getVersionsSet().stream().map(this::generateClass);
}
private GeneratedClass generateClass(ComputedVersion version) {
var interfaceBuilder = TypeSpec.interfaceBuilder("IBaseType");
interfaceBuilder.addModifiers(Modifier.PUBLIC);
var iTypeClassName = ClassName.get(version.getDataPackage(basePackageName), "IType");
var baseTypeClassName = ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType");
interfaceBuilder.addSuperinterface(iTypeClassName);
interfaceBuilder.addMethod(MethodSpec
.methodBuilder("getBaseType$")
.addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
.returns(baseTypeClassName)
.build());
return new GeneratedClass(version.getPackage(basePackageName), interfaceBuilder);
}
}

View File

@ -0,0 +1,39 @@
package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedVersion;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
public class GenINullableBaseType extends ClassGenerator {
public GenINullableBaseType(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
return dataModel.getVersionsSet().stream().map(this::generateClass);
}
private GeneratedClass generateClass(ComputedVersion version) {
var interfaceBuilder = TypeSpec.interfaceBuilder("INullableBaseType");
interfaceBuilder.addModifiers(Modifier.PUBLIC);
var iNullableITypeClass = ClassName.get(version.getDataNullablesPackage(basePackageName), "INullableIType");
var baseTypeClass = ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType");
interfaceBuilder.addSuperinterface(iNullableITypeClass);
interfaceBuilder.addMethod(MethodSpec
.methodBuilder("getBaseType$")
.addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
.returns(baseTypeClass)
.build());
return new GeneratedClass(version.getDataNullablesPackage(basePackageName), interfaceBuilder);
}
}

View File

@ -0,0 +1,34 @@
package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeSpec;
import it.cavallium.data.generator.nativedata.INullable;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedVersion;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
public class GenINullableIType extends ClassGenerator {
public GenINullableIType(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
return dataModel.getVersionsSet().stream().map(this::generateClass);
}
private GeneratedClass generateClass(ComputedVersion version) {
var interfaceBuilder = TypeSpec.interfaceBuilder("INullableIType");
interfaceBuilder.addModifiers(Modifier.PUBLIC);
var iTypeClass = ClassName.get(version.getPackage(basePackageName), "IType");
var iSuperNullableClass = ClassName.get(INullable.class);
interfaceBuilder.addSuperinterface(iTypeClass);
interfaceBuilder.addSuperinterface(iSuperNullableClass);
return new GeneratedClass(version.getDataNullablesPackage(basePackageName), interfaceBuilder);
}
}

View File

@ -0,0 +1,39 @@
package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedVersion;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
public class GenINullableSuperType extends ClassGenerator {
public GenINullableSuperType(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
return dataModel.getVersionsSet().stream().map(this::generateClass);
}
private GeneratedClass generateClass(ComputedVersion version) {
var interfaceBuilder = TypeSpec.interfaceBuilder("INullableSuperType");
interfaceBuilder.addModifiers(Modifier.PUBLIC);
var iNullableITypeClass = ClassName.get(version.getDataNullablesPackage(basePackageName), "INullableIType");
var superTypeClass = ClassName.get(dataModel.getRootPackage(basePackageName), "SuperType");
interfaceBuilder.addSuperinterface(iNullableITypeClass);
interfaceBuilder.addMethod(MethodSpec
.methodBuilder("getSuperType$")
.addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
.returns(superTypeClass)
.build());
return new GeneratedClass(version.getDataNullablesPackage(basePackageName), interfaceBuilder);
}
}

View File

@ -0,0 +1,31 @@
package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeSpec;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedVersion;
import java.io.Serializable;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
public class GenIType extends ClassGenerator {
public GenIType(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
return dataModel.getVersionsSet().stream().map(this::generateClass);
}
private GeneratedClass generateClass(ComputedVersion version) {
var interfaceBuilder = TypeSpec.interfaceBuilder("IType");
interfaceBuilder.addModifiers(Modifier.PUBLIC);
interfaceBuilder.addSuperinterface(ClassName.get(Serializable.class));
return new GeneratedClass(version.getPackage(basePackageName), interfaceBuilder);
}
}

View File

@ -0,0 +1,75 @@
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.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import com.squareup.javapoet.WildcardTypeName;
import it.cavallium.data.generator.DataSerializer;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ClassGenerator.ClassGeneratorParams;
import java.io.IOException;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
public class GenIVersion extends ClassGenerator {
public GenIVersion(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
var iVersionClass = TypeSpec.interfaceBuilder("IVersion");
iVersionClass.addModifiers(Modifier.PUBLIC);
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
{
var getSerializerMethodBuilder = MethodSpec
.methodBuilder("getSerializer")
.addModifiers(Modifier.PUBLIC)
.addModifiers(Modifier.ABSTRACT)
.addTypeVariable(TypeVariableName.get("T",
TypeVariableName.get("B")
))
.returns(ParameterizedTypeName.get(ClassName.get(DataSerializer.class), TypeVariableName.get("T")))
.addException(IOException.class)
.addParameter(ParameterSpec
.builder(ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType"), "type")
.build());
iVersionClass.addMethod(getSerializerMethodBuilder.build());
}
// Add getVersion method
{
var getVersionMethod = MethodSpec
.methodBuilder("getVersion")
.addModifiers(Modifier.PUBLIC)
.addModifiers(Modifier.ABSTRACT)
.returns(TypeName.INT)
.build();
iVersionClass.addMethod(getVersionMethod);
}
return Stream.of(new GeneratedClass(dataModel.getRootPackage(basePackageName), iVersionClass));
}
}

View File

@ -0,0 +1,137 @@
package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import it.cavallium.data.generator.TypedNullable;
import it.cavallium.data.generator.nativedata.INullable;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedTypeBase;
import it.cavallium.data.generator.plugin.ComputedTypeNullableVersioned;
import it.cavallium.data.generator.plugin.ComputedTypeSuper;
import it.cavallium.data.generator.plugin.ComputedVersion;
import java.util.List;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class GenNullableX extends ClassGenerator {
public GenNullableX(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
return dataModel.getVersionsSet().parallelStream().flatMap(this::generateVersionClasses);
}
private Stream<GeneratedClass> generateVersionClasses(ComputedVersion version) {
return dataModel
.getComputedTypes(version)
.values()
.stream()
.filter(type -> type instanceof ComputedTypeNullableVersioned)
.map(type -> (ComputedTypeNullableVersioned) type)
.filter(type -> type.getVersion().equals(version))
.map(type -> generateTypeVersioned(version, type));
}
private GeneratedClass generateTypeVersioned(ComputedVersion version, ComputedTypeNullableVersioned computedType) {
var type = (ClassName) computedType.getJTypeName(basePackageName);
var classBuilder = TypeSpec.recordBuilder(type.simpleName());
var base = computedType.getBase();
var baseType = base.getJTypeName(basePackageName);
classBuilder.addModifiers(Modifier.PUBLIC);
classBuilder.addRecordComponent(ParameterSpec.builder(baseType, "value").build());
var iNullableITypeClass = ClassName.get(version.getDataNullablesPackage(basePackageName), "INullableIType");
var iNullableClass = ClassName.get(INullable.class);
var typedNullable = ParameterizedTypeName.get(ClassName.get(TypedNullable.class), baseType);
if (base instanceof ComputedTypeSuper computedTypeSuper) {
var iNullableSuperTypeClass = ClassName.get(version.getDataNullablesPackage(basePackageName), "INullableSuperType");
var superTypeClass = ClassName.get(dataModel.getRootPackage(basePackageName), "SuperType");
classBuilder.addSuperinterface(iNullableSuperTypeClass);
classBuilder.addMethod(MethodSpec
.methodBuilder("getSuperType$")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.returns(superTypeClass)
.addStatement("return $T.$N", superTypeClass, base.getName())
.build());
} else if (base instanceof ComputedTypeBase computedTypeBase) {
var iNullableBaseTypeClass = ClassName.get(version.getDataNullablesPackage(basePackageName), "INullableBaseType");
var baseTypeClass = ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType");
classBuilder.addSuperinterface(iNullableBaseTypeClass);
classBuilder.addMethod(MethodSpec
.methodBuilder("getBaseType$")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.returns(baseTypeClass)
.addStatement("return $T.$N", baseTypeClass, base.getName())
.build());
} else {
throw new UnsupportedOperationException();
}
if (version.isCurrent()) {
classBuilder.addSuperinterfaces(List.of(iNullableITypeClass, iNullableClass, typedNullable));
classBuilder.addField(FieldSpec.builder(type, "NULL").addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL).initializer("new $T(($T)null)", type, baseType).build());
classBuilder.addMethod(MethodSpec
.methodBuilder("of")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.addParameter(ParameterSpec.builder(baseType, "value").build())
.addException(ClassName.get(NullPointerException.class))
.returns(type)
.beginControlFlow("if (value != null)")
.addStatement("return new $T(value)", type)
.nextControlFlow("else")
.addStatement("throw new $T()", NullPointerException.class)
.endControlFlow()
.build());
classBuilder.addMethod(MethodSpec
.methodBuilder("ofNullable")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.addParameter(ParameterSpec.builder(baseType, "value").build())
.returns(type)
.addStatement("return value != null ? new $T(value) : NULL", type)
.build());
classBuilder.addMethod(MethodSpec
.methodBuilder("empty")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.returns(type)
.addStatement("return NULL")
.build());
classBuilder.addMethod(MethodSpec
.methodBuilder("or")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addAnnotation(NotNull.class)
.returns(type)
.addParameter(ParameterSpec.builder(type, "fallback").addAnnotation(NotNull.class).build())
.addStatement("return this.value == null ? fallback : this")
.build());
classBuilder.addMethod(MethodSpec
.methodBuilder("getNullable")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addAnnotation(Nullable.class)
.returns(baseType)
.addStatement("return this.value")
.build());
}
return new GeneratedClass(type.packageName(), classBuilder);
}
}

View File

@ -0,0 +1,21 @@
package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.TypeSpec;
import it.cavallium.data.generator.plugin.ClassGenerator;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
public class GenSuperType extends ClassGenerator {
public GenSuperType(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
var superTypeClass = TypeSpec.enumBuilder("SuperType");
superTypeClass.addModifiers(Modifier.PUBLIC);
dataModel.getSuperTypesComputed().forEach(superType -> superTypeClass.addEnumConstant(superType.getName()));
return Stream.of(new GeneratedClass(dataModel.getRootPackage(basePackageName), superTypeClass));
}
}

View File

@ -0,0 +1,228 @@
package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeSpec.Builder;
import com.squareup.javapoet.TypeVariableName;
import it.cavallium.data.generator.DataSerializer;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedType.VersionedComputedType;
import it.cavallium.data.generator.plugin.ComputedTypeCustom;
import it.cavallium.data.generator.plugin.ComputedVersion;
import java.io.IOException;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
public class GenVersion extends ClassGenerator {
private static final boolean STRICT_SWITCH = false;
public GenVersion(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
return dataModel.getVersionsSet().stream().map(this::generateClass);
}
private GeneratedClass generateClass(ComputedVersion version) {
var classBuilder = TypeSpec.classBuilder("Version");
classBuilder.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
var iVersionClassName = ClassName.get(dataModel.getRootPackage(basePackageName), "IVersion");
var iBaseTypeClassName = ClassName.get(version.getDataPackage(basePackageName), "IBaseType");
classBuilder.addSuperinterface(ParameterizedTypeName.get(iVersionClassName, iBaseTypeClassName));
generateVersionField(version, classBuilder);
generateInstanceField(version, classBuilder);
generateUpgradeToNextVersion(version, classBuilder);
generateSerializerInstance(version, classBuilder);
generateUpgraderInstance(version, classBuilder);
generateGetSerializer(version, classBuilder);
generateGetVersion(version, classBuilder);
return new GeneratedClass(version.getPackage(basePackageName), classBuilder);
}
/**
* Add a static variable for the current version
*/
private void generateVersionField(ComputedVersion version, Builder classBuilder) {
var versionNumberXField = FieldSpec
.builder(TypeName.INT, "VERSION")
.addModifiers(Modifier.PUBLIC)
.addModifiers(Modifier.STATIC)
.addModifiers(Modifier.FINAL)
.initializer("$T." + version.getVersionVarName(),
ClassName.get(dataModel.getRootPackage(basePackageName), "Versions")
)
.build();
classBuilder.addField(versionNumberXField);
}
/**
* Add a static instance for the current version
*/
private void generateInstanceField(ComputedVersion version, Builder classBuilder) {
var versionClassType = ClassName.get(version.getPackage(basePackageName), "Version");
var versionInstanceField = FieldSpec
.builder(versionClassType, "INSTANCE")
.addModifiers(Modifier.PUBLIC)
.addModifiers(Modifier.STATIC)
.addModifiers(Modifier.FINAL)
.initializer("new $T()", versionClassType)
.build();
classBuilder.addField(versionInstanceField);
}
private void generateUpgradeToNextVersion(ComputedVersion version, Builder classBuilder) {
var nextVersion = dataModel.getNextVersion(version).orElse(null);
// Skip upgrade if it's the latest version
if (nextVersion == null) {
return;
}
var methodBuilder = MethodSpec.methodBuilder("upgradeToNextVersion");
methodBuilder.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL);
methodBuilder.addException(ClassName.get(IOException.class));
var nextIBaseType = ClassName.get(nextVersion.getDataPackage(basePackageName), "IBaseType");
methodBuilder.returns(nextIBaseType);
var iBaseTypeClassName = ClassName.get(version.getDataPackage(basePackageName), "IBaseType");
methodBuilder.addParameter(iBaseTypeClassName, "oldData");
methodBuilder.beginControlFlow( "return switch (oldData.getBaseType$$())");
dataModel.getBaseTypesComputed(version).forEach(baseType -> {
if (baseType.shouldUpgradeAfter(version)) {
var nextBaseType = dataModel.getNextVersion(baseType);
var versionType = ClassName.get(baseType.getVersion().getPackage(basePackageName), "Version");
methodBuilder.addStatement("case $N -> ($T) $T.$NUpgraderInstance.upgrade(($T) oldData)",
baseType.getName(),
nextBaseType.getJTypeName(basePackageName),
versionType,
baseType.getName(),
baseType.getJTypeName(basePackageName)
);
} else {
if (STRICT_SWITCH) {
methodBuilder.addStatement("case $N -> ($T) oldData",
baseType.getName(),
baseType.getJTypeName(basePackageName)
);
}
}
});
if (!STRICT_SWITCH) {
methodBuilder.addStatement("default -> ($T) oldData", nextIBaseType);
}
methodBuilder.addCode(CodeBlock.of("$<};"));
classBuilder.addMethod(methodBuilder.build());
}
private void generateSerializerInstance(ComputedVersion version, Builder classBuilder) {
var versionClassType = ClassName.get(version.getPackage(basePackageName), "Version");
dataModel.getComputedTypes(version).forEach((typeName, type) -> {
boolean shouldCreateInstanceField;
// Check if the type matches the current version
shouldCreateInstanceField = type instanceof VersionedComputedType versionedComputedType
&& versionedComputedType.getVersion().equals(version);
// Check if the type is custom, and this is the latest version
shouldCreateInstanceField |= version.isCurrent() && type instanceof ComputedTypeCustom;
if (!shouldCreateInstanceField) {
return;
}
var serializerFieldLocation = type.getJSerializerInstance(basePackageName);
if (!versionClassType.equals(serializerFieldLocation.className())) {
return;
}
var serializerClassName = type.getJSerializerName(basePackageName);
var fieldBuilder = FieldSpec.builder(ParameterizedTypeName.get(ClassName.get(DataSerializer.class),
type.getJTypeName(basePackageName)
), serializerFieldLocation.fieldName(), Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL);
fieldBuilder.initializer("new $T()", serializerClassName);
classBuilder.addField(fieldBuilder.build());
});
}
private void generateUpgraderInstance(ComputedVersion version, Builder classBuilder) {
var versionClassType = ClassName.get(version.getPackage(basePackageName), "Version");
dataModel.getComputedTypes(version).forEach((typeName, type) -> {
boolean shouldCreateInstanceField = type instanceof VersionedComputedType versionedComputedType
&& versionedComputedType.getVersion().equals(version);
if (!shouldCreateInstanceField) {
return;
}
var upgraderFieldLocation = type.getJUpgraderInstance(basePackageName);
if (!versionClassType.equals(upgraderFieldLocation.className())) {
return;
}
var upgraderClassName = type.getJUpgraderName(basePackageName);
var fieldBuilder = FieldSpec.builder(upgraderClassName,
upgraderFieldLocation.fieldName(),
Modifier.PUBLIC,
Modifier.STATIC,
Modifier.FINAL
);
fieldBuilder.initializer("new $T()", upgraderClassName);
classBuilder.addField(fieldBuilder.build());
});
}
private void generateGetSerializer(ComputedVersion version, Builder classBuilder) {
var methodBuilder = MethodSpec.methodBuilder("getSerializer");
methodBuilder.addModifiers(Modifier.PUBLIC);
methodBuilder.addAnnotation(Override.class);
methodBuilder.addException(ClassName.get(IOException.class));
var iBaseTypeClassName = ClassName.get(version.getDataPackage(basePackageName), "IBaseType");
methodBuilder.addTypeVariable(TypeVariableName.get("T", iBaseTypeClassName));
var baseTypeClassName = ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType");
methodBuilder.addParameter(baseTypeClassName, "type");
methodBuilder.returns(ParameterizedTypeName.get(ClassName.get(DataSerializer.class), TypeVariableName.get("T")));
methodBuilder.beginControlFlow("return switch (type)");
dataModel.getBaseTypesComputed(version).forEach(baseType -> {
var field = baseType.getJSerializerInstance(basePackageName);
methodBuilder.addStatement("case $N -> $T.$N", baseType.getName(), field.className(), field.fieldName());
});
methodBuilder.addCode(CodeBlock.of("$<};"));
classBuilder.addMethod(methodBuilder.build());
}
private void generateGetVersion(ComputedVersion version, Builder classBuilder) {
classBuilder.addMethod(MethodSpec
.methodBuilder("getVersion")
.addModifiers(Modifier.PUBLIC)
.returns(int.class)
.addStatement("return VERSION")
.addAnnotation(Override.class)
.build());
}
}

View File

@ -0,0 +1,62 @@
package it.cavallium.data.generator.plugin.classgen;
import com.squareup.javapoet.ArrayTypeName;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import it.cavallium.data.generator.plugin.ClassGenerator;
import it.cavallium.data.generator.plugin.ComputedVersion;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
public class GenVersions extends ClassGenerator {
public GenVersions(ClassGeneratorParams params) {
super(params);
}
@Override
protected Stream<GeneratedClass> generateClasses() {
var versionsClass = TypeSpec.classBuilder("Versions");
versionsClass.addModifiers(Modifier.PUBLIC);
versionsClass.addModifiers(Modifier.FINAL);
var versionsInstances = FieldSpec.builder(ArrayTypeName.of(ClassName.get(dataModel.getRootPackage(basePackageName), "IVersion")),
"VERSIONS",
Modifier.PUBLIC,
Modifier.STATIC,
Modifier.FINAL
);
List<CodeBlock> versionsInstancesValue = new ArrayList<>();
for (ComputedVersion version : dataModel.getVersionsSet()) {
// Add a static variable for this version, containing the normalized version number
var versionNumberField = FieldSpec
.builder(TypeName.INT, version.getVersionVarName())
.addModifiers(Modifier.PUBLIC)
.addModifiers(Modifier.STATIC)
.addModifiers(Modifier.FINAL)
.initializer(version.getVersionShortInt())
.build();
// Add the fields to the class
versionsClass.addField(versionNumberField);
var versionPackage = version.getPackage(basePackageName);
var versionClassType = ClassName.get(versionPackage, "Version");
versionsInstancesValue.add(CodeBlock.builder().add("$T.INSTANCE", versionClassType).build());
}
versionsInstances.initializer(CodeBlock
.builder()
.add("{\n")
.add(CodeBlock.join(versionsInstancesValue, ",\n"))
.add("\n}")
.build());
versionsClass.addField(versionsInstances.build());
return Stream.of(new GeneratedClass(dataModel.getRootPackage(basePackageName), versionsClass));
}
}

View File

@ -1,9 +1,9 @@
package it.cavallium.data.generator.plugin; package it.cavallium.data.generator.plugin;
import it.cavallium.data.generator.plugin.SourcesGenerator;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Path;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.Test;
public class TestGenerator { public class TestGenerator {
@ -11,7 +11,7 @@ public class TestGenerator {
public void test() throws IOException { public void test() throws IOException {
var dir = Files.createTempDirectory("data-generator-test"); var dir = Files.createTempDirectory("data-generator-test");
try { try {
SourcesGenerator.load(this.getClass().getResourceAsStream("/test.yaml")).generateSources("it.test", dir, false); SourcesGenerator.load(this.getClass().getResourceAsStream("/test.yaml")).generateSources("it.test", dir, false, true, false);
} finally { } finally {
try { try {
FileUtils.deleteDirectory(dir.toFile()); FileUtils.deleteDirectory(dir.toFile());

View File

@ -0,0 +1,28 @@
package it.cavallium.data.generator.nativedata;
public class Serializers {
public static final StringSerializer StringSerializerInstance = new StringSerializer();
public static final Int52Serializer Int52SerializerInstance = new Int52Serializer();
public static final NullableStringSerializer NullableStringSerializerInstance = new NullableStringSerializer();
public static final NullableInt52Serializer NullableInt52SerializerInstance = new NullableInt52Serializer();
public static final NullablebooleanSerializer NullablebooleanSerializerInstance = new NullablebooleanSerializer();
public static final NullablebyteSerializer NullablebyteSerializerInstance = new NullablebyteSerializer();
public static final NullableshortSerializer NullableshortSerializerInstance = new NullableshortSerializer();
public static final NullablecharSerializer NullablecharSerializerInstance = new NullablecharSerializer();
public static final NullableintSerializer NullableintSerializerInstance = new NullableintSerializer();
public static final NullablelongSerializer NullablelongSerializerInstance = new NullablelongSerializer();
public static final NullablefloatSerializer NullablefloatSerializerInstance = new NullablefloatSerializer();
public static final NullabledoubleSerializer NullabledoubleSerializer = new NullabledoubleSerializer();
public static final ArrayStringSerializer ArrayStringSerializerInstance = new ArrayStringSerializer();
public static final ArrayInt52Serializer ArrayInt52SerializerInstance = new ArrayInt52Serializer();
public static final ArraybooleanSerializer ArraybooleanSerializerInstance = new ArraybooleanSerializer();
public static final ArraybyteSerializer ArraybyteSerializerInstance = new ArraybyteSerializer();
public static final ArrayshortSerializer ArrayshortSerializerInstance = new ArrayshortSerializer();
public static final ArraycharSerializer ArraycharSerializerInstance = new ArraycharSerializer();
public static final ArrayintSerializer ArrayintSerializerInstance = new ArrayintSerializer();
public static final ArraylongSerializer ArraylongSerializerInstance = new ArraylongSerializer();
public static final ArrayfloatSerializer ArrayfloatSerializerInstance = new ArrayfloatSerializer();
public static final ArraydoubleSerializer ArraydoubleSerializer = new ArraydoubleSerializer();
}