private static void writeDeepCloneCollectionInstructions()

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);
    }