private void addCopyKeyFieldsToObjectIdMethod()

in openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java [2170:2452]


    private void addCopyKeyFieldsToObjectIdMethod(boolean fieldManager) throws NoSuchMethodException {

        // public void pcCopyKeyFieldsToObjectId (ObjectIdFieldSupplier fs, Object oid)
        String mDesc = fieldManager
                ? Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(OIDFSTYPE), AsmHelper.TYPE_OBJECT)
                : Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.TYPE_OBJECT);
        MethodNode copyKFMeth = new MethodNode(Opcodes.ACC_PUBLIC,
                                               PRE + "CopyKeyFieldsToObjectId",
                                               mDesc,
                                               null, null);
        final ClassNode classNode = pc.getClassNode();
        classNode.methods.add(copyKFMeth);
        InsnList instructions = copyKFMeth.instructions;

        // single field identity always throws exception
        if (_meta.isOpenJPAIdentity()) {
            instructions.add(AsmHelper.throwException(INTERNEXCEP));
            return;
        }

        // call superclass method
        if (_meta.getPCSuperclass() != null && !getCreateSubclass()) {
            instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
            instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st parameter object
            if (fieldManager) {
                instructions.add(new VarInsnNode(Opcodes.ALOAD, 2)); // 2nd parameter object
            }
            instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
                                                Type.getInternalName(getType(_meta.getPCSuperclassMetaData())),
                                                PRE + "CopyKeyFieldsToObjectId",
                                                mDesc));
        }

        // Object id = oid;
        if (fieldManager) {
            instructions.add(new VarInsnNode(Opcodes.ALOAD, 2)); // 2nd parameter object
        }
        else {
            instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st parameter object
        }

        if (_meta.isObjectIdTypeShared()) {
            // oid = ((ObjectId) id).getId ();
            instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(ObjectId.class)));
            instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
                                                Type.getInternalName(ObjectId.class),
                                                "getId",
                                                Type.getMethodDescriptor(AsmHelper.TYPE_OBJECT)));
        }

        // <oid type> id = (<oid type>) oid;
        int nextFreeVarPos = (fieldManager) ? 3 : 2;
        int idVarPos = nextFreeVarPos++;

        Class oidType = _meta.getObjectIdType();
        instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(oidType)));
        instructions.add(new VarInsnNode(Opcodes.ASTORE, idVarPos));

        // int inherited = pcInheritedFieldCount;
        int inherited = 0;
        if (fieldManager) {
            instructions.add(new FieldInsnNode(Opcodes.GETSTATIC, classNode.name, INHERIT, Type.INT_TYPE.getDescriptor()));
            inherited = nextFreeVarPos++;
            instructions.add(new VarInsnNode(Opcodes.ISTORE, inherited));
        }

        // id.<field> = fs.fetch<type>Field (<index>); or...
        // id.<field> = pc.<field>;
        FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
                : _meta.getDeclaredFields();

        // If optimizeIdCopy is enabled and not a field manager method, try to
        // optimize the copyTo by using a public constructor instead of reflection
        if (_optimizeIdCopy) {
            ArrayList<Integer> pkfields = optimizeIdCopy(oidType, fmds);
            if (pkfields != null) {
                // search for a constructor on the IdClass that can be used
                // to construct the IdClass
                int[] parmOrder = getIdClassConstructorParmOrder(oidType, pkfields, fmds);
                if (parmOrder != null) {
                    // If using a field manager, values must be loaded into locals so they can be properly ordered
                    // as constructor parameters.
                    int[] localIndexes = new int[fmds.length];

                    if (fieldManager) {
                        for (int k = 0; k < fmds.length; k++) {
                            if (!fmds[k].isPrimaryKey()) {
                                continue;
                            }
                            instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // 1st param
                            instructions.add(AsmHelper.getLoadConstantInsn(k));
                            instructions.add(new VarInsnNode(Opcodes.ILOAD, inherited));
                            instructions.add(new InsnNode(Opcodes.IADD));

                            final Method fieldSupplierMethod = getFieldSupplierMethod(fmds[k].getObjectIdFieldType());
                            instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
                                                                Type.getInternalName(fieldSupplierMethod.getDeclaringClass()),
                                                                fieldSupplierMethod.getName(),
                                                                Type.getMethodDescriptor(fieldSupplierMethod)));
                            localIndexes[k] = nextFreeVarPos++;
                            instructions.add(new VarInsnNode(AsmHelper.getStoreInsn(fmds[k].getObjectIdFieldType()), localIndexes[k]));
                        }
                    }

                    // found a matching constructor.  parm array is constructor parm order
                    instructions.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(oidType)));
                    instructions.add(new InsnNode(Opcodes.DUP));

                    // build the parm list in order
                    Class<?>[] clsArgs = new Class<?>[parmOrder.length];
                    for (int i = 0; i < clsArgs.length; i++) {
                        int parmIndex = parmOrder[i];
                        clsArgs[i] = fmds[parmIndex].getObjectIdFieldType();
                        if (!fieldManager) {
                            instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
                            addGetManagedValueCode(classNode, instructions, fmds[parmIndex], true);
                        }
                        else {
                            // Load constructor parameters in appropriate order
                            instructions.add(new VarInsnNode(AsmHelper.getLoadInsn(fmds[parmIndex].getObjectIdFieldType()), localIndexes[parmIndex]));

                            if (fmds[parmIndex].getObjectIdFieldTypeCode() == JavaTypes.OBJECT &&
                                    !fmds[parmIndex].getDeclaredType().isEnum()) {
                                instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(ObjectId.class)));
                                instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
                                                                    Type.getInternalName(ObjectId.class),
                                                                    "getId",
                                                                    Type.getMethodDescriptor(AsmHelper.TYPE_OBJECT)));
                            }

                            // if the type of this field meta data is
                            // non-primitive and non-string, be sure to cast
                            // to the appropriate type.
                            if (!clsArgs[i].isPrimitive() && !clsArgs[i].getName().equals(String.class.getName())) {
                                instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(clsArgs[i])));
                            }
                        }
                    }

                    // invoke the public constructor to create a new local id
                    Type[] parms = Arrays.stream(clsArgs)
                            .map(Type::getType)
                            .toArray(Type[]::new);

                    instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
                                                        Type.getInternalName(oidType),
                                                        "<init>",
                                                        Type.getMethodDescriptor(Type.VOID_TYPE, parms)));


                    int retVarPos = inherited + fmds.length;
                    instructions.add(new VarInsnNode(Opcodes.ASTORE, retVarPos));

                    // swap out the app id with the new one
                    instructions.add(new VarInsnNode(Opcodes.ALOAD, fieldManager ? 2 : 1));
                    instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(ObjectId.class)));
                    instructions.add(new VarInsnNode(Opcodes.ALOAD, retVarPos));
                    instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
                                                        Type.getInternalName(ApplicationIds.class),
                                                        "setAppId",
                                                        Type.getMethodDescriptor(Type.VOID_TYPE,
                                                                                 Type.getType(ObjectId.class), AsmHelper.TYPE_OBJECT)));
                    instructions.add(new InsnNode(Opcodes.RETURN));
                    return;
                }
            }
        }

        Field field = null;
        Method setter = null;
        for (int i = 0; i < fmds.length; i++) {
            if (!fmds[i].isPrimaryKey()) {
                continue;
            }

            instructions.add(new VarInsnNode(Opcodes.ALOAD, idVarPos));

            String name = fmds[i].getName();
            Class<?> type = fmds[i].getObjectIdFieldType();
            boolean reflect = false;

            if (isFieldAccess(fmds[i])) {
                field = Reflection.findField(oidType, name, true);
                reflect = !Modifier.isPublic(field.getModifiers());
                if (reflect) {
                    instructions.add(AsmHelper.getLoadConstantInsn(oidType));
                    instructions.add(AsmHelper.getLoadConstantInsn(name));
                    instructions.add(AsmHelper.getLoadConstantInsn(true));
                    instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
                                                        Type.getInternalName(Reflection.class),
                                                        "findField",
                                                        Type.getMethodDescriptor(Type.getType(Field.class), Type.getType(Class.class),
                                                                                 Type.getType(String.class), Type.BOOLEAN_TYPE)));
                }
            }
            else {
                setter = Reflection.findSetter(oidType, name, type, true);
                reflect = !Modifier.isPublic(setter.getModifiers());
                if (reflect) {
                    instructions.add(AsmHelper.getLoadConstantInsn(oidType));
                    instructions.add(AsmHelper.getLoadConstantInsn(name));
                    instructions.add(AsmHelper.getLoadConstantInsn(type));
                    instructions.add(AsmHelper.getLoadConstantInsn(true));
                    instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
                                                        Type.getInternalName(Reflection.class),
                                                        "findSetter",
                                                        Type.getMethodDescriptor(Type.getType(Method.class),
                                                                                 Type.getType(Class.class),
                                                                                 Type.getType(String.class),
                                                                                 Type.getType(Class.class),
                                                                                 Type.BOOLEAN_TYPE)));
                }
            }

            if (fieldManager) {
                instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));  // 1st param
                instructions.add(AsmHelper.getLoadConstantInsn(i));
                instructions.add(new VarInsnNode(Opcodes.ILOAD, inherited));
                instructions.add(new InsnNode(Opcodes.IADD));

                final Method fieldSupplierMethod = getFieldSupplierMethod(type);
                instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
                                                    Type.getInternalName(fieldSupplierMethod.getDeclaringClass()),
                                                    fieldSupplierMethod.getName(),
                                                    Type.getMethodDescriptor(fieldSupplierMethod)));


                if (fmds[i].getObjectIdFieldTypeCode() == JavaTypes.OBJECT && !fmds[i].getDeclaredType().isEnum()) {
                    instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(ObjectId.class)));
                    instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
                                                        Type.getInternalName(ObjectId.class),
                                                        "getId",
                                                        Type.getMethodDescriptor(AsmHelper.TYPE_OBJECT)));
                }

                // if the type of this field meta data is
                // non-primitive and non-string, be sure to cast
                // to the appropriate type.
                if (!reflect && !type.isPrimitive() && !type.getName().equals(String.class.getName())) {
                    instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(type)));
                }
            }
            else {
                instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
                addGetManagedValueCode(classNode, instructions, fmds[i], true);

                // get id/pk from pc instance
                if (fmds[i].getDeclaredTypeCode() == JavaTypes.PC) {
                    addExtractObjectIdFieldValueCode(classNode, instructions, fmds[i], nextFreeVarPos++);
                }
            }

            if (reflect && field != null) {
                instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
                                                    Type.getInternalName(Reflection.class),
                                                    "set",
                                                    Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.TYPE_OBJECT, Type.getType(Field.class),
                                                                             (type.isPrimitive()) ? Type.getType(type) : AsmHelper.TYPE_OBJECT)));

            }
            else if (reflect) {
                instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
                                                    Type.getInternalName(Reflection.class),
                                                    "set",
                                                    Type.getMethodDescriptor(Type.VOID_TYPE, AsmHelper.TYPE_OBJECT, Type.getType(Method.class),
                                                                             (type.isPrimitive()) ? Type.getType(type) : AsmHelper.TYPE_OBJECT)));
            }
            else if (field != null) {
                instructions.add(new FieldInsnNode(Opcodes.PUTFIELD,
                                                   Type.getInternalName(field.getDeclaringClass()),
                                                   field.getName(),
                                                   Type.getDescriptor(field.getType())));
            }
            else {
                instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
                                                    Type.getInternalName(setter.getDeclaringClass()),
                                                    setter.getName(),
                                                    Type.getMethodDescriptor(setter)));
            }

        }
        instructions.add(new InsnNode(Opcodes.RETURN));
    }