Compute all types
This commit is contained in:
parent
f6fe7017c7
commit
4485dd675f
|
@ -0,0 +1,487 @@
|
||||||
|
package it.cavallium.data.generator.plugin;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public sealed interface ComputedType {
|
||||||
|
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
sealed interface VersionedComputedType extends ComputedType {
|
||||||
|
|
||||||
|
int getVersion();
|
||||||
|
|
||||||
|
ComputedType withChangeAtVersion(int version, VersionChangeChecker versionChangeChecker);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all types that are required by this type
|
||||||
|
*/
|
||||||
|
Stream<ComputedType> getDependencies();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all types that require this type
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package it.cavallium.data.generator.plugin;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class ComputedTypeSupplier {
|
||||||
|
|
||||||
|
private final Int2ObjectMap<Map<String, ComputedType>> computedTypeMap;
|
||||||
|
private final Int2ObjectMap<Map<String, List<ComputedType>>> computedTypeDependentsCacheMap = new Int2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
|
public ComputedTypeSupplier(Int2ObjectMap<Map<String, ComputedType>> computedTypeMap) {
|
||||||
|
this.computedTypeMap = computedTypeMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComputedType get(VersionedType type) {
|
||||||
|
var computedType = computedTypeMap.get(type.version()).get(type.type());
|
||||||
|
if (computedType == null) {
|
||||||
|
throw new IllegalStateException("Type " + type + " does not exist");
|
||||||
|
}
|
||||||
|
return computedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<ComputedType> getDependencies(VersionedType type) {
|
||||||
|
return computedTypeMap.get(type.version()).get(type.type()).getDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<ComputedType> getDependents(VersionedType type) {
|
||||||
|
synchronized (computedTypeDependentsCacheMap) {
|
||||||
|
return computedTypeDependentsCacheMap
|
||||||
|
.computeIfAbsent(type.version(), x -> new HashMap<>())
|
||||||
|
.computeIfAbsent(type.type(),
|
||||||
|
typeName -> computedTypeMap.get(type.version()).values().stream().filter(computedType ->
|
||||||
|
computedType.getDependencies().anyMatch(y -> Objects.equals(y.getName(), typeName))).toList())
|
||||||
|
.stream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,6 +31,10 @@ public final class CustomTypesConfiguration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getJavaClassString() {
|
||||||
|
return javaClass;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) {
|
if (this == o) {
|
||||||
|
|
|
@ -1,13 +1,22 @@
|
||||||
package it.cavallium.data.generator.plugin;
|
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 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.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.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -15,6 +24,7 @@ import java.util.Map.Entry;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
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;
|
||||||
|
@ -36,12 +46,12 @@ public class DataModel {
|
||||||
);
|
);
|
||||||
|
|
||||||
private final int currentVersion;
|
private final int currentVersion;
|
||||||
private final Int2ObjectMap<Map<String, ParsedClass>> classConfig;
|
|
||||||
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;
|
||||||
private final Map<String, Set<String>> superTypes;
|
private final Map<String, Set<String>> superTypes;
|
||||||
private final Map<String, CustomTypesConfiguration> customTypes;
|
private final Map<String, CustomTypesConfiguration> customTypes;
|
||||||
|
private final Int2ObjectMap<Map<String, ComputedType>> computedTypes;
|
||||||
|
|
||||||
public DataModel(int hash,
|
public DataModel(int hash,
|
||||||
String currentVersionKey,
|
String currentVersionKey,
|
||||||
|
@ -95,7 +105,7 @@ public class DataModel {
|
||||||
// Build versions sequence
|
// Build versions sequence
|
||||||
List<String> rawVersionsSequence = new ArrayList<>();
|
List<String> rawVersionsSequence = new ArrayList<>();
|
||||||
int versionsCount = 0;
|
int versionsCount = 0;
|
||||||
Int2ObjectMap<String> versionToName = new Int2ObjectOpenHashMap<>();
|
Int2ObjectMap<String> versionToName = new Int2ObjectLinkedOpenHashMap<>();
|
||||||
Object2IntMap<String> nameToVersion = new Object2IntOpenHashMap<>();
|
Object2IntMap<String> nameToVersion = new Object2IntOpenHashMap<>();
|
||||||
{
|
{
|
||||||
String lastVersion = null;
|
String lastVersion = null;
|
||||||
|
@ -132,7 +142,7 @@ public class DataModel {
|
||||||
|
|
||||||
Stream.concat(Stream.concat(Stream.concat(baseTypes.stream(), superTypes.stream()),
|
Stream.concat(Stream.concat(Stream.concat(baseTypes.stream(), superTypes.stream()),
|
||||||
customTypes.stream()), NATIVE_TYPES.stream())
|
customTypes.stream()), NATIVE_TYPES.stream())
|
||||||
.collect(Collectors.groupingBy(Function.identity()))
|
.collect(Collectors.groupingBy(identity()))
|
||||||
.values()
|
.values()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(x -> x.size() > 1)
|
.filter(x -> x.size() > 1)
|
||||||
|
@ -142,10 +152,10 @@ public class DataModel {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Compute the numeric versions map
|
// Compute the numeric versions map
|
||||||
Int2ObjectMap<ParsedVersion> versions = new Int2ObjectOpenHashMap<>();
|
Int2ObjectMap<ParsedVersion> versions = new Int2ObjectLinkedOpenHashMap<>();
|
||||||
rawVersions.forEach((k, v) -> versions.put(nameToVersion.getInt(k), new ParsedVersion(v)));
|
rawVersions.forEach((k, v) -> versions.put(nameToVersion.getInt(k), new ParsedVersion(v)));
|
||||||
|
|
||||||
Int2ObjectMap<Map<String, ParsedClass>> computedClassConfig = new Int2ObjectOpenHashMap<>();
|
Int2ObjectMap<Map<String, ParsedClass>> computedClassConfig = new Int2ObjectLinkedOpenHashMap<>();
|
||||||
for (int versionIndex = 0; versionIndex < versionsCount; versionIndex++) {
|
for (int versionIndex = 0; versionIndex < versionsCount; versionIndex++) {
|
||||||
if (versionIndex == 0) {
|
if (versionIndex == 0) {
|
||||||
computedClassConfig.put(0, baseTypesData.entrySet().stream()
|
computedClassConfig.put(0, baseTypesData.entrySet().stream()
|
||||||
|
@ -179,6 +189,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;
|
||||||
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);
|
||||||
|
@ -201,6 +212,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;
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -223,6 +235,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;
|
||||||
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 \""
|
||||||
|
@ -236,6 +249,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;
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -252,7 +266,133 @@ public class DataModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.classConfig = computedClassConfig;
|
Int2ObjectMap<Map<String, ComputedType>> computedTypes = new Int2ObjectLinkedOpenHashMap<>();
|
||||||
|
ComputedTypeSupplier computedTypeSupplier = new ComputedTypeSupplier(computedTypes);
|
||||||
|
for (int versionIndex = 0; versionIndex < versionsCount; versionIndex++) {
|
||||||
|
int versionIndexF = versionIndex;
|
||||||
|
if (versionIndexF == 0) {
|
||||||
|
// 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, 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Example upgrade:
|
||||||
|
|
||||||
|
V001======================================
|
||||||
|
_Message v1
|
||||||
|
|__MessageForwardOrigin v1
|
||||||
|
|__MessageText v1
|
||||||
|
|
||||||
|
_MessageText
|
||||||
|
|
||||||
|
_MessageForwardOrigin v1
|
||||||
|
|__MessageForwardOriginChat v1
|
||||||
|
|
||||||
|
_MessageForwardOriginChat v1
|
||||||
|
|__ChatEntityId v1
|
||||||
|
|
||||||
|
_UserId v1
|
||||||
|
|
||||||
|
_ChatEntityId v1
|
||||||
|
|__UserId v1
|
||||||
|
|__SupergroupId v1
|
||||||
|
|__BaseGroupId v1
|
||||||
|
|
||||||
|
|
||||||
|
V002======================================
|
||||||
|
* UserId changed
|
||||||
|
==========================================
|
||||||
|
_Message v2 *
|
||||||
|
|__MessageForwardOrigin v2 *
|
||||||
|
|__MessageText v1
|
||||||
|
|
||||||
|
_MessageText v1
|
||||||
|
|
||||||
|
_MessageForwardOrigin v2 *
|
||||||
|
|__MessageForwardOriginChat v2 *
|
||||||
|
|
||||||
|
_MessageForwardOriginChat v2 *
|
||||||
|
|__ChatEntityId v2 *
|
||||||
|
|
||||||
|
_UserId v2 *
|
||||||
|
|
||||||
|
_ChatEntityId v2 *
|
||||||
|
|__UserId v2 *
|
||||||
|
|__SupergroupId v1
|
||||||
|
|__BaseGroupId v1
|
||||||
|
*/
|
||||||
|
|
||||||
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));
|
||||||
|
@ -269,11 +409,30 @@ public class DataModel {
|
||||||
(a, b) -> {
|
(a, b) -> {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
},
|
},
|
||||||
Int2ObjectOpenHashMap::new
|
Int2ObjectLinkedOpenHashMap::new
|
||||||
));
|
));
|
||||||
|
LongAdder unchangedTot = new LongAdder();
|
||||||
|
LongAdder changedTot = new LongAdder();
|
||||||
|
computedTypes.forEach((version, types) -> {
|
||||||
|
System.out.println("Version: " + version);
|
||||||
|
System.out.println("\tTypes: " + types.size());
|
||||||
|
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 changed = types.values().stream().filter(t -> (t instanceof VersionedComputedType versionedComputedType && versionedComputedType.getVersion() == version)).count();
|
||||||
|
unchangedTot.add(unchanged);
|
||||||
|
changedTot.add(changed);
|
||||||
|
System.out.println("\t\tUnchanged: " + unchanged + " (" + (unchanged * 100 / (changed + unchanged)) + "%)");
|
||||||
|
System.out.println("\t\tChanged: " + changed + " (" + (changed * 100 / (changed + unchanged)) + "%)");
|
||||||
|
});
|
||||||
|
System.out.println("Result:");
|
||||||
|
var unchanged = unchangedTot.sum();
|
||||||
|
var changed = changedTot.sum();
|
||||||
|
System.out.println("\tAvoided type versions: " + unchanged + " (" + (unchanged * 100 / (changed + unchanged)) + "%)");
|
||||||
|
System.out.println("\tType versions: " + changed + " (" + (changed * 100 / (changed + unchanged)) + "%)");
|
||||||
this.currentVersion = versionsCount - 1;
|
this.currentVersion = versionsCount - 1;
|
||||||
this.superTypes = superTypesData;
|
this.superTypes = superTypesData;
|
||||||
this.customTypes = customTypesData;
|
this.customTypes = customTypesData;
|
||||||
|
this.computedTypes = computedTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String tryInsertAtIndex(LinkedHashMap<String, String> data, String key, String value, int index) {
|
private String tryInsertAtIndex(LinkedHashMap<String, String> data, String key, String value, int index) {
|
||||||
|
@ -429,4 +588,8 @@ public class DataModel {
|
||||||
public Map<String, CustomTypesConfiguration> getCustomTypes() {
|
public Map<String, CustomTypesConfiguration> getCustomTypes() {
|
||||||
return customTypes;
|
return customTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Int2ObjectMap<Map<String, ComputedType>> getComputedTypes() {
|
||||||
|
return computedTypes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ 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;
|
||||||
|
@ -13,6 +14,7 @@ public final class ParsedClass {
|
||||||
public String stringRepresenter;
|
public String stringRepresenter;
|
||||||
|
|
||||||
public LinkedHashMap<String, String> data;
|
public LinkedHashMap<String, String> data;
|
||||||
|
public boolean changed;
|
||||||
|
|
||||||
public ParsedClass(ClassConfiguration baseTypesData) {
|
public ParsedClass(ClassConfiguration baseTypesData) {
|
||||||
this.stringRepresenter = baseTypesData.stringRepresenter;
|
this.stringRepresenter = baseTypesData.stringRepresenter;
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package it.cavallium.data.generator.plugin;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class VersionChangeChecker {
|
||||||
|
|
||||||
|
private final Set<String> changedTypes;
|
||||||
|
|
||||||
|
public VersionChangeChecker(Set<String> changedTypes) {
|
||||||
|
this.changedTypes = changedTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkChanged(String name) {
|
||||||
|
return changedTypes.contains(name);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
package it.cavallium.data.generator.plugin;
|
package it.cavallium.data.generator.plugin;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@ -8,6 +10,29 @@ public class VersionConfiguration {
|
||||||
public String previousVersion;
|
public String previousVersion;
|
||||||
public DetailsConfiguration details;
|
public DetailsConfiguration details;
|
||||||
public List<VersionTransformation> transformations;
|
public List<VersionTransformation> transformations;
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* Type 1: v1
|
||||||
|
* Type 2: v4
|
||||||
|
* Type 3: v2
|
||||||
|
* ...
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public Object2IntMap<String> typeVersions;
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* - Type 1
|
||||||
|
* |_Dependent type 1
|
||||||
|
* |_Dependent type 2
|
||||||
|
* |_Dependent type ...
|
||||||
|
*
|
||||||
|
* - Type 2
|
||||||
|
* |_Dependent type 1
|
||||||
|
* |_Dependent type 2
|
||||||
|
* |_Dependent type ...
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public Object2ObjectMap<String, List<String>> dependentTypes;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package it.cavallium.data.generator.plugin;
|
||||||
|
|
||||||
|
public record VersionedType(String type, int version) {
|
||||||
|
|
||||||
|
public VersionedType withVersion(int version) {
|
||||||
|
if (version == this.version) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new VersionedType(type, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VersionedType withVersionIfChanged(int version, VersionChangeChecker versionChangeChecker) {
|
||||||
|
if (versionChangeChecker.checkChanged(this.type)) {
|
||||||
|
return withVersion(version);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user