in core/optaplanner-core-impl/src/main/java/org/optaplanner/core/impl/domain/solution/cloner/gizmo/GizmoSolutionClonerImplementor.java [558:643]
private static void writeDeepCloneCollectionInstructions(BytecodeCreator bytecodeCreator,
GizmoSolutionOrEntityDescriptor solutionDescriptor,
Class<?> deeplyClonedFieldClass, java.lang.reflect.Type type, ResultHandle toClone,
AssignableResultHandle cloneResultHolder, ResultHandle createdCloneMap,
SortedSet<Class<?>> deepClonedClassesSortedSet) {
// Clone collection
AssignableResultHandle cloneCollection = bytecodeCreator.createVariable(deeplyClonedFieldClass);
ResultHandle size = bytecodeCreator
.invokeInterfaceMethod(MethodDescriptor.ofMethod(Collection.class, "size", int.class), toClone);
if (List.class.isAssignableFrom(deeplyClonedFieldClass)) {
bytecodeCreator.assign(cloneCollection,
bytecodeCreator.newInstance(MethodDescriptor.ofConstructor(ArrayList.class, int.class), size));
} else if (Set.class.isAssignableFrom(deeplyClonedFieldClass)) {
ResultHandle isSortedSet = bytecodeCreator.instanceOf(toClone, SortedSet.class);
BranchResult isSortedSetBranchResult = bytecodeCreator.ifTrue(isSortedSet);
BytecodeCreator isSortedSetBranch = isSortedSetBranchResult.trueBranch();
ResultHandle setComparator = isSortedSetBranch
.invokeInterfaceMethod(MethodDescriptor.ofMethod(SortedSet.class,
"comparator", Comparator.class), toClone);
isSortedSetBranch.assign(cloneCollection,
isSortedSetBranch.newInstance(MethodDescriptor.ofConstructor(TreeSet.class, Comparator.class),
setComparator));
BytecodeCreator isNotSortedSetBranch = isSortedSetBranchResult.falseBranch();
isNotSortedSetBranch.assign(cloneCollection,
isNotSortedSetBranch.newInstance(MethodDescriptor.ofConstructor(LinkedHashSet.class, int.class), size));
} else {
// field is probably of type collection
ResultHandle isSet = bytecodeCreator.instanceOf(toClone, Set.class);
BranchResult isSetBranchResult = bytecodeCreator.ifTrue(isSet);
BytecodeCreator isSetBranch = isSetBranchResult.trueBranch();
ResultHandle isSortedSet = isSetBranch.instanceOf(toClone, SortedSet.class);
BranchResult isSortedSetBranchResult = isSetBranch.ifTrue(isSortedSet);
BytecodeCreator isSortedSetBranch = isSortedSetBranchResult.trueBranch();
ResultHandle setComparator = isSortedSetBranch
.invokeInterfaceMethod(MethodDescriptor.ofMethod(SortedSet.class,
"comparator", Comparator.class), toClone);
isSortedSetBranch.assign(cloneCollection,
isSortedSetBranch.newInstance(MethodDescriptor.ofConstructor(TreeSet.class, Comparator.class),
setComparator));
BytecodeCreator isNotSortedSetBranch = isSortedSetBranchResult.falseBranch();
isNotSortedSetBranch.assign(cloneCollection,
isNotSortedSetBranch.newInstance(MethodDescriptor.ofConstructor(LinkedHashSet.class, int.class), size));
// Default to ArrayList
BytecodeCreator isNotSetBranch = isSetBranchResult.falseBranch();
isNotSetBranch.assign(cloneCollection,
isNotSetBranch.newInstance(MethodDescriptor.ofConstructor(ArrayList.class, int.class), size));
}
ResultHandle iterator = bytecodeCreator
.invokeInterfaceMethod(MethodDescriptor.ofMethod(Iterable.class, "iterator", Iterator.class), toClone);
BytecodeCreator whileLoopBlock = bytecodeCreator.whileLoop(conditionBytecode -> {
ResultHandle hasNext = conditionBytecode
.invokeInterfaceMethod(MethodDescriptor.ofMethod(Iterator.class, "hasNext", boolean.class), iterator);
return conditionBytecode.ifTrue(hasNext);
}).block();
Class<?> elementClass;
java.lang.reflect.Type elementClassType;
if (type instanceof ParameterizedType) {
// Assume Collection follow Collection<T> convention of first type argument = element class
elementClassType = ((ParameterizedType) type).getActualTypeArguments()[0];
if (elementClassType instanceof Class) {
elementClass = (Class<?>) elementClassType;
} else if (elementClassType instanceof ParameterizedType) {
elementClass = (Class<?>) ((ParameterizedType) elementClassType).getRawType();
} else {
throw new IllegalStateException("Unhandled type " + elementClassType + ".");
}
} else {
throw new IllegalStateException("Cannot infer element type for Collection type (" + type + ").");
}
// Odd case of member get and set being on different classes; will work as we only
// use get on the original and set on the clone.
ResultHandle next =
whileLoopBlock.invokeInterfaceMethod(MethodDescriptor.ofMethod(Iterator.class, "next", Object.class), iterator);
final AssignableResultHandle clonedElement = whileLoopBlock.createVariable(elementClass);
writeDeepCloneInstructions(whileLoopBlock, solutionDescriptor,
elementClass, elementClassType, next, clonedElement, createdCloneMap, deepClonedClassesSortedSet);
whileLoopBlock.invokeInterfaceMethod(MethodDescriptor.ofMethod(Collection.class, "add", boolean.class, Object.class),
cloneCollection,
clonedElement);
bytecodeCreator.assign(cloneResultHolder, cloneCollection);
}