in core/optaplanner-core-impl/src/main/java/org/optaplanner/core/impl/domain/solution/cloner/gizmo/GizmoSolutionClonerImplementor.java [661:754]
private static void writeDeepCloneMapInstructions(BytecodeCreator bytecodeCreator,
GizmoSolutionOrEntityDescriptor solutionDescriptor,
Class<?> deeplyClonedFieldClass, java.lang.reflect.Type type, ResultHandle toClone,
AssignableResultHandle cloneResultHolder, ResultHandle createdCloneMap,
SortedSet<Class<?>> deepClonedClassesSortedSet) {
Class<?> holderClass = deeplyClonedFieldClass;
try {
holderClass.getConstructor();
} catch (NoSuchMethodException e) {
if (LinkedHashMap.class.isAssignableFrom(holderClass)) {
holderClass = LinkedHashMap.class;
} else if (ConcurrentHashMap.class.isAssignableFrom(holderClass)) {
holderClass = ConcurrentHashMap.class;
} else {
// Default to LinkedHashMap
holderClass = LinkedHashMap.class;
}
}
ResultHandle cloneMap;
ResultHandle size =
bytecodeCreator.invokeInterfaceMethod(MethodDescriptor.ofMethod(Map.class, "size", int.class), toClone);
ResultHandle entrySet = bytecodeCreator
.invokeInterfaceMethod(MethodDescriptor.ofMethod(Map.class, "entrySet", Set.class), toClone);
ResultHandle iterator = bytecodeCreator
.invokeInterfaceMethod(MethodDescriptor.ofMethod(Iterable.class, "iterator", Iterator.class), entrySet);
try {
holderClass.getConstructor(int.class);
cloneMap = bytecodeCreator.newInstance(MethodDescriptor.ofConstructor(holderClass, int.class), size);
} catch (NoSuchMethodException e) {
cloneMap = bytecodeCreator.newInstance(MethodDescriptor.ofConstructor(holderClass));
}
BytecodeCreator whileLoopBlock = bytecodeCreator.whileLoop(conditionBytecode -> {
ResultHandle hasNext = conditionBytecode
.invokeInterfaceMethod(MethodDescriptor.ofMethod(Iterator.class, "hasNext", boolean.class), iterator);
return conditionBytecode.ifTrue(hasNext);
}).block();
Class<?> keyClass;
Class<?> elementClass;
java.lang.reflect.Type keyType;
java.lang.reflect.Type elementClassType;
if (type instanceof ParameterizedType) {
// Assume Map follow Map<K,V> convention of second type argument = value class
keyType = ((ParameterizedType) type).getActualTypeArguments()[0];
elementClassType = ((ParameterizedType) type).getActualTypeArguments()[1];
if (elementClassType instanceof Class) {
elementClass = (Class<?>) elementClassType;
} else if (elementClassType instanceof ParameterizedType) {
elementClass = (Class<?>) ((ParameterizedType) elementClassType).getRawType();
} else {
throw new IllegalStateException("Unhandled type " + elementClassType + ".");
}
if (keyType instanceof Class) {
keyClass = (Class<?>) keyType;
} else if (keyType instanceof ParameterizedType) {
keyClass = (Class<?>) ((ParameterizedType) keyType).getRawType();
} else {
throw new IllegalStateException("Unhandled type " + keyType + ".");
}
} else {
throw new IllegalStateException("Cannot infer element type for Map type (" + type + ").");
}
List<Class<?>> entitySubclasses = deepClonedClassesSortedSet.stream()
.filter(keyClass::isAssignableFrom).collect(Collectors.toList());
ResultHandle entry = whileLoopBlock
.invokeInterfaceMethod(MethodDescriptor.ofMethod(Iterator.class, "next", Object.class), iterator);
ResultHandle toCloneValue = whileLoopBlock
.invokeInterfaceMethod(MethodDescriptor.ofMethod(Map.Entry.class, "getValue", Object.class), entry);
final AssignableResultHandle clonedElement = whileLoopBlock.createVariable(elementClass);
writeDeepCloneInstructions(whileLoopBlock, solutionDescriptor,
elementClass, elementClassType, toCloneValue, clonedElement, createdCloneMap, deepClonedClassesSortedSet);
ResultHandle key = whileLoopBlock
.invokeInterfaceMethod(MethodDescriptor.ofMethod(Map.Entry.class, "getKey", Object.class), entry);
if (!entitySubclasses.isEmpty()) {
AssignableResultHandle keyCloneResultHolder = whileLoopBlock.createVariable(keyClass);
writeDeepCloneEntityOrFactInstructions(whileLoopBlock, solutionDescriptor, keyClass,
key, keyCloneResultHolder, createdCloneMap, deepClonedClassesSortedSet, false);
whileLoopBlock.invokeInterfaceMethod(
PUT_METHOD,
cloneMap, keyCloneResultHolder, clonedElement);
} else {
whileLoopBlock.invokeInterfaceMethod(
PUT_METHOD,
cloneMap, key, clonedElement);
}
bytecodeCreator.assign(cloneResultHolder, cloneMap);
}