private static void writeDeepCloneMapInstructions()

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