Refactoring of constructors for generated entities
This commit is contained in:
parent
6d0b551b5e
commit
96b2669a00
11 changed files with 89 additions and 91 deletions
|
@ -50,6 +50,7 @@ import javax.lang.model.SourceVersion;
|
||||||
import static org.keycloak.models.map.processor.FieldAccessorType.GETTER;
|
import static org.keycloak.models.map.processor.FieldAccessorType.GETTER;
|
||||||
import static org.keycloak.models.map.processor.Util.getGenericsDeclaration;
|
import static org.keycloak.models.map.processor.Util.getGenericsDeclaration;
|
||||||
import static org.keycloak.models.map.processor.Util.isMapType;
|
import static org.keycloak.models.map.processor.Util.isMapType;
|
||||||
|
import static org.keycloak.models.map.processor.Util.singularToPlural;
|
||||||
|
|
||||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||||
public abstract class AbstractGenerateEntityImplementationsProcessor extends AbstractProcessor {
|
public abstract class AbstractGenerateEntityImplementationsProcessor extends AbstractProcessor {
|
||||||
|
@ -123,11 +124,11 @@ public abstract class AbstractGenerateEntityImplementationsProcessor extends Abs
|
||||||
|
|
||||||
// Merge plurals with singulars
|
// Merge plurals with singulars
|
||||||
methodsPerAttribute.keySet().stream()
|
methodsPerAttribute.keySet().stream()
|
||||||
.filter(key -> methodsPerAttribute.containsKey(key + "s"))
|
.filter(key -> methodsPerAttribute.containsKey(singularToPlural(key)))
|
||||||
.collect(Collectors.toSet())
|
.collect(Collectors.toSet())
|
||||||
.forEach(key -> {
|
.forEach(key -> {
|
||||||
HashSet<ExecutableElement> removed = methodsPerAttribute.remove(key);
|
HashSet<ExecutableElement> removed = methodsPerAttribute.remove(key);
|
||||||
methodsPerAttribute.get(key + "s").addAll(removed);
|
methodsPerAttribute.get(singularToPlural(key)).addAll(removed);
|
||||||
});
|
});
|
||||||
|
|
||||||
return methodsPerAttribute;
|
return methodsPerAttribute;
|
||||||
|
|
|
@ -26,6 +26,7 @@ import javax.lang.model.element.Name;
|
||||||
import javax.lang.model.type.TypeMirror;
|
import javax.lang.model.type.TypeMirror;
|
||||||
import javax.lang.model.util.Types;
|
import javax.lang.model.util.Types;
|
||||||
import static org.keycloak.models.map.processor.Util.getGenericsDeclaration;
|
import static org.keycloak.models.map.processor.Util.getGenericsDeclaration;
|
||||||
|
import static org.keycloak.models.map.processor.Util.pluralToSingular;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -52,7 +53,7 @@ enum FieldAccessorType {
|
||||||
COLLECTION_ADD {
|
COLLECTION_ADD {
|
||||||
@Override
|
@Override
|
||||||
public boolean is(ExecutableElement method, String fieldName, Types types, TypeMirror fieldType) {
|
public boolean is(ExecutableElement method, String fieldName, Types types, TypeMirror fieldType) {
|
||||||
String fieldNameSingular = fieldName.endsWith("s") ? fieldName.substring(0, fieldName.length() - 1) : fieldName;
|
String fieldNameSingular = pluralToSingular(fieldName);
|
||||||
String methodName = "add" + fieldNameSingular;
|
String methodName = "add" + fieldNameSingular;
|
||||||
List<TypeMirror> res = getGenericsDeclaration(fieldType);
|
List<TypeMirror> res = getGenericsDeclaration(fieldType);
|
||||||
return Objects.equals(methodName, method.getSimpleName().toString())
|
return Objects.equals(methodName, method.getSimpleName().toString())
|
||||||
|
@ -63,7 +64,7 @@ enum FieldAccessorType {
|
||||||
COLLECTION_DELETE {
|
COLLECTION_DELETE {
|
||||||
@Override
|
@Override
|
||||||
public boolean is(ExecutableElement method, String fieldName, Types types, TypeMirror fieldType) {
|
public boolean is(ExecutableElement method, String fieldName, Types types, TypeMirror fieldType) {
|
||||||
String fieldNameSingular = fieldName.endsWith("s") ? fieldName.substring(0, fieldName.length() - 1) : fieldName;
|
String fieldNameSingular = pluralToSingular(fieldName);
|
||||||
String removeFromCollection = "remove" + fieldNameSingular;
|
String removeFromCollection = "remove" + fieldNameSingular;
|
||||||
List<TypeMirror> res = getGenericsDeclaration(fieldType);
|
List<TypeMirror> res = getGenericsDeclaration(fieldType);
|
||||||
return Objects.equals(removeFromCollection, method.getSimpleName().toString())
|
return Objects.equals(removeFromCollection, method.getSimpleName().toString())
|
||||||
|
@ -74,7 +75,7 @@ enum FieldAccessorType {
|
||||||
MAP_ADD {
|
MAP_ADD {
|
||||||
@Override
|
@Override
|
||||||
public boolean is(ExecutableElement method, String fieldName, Types types, TypeMirror fieldType) {
|
public boolean is(ExecutableElement method, String fieldName, Types types, TypeMirror fieldType) {
|
||||||
String fieldNameSingular = fieldName.endsWith("s") ? fieldName.substring(0, fieldName.length() - 1) : fieldName;
|
String fieldNameSingular = pluralToSingular(fieldName);
|
||||||
String methodName = "set" + fieldNameSingular;
|
String methodName = "set" + fieldNameSingular;
|
||||||
List<TypeMirror> res = getGenericsDeclaration(fieldType);
|
List<TypeMirror> res = getGenericsDeclaration(fieldType);
|
||||||
return Objects.equals(methodName, method.getSimpleName().toString())
|
return Objects.equals(methodName, method.getSimpleName().toString())
|
||||||
|
@ -86,7 +87,7 @@ enum FieldAccessorType {
|
||||||
MAP_GET {
|
MAP_GET {
|
||||||
@Override
|
@Override
|
||||||
public boolean is(ExecutableElement method, String fieldName, Types types, TypeMirror fieldType) {
|
public boolean is(ExecutableElement method, String fieldName, Types types, TypeMirror fieldType) {
|
||||||
String fieldNameSingular = fieldName.endsWith("s") ? fieldName.substring(0, fieldName.length() - 1) : fieldName;
|
String fieldNameSingular = pluralToSingular(fieldName);
|
||||||
String methodName = "get" + fieldNameSingular;
|
String methodName = "get" + fieldNameSingular;
|
||||||
List<TypeMirror> res = getGenericsDeclaration(fieldType);
|
List<TypeMirror> res = getGenericsDeclaration(fieldType);
|
||||||
return Objects.equals(methodName, method.getSimpleName().toString())
|
return Objects.equals(methodName, method.getSimpleName().toString())
|
||||||
|
|
|
@ -171,7 +171,7 @@ public class GenerateEntityImplementationsProcessor extends AbstractGenerateEnti
|
||||||
pw.println(" return " + types.erasure(firstParameterType) + ".class;");
|
pw.println(" return " + types.erasure(firstParameterType) + ".class;");
|
||||||
pw.println(" }");
|
pw.println(" }");
|
||||||
});
|
});
|
||||||
|
|
||||||
FieldAccessorType.getMethod(FieldAccessorType.MAP_ADD, methods, fieldName, types, fieldType).ifPresent(method -> {
|
FieldAccessorType.getMethod(FieldAccessorType.MAP_ADD, methods, fieldName, types, fieldType).ifPresent(method -> {
|
||||||
TypeMirror firstParameterType = method.getParameters().get(0).asType();
|
TypeMirror firstParameterType = method.getParameters().get(0).asType();
|
||||||
TypeMirror secondParameterType = method.getParameters().get(1).asType();
|
TypeMirror secondParameterType = method.getParameters().get(1).asType();
|
||||||
|
@ -182,7 +182,7 @@ public class GenerateEntityImplementationsProcessor extends AbstractGenerateEnti
|
||||||
pw.println(" return " + types.erasure(secondParameterType) + ".class;");
|
pw.println(" return " + types.erasure(secondParameterType) + ".class;");
|
||||||
pw.println(" }");
|
pw.println(" }");
|
||||||
});
|
});
|
||||||
|
|
||||||
for (ExecutableElement ee : methods) {
|
for (ExecutableElement ee : methods) {
|
||||||
FieldAccessorType fat = FieldAccessorType.determineType(ee, fieldName, types, fieldType);
|
FieldAccessorType fat = FieldAccessorType.determineType(ee, fieldName, types, fieldType);
|
||||||
printMethodBody(pw, fat, ee, className, fieldType);
|
printMethodBody(pw, fat, ee, className, fieldType);
|
||||||
|
@ -265,6 +265,7 @@ public class GenerateEntityImplementationsProcessor extends AbstractGenerateEnti
|
||||||
boolean needsDeepClone = fieldGetters(methodsPerAttribute)
|
boolean needsDeepClone = fieldGetters(methodsPerAttribute)
|
||||||
.map(ExecutableElement::getReturnType)
|
.map(ExecutableElement::getReturnType)
|
||||||
.anyMatch(fieldType -> ! isKnownCollectionOfImmutableFinalTypes(fieldType) && ! isImmutableFinalType(fieldType));
|
.anyMatch(fieldType -> ! isKnownCollectionOfImmutableFinalTypes(fieldType) && ! isImmutableFinalType(fieldType));
|
||||||
|
boolean usingGeneratedCloner = ! hasDeepClone && needsDeepClone;
|
||||||
|
|
||||||
JavaFileObject file = processingEnv.getFiler().createSourceFile(mapImplClassName);
|
JavaFileObject file = processingEnv.getFiler().createSourceFile(mapImplClassName);
|
||||||
try (PrintWriter pw = new PrintWriterNoJavaLang(file.openWriter())) {
|
try (PrintWriter pw = new PrintWriterNoJavaLang(file.openWriter())) {
|
||||||
|
@ -283,28 +284,26 @@ public class GenerateEntityImplementationsProcessor extends AbstractGenerateEnti
|
||||||
.map(ExecutableElement.class::cast)
|
.map(ExecutableElement.class::cast)
|
||||||
.filter((ExecutableElement ee) -> ee.getKind() == ElementKind.CONSTRUCTOR)
|
.filter((ExecutableElement ee) -> ee.getKind() == ElementKind.CONSTRUCTOR)
|
||||||
.forEach((ExecutableElement ee) -> {
|
.forEach((ExecutableElement ee) -> {
|
||||||
if (hasDeepClone || ! needsDeepClone) {
|
// Create constructor and initialize cloner to DUMB_CLONER if necessary
|
||||||
pw.println(" "
|
if (usingGeneratedCloner) {
|
||||||
+ ee.getModifiers().stream().map(Object::toString).collect(Collectors.joining(" "))
|
|
||||||
+ " " + mapSimpleClassName + "(" + methodParameters(ee.getParameters()) + ") { super(" + ee.getParameters() + "); }"
|
|
||||||
);
|
|
||||||
} else if (needsDeepClone) {
|
|
||||||
pw.println(" /**");
|
pw.println(" /**");
|
||||||
pw.println(" * @deprecated This constructor uses a {@link DeepCloner#DUMB_CLONER} that does not clone anything. Use {@link #" + mapSimpleClassName + "(DeepCloner)} variant instead");
|
pw.println(" * @deprecated This constructor uses a {@link DeepCloner#DUMB_CLONER} that does not clone anything. Use {@link #" + mapSimpleClassName + "(DeepCloner)} variant instead");
|
||||||
pw.println(" */");
|
pw.println(" */");
|
||||||
pw.println(" "
|
|
||||||
+ ee.getModifiers().stream().map(Object::toString).collect(Collectors.joining(" "))
|
|
||||||
+ " "
|
|
||||||
+ mapSimpleClassName + "(" + methodParameters(ee.getParameters()) + ") { this(DeepCloner.DUMB_CLONER" + (ee.getParameters().isEmpty() ? "" : ", ") + ee.getParameters() + "); }"
|
|
||||||
);
|
|
||||||
pw.println(" "
|
|
||||||
+ ee.getModifiers().stream().map(Object::toString).collect(Collectors.joining(" "))
|
|
||||||
+ " "
|
|
||||||
+ mapSimpleClassName + "(DeepCloner cloner" + (ee.getParameters().isEmpty() ? "" : ", ") + methodParameters(ee.getParameters()) + ") { super(" + ee.getParameters() + "); this.cloner = cloner; }"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
pw.println(" "
|
||||||
|
+ ee.getModifiers().stream().map(Object::toString).collect(Collectors.joining(" "))
|
||||||
|
+ " " + mapSimpleClassName + "(" + methodParameters(ee.getParameters()) + ") {"
|
||||||
|
);
|
||||||
|
pw.println(" super(" + ee.getParameters() + ");");
|
||||||
|
if (usingGeneratedCloner) pw.println(" this.cloner = DeepCloner.DUMB_CLONER;");
|
||||||
|
pw.println(" }");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pw.println(" "
|
||||||
|
+ "public "
|
||||||
|
+ mapSimpleClassName + "(DeepCloner cloner) { super(); " + (!usingGeneratedCloner ? "" : "this.cloner = cloner;") + "}"
|
||||||
|
);
|
||||||
|
|
||||||
// equals, hashCode, toString
|
// equals, hashCode, toString
|
||||||
pw.println(" @Override public boolean equals(Object o) {");
|
pw.println(" @Override public boolean equals(Object o) {");
|
||||||
pw.println(" if (o == this) return true; ");
|
pw.println(" if (o == this) return true; ");
|
||||||
|
@ -339,7 +338,7 @@ public class GenerateEntityImplementationsProcessor extends AbstractGenerateEnti
|
||||||
pw.println(" }");
|
pw.println(" }");
|
||||||
|
|
||||||
// deepClone
|
// deepClone
|
||||||
if (! hasDeepClone && needsDeepClone) {
|
if (usingGeneratedCloner) {
|
||||||
pw.println(" private final DeepCloner cloner;");
|
pw.println(" private final DeepCloner cloner;");
|
||||||
pw.println(" public <V> V deepClone(V obj) {");
|
pw.println(" public <V> V deepClone(V obj) {");
|
||||||
pw.println(" return cloner.from(obj);");
|
pw.println(" return cloner.from(obj);");
|
||||||
|
@ -363,7 +362,7 @@ public class GenerateEntityImplementationsProcessor extends AbstractGenerateEnti
|
||||||
|
|
||||||
if (parentMethod.isPresent()) {
|
if (parentMethod.isPresent()) {
|
||||||
processingEnv.getMessager().printMessage(Kind.OTHER, "Method " + method + " is declared in a parent class.", method);
|
processingEnv.getMessager().printMessage(Kind.OTHER, "Method " + method + " is declared in a parent class.", method);
|
||||||
} else if (fat != FieldAccessorType.UNKNOWN && ! printMethodBody(pw, fat, method, "f" + me.getKey(), fieldType)) {
|
} else if (fat == FieldAccessorType.UNKNOWN || ! printMethodBody(pw, fat, method, "f" + me.getKey(), fieldType)) {
|
||||||
processingEnv.getMessager().printMessage(Kind.WARNING, "Could not determine desired semantics of method from its signature", method);
|
processingEnv.getMessager().printMessage(Kind.WARNING, "Could not determine desired semantics of method from its signature", method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -446,10 +445,12 @@ public class GenerateEntityImplementationsProcessor extends AbstractGenerateEnti
|
||||||
pw.println(" }");
|
pw.println(" }");
|
||||||
return true;
|
return true;
|
||||||
case COLLECTION_DELETE:
|
case COLLECTION_DELETE:
|
||||||
|
boolean needsReturn = method.getReturnType().getKind() != TypeKind.VOID;
|
||||||
pw.println(" @SuppressWarnings(\"unchecked\") @Override public " + method.getReturnType() + " " + method.getSimpleName() + "(" + firstParameterType + " p0) {");
|
pw.println(" @SuppressWarnings(\"unchecked\") @Override public " + method.getReturnType() + " " + method.getSimpleName() + "(" + firstParameterType + " p0) {");
|
||||||
pw.println(" if (" + fieldName + " == null) { return; }");
|
pw.println(" if (" + fieldName + " == null) { return" + (needsReturn ? " false" : "") + "; }");
|
||||||
pw.println(" boolean removed = " + fieldName + ".remove(p0)" + ("java.util.Map".equals(typeElement.getQualifiedName().toString()) ? " != null" : "") + ";");
|
pw.println(" boolean removed = " + fieldName + ".remove(p0)" + ("java.util.Map".equals(typeElement.getQualifiedName().toString()) ? " != null" : "") + ";");
|
||||||
pw.println(" updated |= removed;");
|
pw.println(" updated |= removed;");
|
||||||
|
if (needsReturn) pw.println(" return removed;");
|
||||||
pw.println(" }");
|
pw.println(" }");
|
||||||
return true;
|
return true;
|
||||||
case MAP_ADD:
|
case MAP_ADD:
|
||||||
|
|
|
@ -104,6 +104,7 @@ public class GenerateHotRodEntityImplementationsProcessor extends AbstractGenera
|
||||||
boolean needsDeepClone = fieldGetters(methodsPerAttribute)
|
boolean needsDeepClone = fieldGetters(methodsPerAttribute)
|
||||||
.map(ExecutableElement::getReturnType)
|
.map(ExecutableElement::getReturnType)
|
||||||
.anyMatch(fieldType -> ! isKnownCollectionOfImmutableFinalTypes(fieldType) && ! isImmutableFinalType(fieldType));
|
.anyMatch(fieldType -> ! isKnownCollectionOfImmutableFinalTypes(fieldType) && ! isImmutableFinalType(fieldType));
|
||||||
|
boolean usingGeneratedCloner = ! hasDeepClone && needsDeepClone;
|
||||||
boolean hasId = methodsPerAttribute.containsKey("Id") || allMembers.stream().anyMatch(el -> "getId".equals(el.getSimpleName().toString()));
|
boolean hasId = methodsPerAttribute.containsKey("Id") || allMembers.stream().anyMatch(el -> "getId".equals(el.getSimpleName().toString()));
|
||||||
|
|
||||||
JavaFileObject file = processingEnv.getFiler().createSourceFile(hotRodImplClassName);
|
JavaFileObject file = processingEnv.getFiler().createSourceFile(hotRodImplClassName);
|
||||||
|
@ -132,45 +133,43 @@ public class GenerateHotRodEntityImplementationsProcessor extends AbstractGenera
|
||||||
.map(ExecutableElement.class::cast)
|
.map(ExecutableElement.class::cast)
|
||||||
.filter((ExecutableElement ee) -> ee.getKind() == ElementKind.CONSTRUCTOR)
|
.filter((ExecutableElement ee) -> ee.getKind() == ElementKind.CONSTRUCTOR)
|
||||||
.forEach((ExecutableElement ee) -> {
|
.forEach((ExecutableElement ee) -> {
|
||||||
if (hasDeepClone || ! needsDeepClone) {
|
// Create constructor and initialize cloner to DUMB_CLONER if necessary
|
||||||
pw.println(" "
|
if (usingGeneratedCloner) {
|
||||||
+ ee.getModifiers().stream().map(Object::toString).collect(Collectors.joining(" "))
|
|
||||||
+ " " + hotRodSimpleClassName + "(" + methodParameters(ee.getParameters()) + ") {"
|
|
||||||
);
|
|
||||||
pw.println(" super(" + ee.getParameters() + ");");
|
|
||||||
pw.println(" this." + ENTITY_VARIABLE + " = new " + className + "();");
|
|
||||||
pw.println(" }");
|
|
||||||
} else if (needsDeepClone) {
|
|
||||||
pw.println(" /**");
|
pw.println(" /**");
|
||||||
pw.println(" * @deprecated This constructor uses a {@link DeepCloner#DUMB_CLONER} that does not clone anything. Use {@link #" + hotRodSimpleClassName + "(DeepCloner)} variant instead");
|
pw.println(" * @deprecated This constructor uses a {@link DeepCloner#DUMB_CLONER} that does not clone anything. Use {@link #" + hotRodSimpleClassName + "(DeepCloner)} variant instead");
|
||||||
pw.println(" */");
|
pw.println(" */");
|
||||||
pw.println(" "
|
|
||||||
+ ee.getModifiers().stream().map(Object::toString).collect(Collectors.joining(" "))
|
|
||||||
+ " "
|
|
||||||
+ hotRodSimpleClassName + "(" + methodParameters(ee.getParameters()) + ") { this(DeepCloner.DUMB_CLONER" + (ee.getParameters().isEmpty() ? "" : ", ") + ee.getParameters() + "); }"
|
|
||||||
);
|
|
||||||
pw.println(" "
|
|
||||||
+ ee.getModifiers().stream().map(Object::toString).collect(Collectors.joining(" "))
|
|
||||||
+ " "
|
|
||||||
+ hotRodSimpleClassName + "(DeepCloner cloner" + (ee.getParameters().isEmpty() ? "" : ", ") + methodParameters(ee.getParameters()) + ") {"
|
|
||||||
);
|
|
||||||
pw.println(" super(" + ee.getParameters() + ");");
|
|
||||||
pw.println(" this.cloner = cloner;");
|
|
||||||
pw.println(" this." + ENTITY_VARIABLE + " = new " + className + "();");
|
|
||||||
pw.println(" }");
|
|
||||||
}
|
}
|
||||||
|
pw.println(" "
|
||||||
|
+ ee.getModifiers().stream().map(Object::toString).collect(Collectors.joining(" "))
|
||||||
|
+ " " + hotRodSimpleClassName + "(" + methodParameters(ee.getParameters()) + ") {"
|
||||||
|
);
|
||||||
|
pw.println(" super(" + ee.getParameters() + ");");
|
||||||
|
if (usingGeneratedCloner) pw.println(" this.cloner = DeepCloner.DUMB_CLONER;");
|
||||||
|
pw.println(" this." + ENTITY_VARIABLE + " = new " + className + "();");
|
||||||
|
pw.println(" }");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add constructor for setting HotRodEntity
|
// Add constructor for setting HotRodEntity
|
||||||
|
if (usingGeneratedCloner) {
|
||||||
|
pw.println(" /**");
|
||||||
|
pw.println(" * @deprecated This constructor uses a {@link DeepCloner#DUMB_CLONER} that does not clone anything. Use {@link #" + hotRodSimpleClassName + "(DeepCloner)} variant instead");
|
||||||
|
pw.println(" */");
|
||||||
|
}
|
||||||
pw.println(" " +
|
pw.println(" " +
|
||||||
"public " + hotRodSimpleClassName + "(" + className + " " + ENTITY_VARIABLE + ") {"
|
"public " + hotRodSimpleClassName + "(" + className + " " + ENTITY_VARIABLE + ") {"
|
||||||
);
|
);
|
||||||
pw.println(" this." + ENTITY_VARIABLE + " = " + ENTITY_VARIABLE + ";");
|
pw.println(" this." + ENTITY_VARIABLE + " = " + ENTITY_VARIABLE + ";");
|
||||||
if (! hasDeepClone && needsDeepClone) {
|
if (usingGeneratedCloner) {
|
||||||
pw.println(" this.cloner = DeepCloner.DUMB_CLONER;");
|
pw.println(" this.cloner = DeepCloner.DUMB_CLONER;");
|
||||||
}
|
}
|
||||||
pw.println(" }");
|
pw.println(" }");
|
||||||
|
|
||||||
|
pw.println(" public " + hotRodSimpleClassName + "(DeepCloner cloner) {");
|
||||||
|
pw.println(" super();");
|
||||||
|
pw.println(" this." + ENTITY_VARIABLE + " = new " + className + "();");
|
||||||
|
if (usingGeneratedCloner) pw.println(" this.cloner = cloner;");
|
||||||
|
pw.println(" }");
|
||||||
|
|
||||||
// equals, hashCode, toString
|
// equals, hashCode, toString
|
||||||
pw.println(" @Override public boolean equals(Object o) {");
|
pw.println(" @Override public boolean equals(Object o) {");
|
||||||
pw.println(" if (o == this) return true; ");
|
pw.println(" if (o == this) return true; ");
|
||||||
|
|
|
@ -99,4 +99,11 @@ public class Util {
|
||||||
.findAny();
|
.findAny();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String singularToPlural(String word) {
|
||||||
|
return word.endsWith("y") ? word.substring(0, word.length() -1) + "ies" : word + "s";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String pluralToSingular(String word) {
|
||||||
|
return word.endsWith("ies") ? word.substring(0, word.length() - 3) + "y" : word.endsWith("s") ? word.substring(0, word.length() - 1) : word;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,9 +49,9 @@ public class HotRodMapStorageProviderFactory implements AmphibianProviderFactory
|
||||||
private static final Logger LOG = Logger.getLogger(HotRodMapStorageProviderFactory.class);
|
private static final Logger LOG = Logger.getLogger(HotRodMapStorageProviderFactory.class);
|
||||||
|
|
||||||
private final static DeepCloner CLONER = new DeepCloner.Builder()
|
private final static DeepCloner CLONER = new DeepCloner.Builder()
|
||||||
.constructorDC(MapClientEntity.class, HotRodClientEntityDelegate::new)
|
.constructor(MapClientEntity.class, HotRodClientEntityDelegate::new)
|
||||||
.constructor(MapProtocolMapperEntity.class, HotRodProtocolMapperEntityDelegate::new)
|
.constructor(MapProtocolMapperEntity.class, HotRodProtocolMapperEntityDelegate::new)
|
||||||
.constructorDC(MapGroupEntity.class, HotRodGroupEntityDelegate::new)
|
.constructor(MapGroupEntity.class, HotRodGroupEntityDelegate::new)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public static final Map<Class<?>, HotRodEntityDescriptor<?, ?>> ENTITY_DESCRIPTOR_MAP = new HashMap<>();
|
public static final Map<Class<?>, HotRodEntityDescriptor<?, ?>> ENTITY_DESCRIPTOR_MAP = new HashMap<>();
|
||||||
|
|
|
@ -42,6 +42,8 @@ import org.hibernate.annotations.TypeDefs;
|
||||||
import org.keycloak.models.map.client.MapClientEntity.AbstractClientEntity;
|
import org.keycloak.models.map.client.MapClientEntity.AbstractClientEntity;
|
||||||
import org.keycloak.models.map.client.MapProtocolMapperEntity;
|
import org.keycloak.models.map.client.MapProtocolMapperEntity;
|
||||||
import static org.keycloak.models.map.storage.jpa.client.JpaClientMapStorage.SUPPORTED_VERSION;
|
import static org.keycloak.models.map.storage.jpa.client.JpaClientMapStorage.SUPPORTED_VERSION;
|
||||||
|
|
||||||
|
import org.keycloak.models.map.common.DeepCloner;
|
||||||
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
import org.keycloak.models.map.storage.jpa.hibernate.jsonb.JsonbType;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
|
@ -87,6 +89,10 @@ public class JpaClientEntity extends AbstractClientEntity implements Serializabl
|
||||||
this.metadata = new JpaClientMetadata();
|
this.metadata = new JpaClientMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JpaClientEntity(DeepCloner cloner) {
|
||||||
|
this.metadata = new JpaClientMetadata(cloner);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by hibernate when calling cb.construct from read(QueryParameters) method.
|
* Used by hibernate when calling cb.construct from read(QueryParameters) method.
|
||||||
* It is used to select client without metadata(json) field.
|
* It is used to select client without metadata(json) field.
|
||||||
|
|
|
@ -18,9 +18,18 @@ package org.keycloak.models.map.storage.jpa.client.entity;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import org.keycloak.models.map.client.MapClientEntityImpl;
|
import org.keycloak.models.map.client.MapClientEntityImpl;
|
||||||
|
import org.keycloak.models.map.common.DeepCloner;
|
||||||
|
|
||||||
public class JpaClientMetadata extends MapClientEntityImpl implements Serializable {
|
public class JpaClientMetadata extends MapClientEntityImpl implements Serializable {
|
||||||
|
|
||||||
|
public JpaClientMetadata(DeepCloner cloner) {
|
||||||
|
super(cloner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JpaClientMetadata() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
private Integer entityVersion;
|
private Integer entityVersion;
|
||||||
|
|
||||||
public Integer getEntityVersion() {
|
public Integer getEntityVersion() {
|
||||||
|
|
|
@ -111,7 +111,6 @@ public class DeepCloner {
|
||||||
* Builder for the {@code DeepCloner} helper class.
|
* Builder for the {@code DeepCloner} helper class.
|
||||||
*/
|
*/
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private final Map<Class<?>, Supplier<?>> parameterlessConstructors = new HashMap<>();
|
|
||||||
private final Map<Class<?>, Function<DeepCloner, ?>> constructors = new HashMap<>();
|
private final Map<Class<?>, Function<DeepCloner, ?>> constructors = new HashMap<>();
|
||||||
private final Map<Class<?>, Cloner<?>> clonersWithId = new HashMap<>(org.keycloak.models.map.common.AutogeneratedCloners.CLONERS_WITH_ID);
|
private final Map<Class<?>, Cloner<?>> clonersWithId = new HashMap<>(org.keycloak.models.map.common.AutogeneratedCloners.CLONERS_WITH_ID);
|
||||||
private final Map<Class<?>, Cloner<?>> clonersWithoutId = new HashMap<>(org.keycloak.models.map.common.AutogeneratedCloners.CLONERS_WITHOUT_ID);
|
private final Map<Class<?>, Cloner<?>> clonersWithoutId = new HashMap<>(org.keycloak.models.map.common.AutogeneratedCloners.CLONERS_WITHOUT_ID);
|
||||||
|
@ -124,7 +123,7 @@ public class DeepCloner {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public DeepCloner build() {
|
public DeepCloner build() {
|
||||||
return new DeepCloner(parameterlessConstructors, constructors, delegateCreators, entityFieldDelegateCreators, clonersWithId, clonersWithoutId, genericCloner);
|
return new DeepCloner(constructors, delegateCreators, entityFieldDelegateCreators, clonersWithId, clonersWithoutId, genericCloner);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <V> void forThisClassAndAllMarkedParentsAndInterfaces(Class<V> rootClazz, Consumer<Class<?>> action) {
|
private <V> void forThisClassAndAllMarkedParentsAndInterfaces(Class<V> rootClazz, Consumer<Class<?>> action) {
|
||||||
|
@ -149,22 +148,6 @@ public class DeepCloner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a method, often a constructor, that instantiates a record of type {@code V}.
|
|
||||||
*
|
|
||||||
* @param <V> Class or interface that would be instantiated by the given methods
|
|
||||||
* @param clazz Class or interface that would be instantiated by the given methods
|
|
||||||
* @param constructorNoParameters Parameterless function that creates a new instance of class {@code V}.
|
|
||||||
* If {@code null}, parameterless constructor is not available.
|
|
||||||
* @return This builder.
|
|
||||||
*/
|
|
||||||
public <V> Builder constructor(Class<V> clazz, Supplier<? extends V> constructorNoParameters) {
|
|
||||||
if (constructorNoParameters != null) {
|
|
||||||
forThisClassAndAllMarkedParentsAndInterfaces(clazz, cl -> this.parameterlessConstructors.put(cl, constructorNoParameters));
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a method, often a constructor, that instantiates a record of type {@code V}.
|
* Adds a method, often a constructor, that instantiates a record of type {@code V}.
|
||||||
*
|
*
|
||||||
|
@ -174,7 +157,7 @@ public class DeepCloner {
|
||||||
* If {@code null}, such a single-parameter constructor is not available.
|
* If {@code null}, such a single-parameter constructor is not available.
|
||||||
* @return This builder.
|
* @return This builder.
|
||||||
*/
|
*/
|
||||||
public <V> Builder constructorDC(Class<V> clazz, Function<DeepCloner, ? extends V> constructor) {
|
public <V> Builder constructor(Class<V> clazz, Function<DeepCloner, ? extends V> constructor) {
|
||||||
if (constructor != null) {
|
if (constructor != null) {
|
||||||
forThisClassAndAllMarkedParentsAndInterfaces(clazz, cl -> this.constructors.put(cl, constructor));
|
forThisClassAndAllMarkedParentsAndInterfaces(clazz, cl -> this.constructors.put(cl, constructor));
|
||||||
}
|
}
|
||||||
|
@ -267,7 +250,6 @@ public class DeepCloner {
|
||||||
|
|
||||||
private static final Logger LOG = Logger.getLogger(DeepCloner.class);
|
private static final Logger LOG = Logger.getLogger(DeepCloner.class);
|
||||||
|
|
||||||
private final Map<Class<?>, Supplier<?>> parameterlessConstructors;
|
|
||||||
private final Map<Class<?>, Function<DeepCloner, ?>> constructors;
|
private final Map<Class<?>, Function<DeepCloner, ?>> constructors;
|
||||||
private final Map<Class<?>, Cloner<?>> clonersWithId;
|
private final Map<Class<?>, Cloner<?>> clonersWithId;
|
||||||
private final Map<Class<?>, Cloner<?>> clonersWithoutId;
|
private final Map<Class<?>, Cloner<?>> clonersWithoutId;
|
||||||
|
@ -276,14 +258,12 @@ public class DeepCloner {
|
||||||
private final Cloner<?> genericCloner;
|
private final Cloner<?> genericCloner;
|
||||||
private final Map<Class<?>, Object> emptyInstances = new HashMap<>(AutogeneratedCloners.EMPTY_INSTANCES);
|
private final Map<Class<?>, Object> emptyInstances = new HashMap<>(AutogeneratedCloners.EMPTY_INSTANCES);
|
||||||
|
|
||||||
private DeepCloner(Map<Class<?>, Supplier<?>> parameterlessConstructors,
|
private DeepCloner(Map<Class<?>, Function<DeepCloner, ?>> constructors,
|
||||||
Map<Class<?>, Function<DeepCloner, ?>> constructors,
|
|
||||||
Map<Class<?>, DelegateCreator<?>> delegateCreators,
|
Map<Class<?>, DelegateCreator<?>> delegateCreators,
|
||||||
Map<Class<?>, EntityFieldDelegateCreator<?>> entityFieldDelegateCreators,
|
Map<Class<?>, EntityFieldDelegateCreator<?>> entityFieldDelegateCreators,
|
||||||
Map<Class<?>, Cloner<?>> clonersWithId,
|
Map<Class<?>, Cloner<?>> clonersWithId,
|
||||||
Map<Class<?>, Cloner<?>> clonersWithoutId,
|
Map<Class<?>, Cloner<?>> clonersWithoutId,
|
||||||
Cloner<?> genericCloner) {
|
Cloner<?> genericCloner) {
|
||||||
this.parameterlessConstructors = parameterlessConstructors;
|
|
||||||
this.constructors = constructors;
|
this.constructors = constructors;
|
||||||
this.clonersWithId = clonersWithId;
|
this.clonersWithId = clonersWithId;
|
||||||
this.clonersWithoutId = clonersWithoutId;
|
this.clonersWithoutId = clonersWithoutId;
|
||||||
|
@ -377,16 +357,10 @@ public class DeepCloner {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Function<DeepCloner, V> c = (Function<DeepCloner, V>) getFromClassRespectingHierarchy(this.constructors, clazz);
|
Function<DeepCloner, V> c = (Function<DeepCloner, V>) getFromClassRespectingHierarchy(this.constructors, clazz);
|
||||||
if (c == null) {
|
if (c == null) {
|
||||||
@SuppressWarnings("unchecked")
|
try {
|
||||||
Supplier<V> s = (Supplier<V>) getFromClassRespectingHierarchy(this.parameterlessConstructors, clazz);
|
res = clazz.newInstance();
|
||||||
if (s == null) {
|
} catch (InstantiationException | IllegalAccessException ex) {
|
||||||
try {
|
res = null;
|
||||||
res = clazz.newInstance();
|
|
||||||
} catch (InstantiationException | IllegalAccessException ex) {
|
|
||||||
res = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res = s.get();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
res = c.apply(this);
|
res = c.apply(this);
|
||||||
|
|
|
@ -80,10 +80,10 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide
|
||||||
|
|
||||||
private final static DeepCloner CLONER = new DeepCloner.Builder()
|
private final static DeepCloner CLONER = new DeepCloner.Builder()
|
||||||
.genericCloner(Serialization::from)
|
.genericCloner(Serialization::from)
|
||||||
.constructorDC(MapClientEntityImpl.class, MapClientEntityImpl::new)
|
.constructor(MapClientEntityImpl.class, MapClientEntityImpl::new)
|
||||||
.constructor(MapProtocolMapperEntity.class, MapProtocolMapperEntityImpl::new)
|
.constructor(MapProtocolMapperEntity.class, MapProtocolMapperEntityImpl::new)
|
||||||
.constructorDC(MapGroupEntityImpl.class, MapGroupEntityImpl::new)
|
.constructor(MapGroupEntityImpl.class, MapGroupEntityImpl::new)
|
||||||
.constructorDC(MapRoleEntityImpl.class, MapRoleEntityImpl::new)
|
.constructor(MapRoleEntityImpl.class, MapRoleEntityImpl::new)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private static final Map<String, StringKeyConvertor> KEY_CONVERTORS = new HashMap<>();
|
private static final Map<String, StringKeyConvertor> KEY_CONVERTORS = new HashMap<>();
|
||||||
|
|
|
@ -38,7 +38,7 @@ import static org.keycloak.models.map.common.DeepCloner.DUMB_CLONER;
|
||||||
public class MapClientEntityClonerTest {
|
public class MapClientEntityClonerTest {
|
||||||
|
|
||||||
private final static DeepCloner CLONER = new DeepCloner.Builder()
|
private final static DeepCloner CLONER = new DeepCloner.Builder()
|
||||||
.constructorDC(MapClientEntityImpl.class, MapClientEntityImpl::new)
|
.constructor(MapClientEntityImpl.class, MapClientEntityImpl::new)
|
||||||
.constructor(MapProtocolMapperEntity.class, MapProtocolMapperEntityImpl::new)
|
.constructor(MapProtocolMapperEntity.class, MapProtocolMapperEntityImpl::new)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue