All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.babyfish.model.jpa.instrument.spi.AbstractObjectModel4JPAReplacer Maven / Gradle / Ivy

Go to download

This tool project supports some maven plugins of BabyFish, it shouldn't used by the user directly.

The newest version!
/*
 * BabyFish, Object Model Framework for Java and JPA.
 * https://github.com/babyfish-ct/babyfish
 *
 * Copyright (c) 2008-2016, Tao Chen
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * Please visit "http://opensource.org/licenses/LGPL-3.0" to know more.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 */
package org.babyfish.model.jpa.instrument.spi;

import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.babyfish.collection.ArrayList;
import org.babyfish.collection.HashMap;
import org.babyfish.collection.MACollections;
import org.babyfish.lang.I18N;
import org.babyfish.lang.Nulls;
import org.babyfish.lang.bytecode.ASMUtils;
import org.babyfish.lang.instrument.IllegalClassException;
import org.babyfish.model.instrument.metadata.MetadataClass;
import org.babyfish.model.instrument.metadata.MetadataProperty;
import org.babyfish.model.instrument.spi.AbstractModelReplacer;
import org.babyfish.model.instrument.spi.Identifiers;
import org.babyfish.model.jpa.instrument.impl.ObjectModel4JPAInstrumenter;
import org.babyfish.model.jpa.instrument.metadata.JPAMetadataProperty;
import org.babyfish.org.objectweb.asm.AnnotationVisitor;
import org.babyfish.org.objectweb.asm.ClassVisitor;
import org.babyfish.org.objectweb.asm.MethodVisitor;
import org.babyfish.org.objectweb.asm.Opcodes;
import org.babyfish.org.objectweb.asm.tree.AnnotationNode;
import org.babyfish.org.objectweb.asm.tree.ClassNode;
import org.babyfish.org.objectweb.asm.tree.FieldNode;

/**
 * @author Tao Chen
 */
public abstract class AbstractObjectModel4JPAReplacer extends AbstractModelReplacer {

    protected AbstractObjectModel4JPAReplacer(
            ObjectModel4JPAInstrumenter instrumenter, 
            String className, 
            File classFile) {
        super(instrumenter, className, classFile, true);
    }

    @Override
    protected String getRuntimeModelClassImplDescriptor() {
        return ASMConstants.JPA_MODEL_CLASS_IMPL_DESCRIPTOR;
    }

    @Override
    protected String getRuntimeModelPropertyImplDescriptor() {
        return ASMConstants.JPA_MODEL_PROPERTY_IMPL_DESCRIPTOR;
    }

    @Override
    protected String[] getAdditionalModelPropertyImplConstructorArgDescriptors() {
        return new String[] { ASMConstants.JPA_SCALAR_TYPE_DESCRIPTOR, "Z" };
    }

    @Override
    protected void loadAdditionalModelPropertyImplConstructorArgs(
            MethodVisitor mv, 
            MetadataProperty metadataProperty) {
        JPAMetadataProperty jpaMetadataProperty = (JPAMetadataProperty)metadataProperty;
        ASMUtils.visitEnumLdc(mv, jpaMetadataProperty.getScalarType());
        mv.visitInsn(jpaMetadataProperty.isInverse() ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
    }

    @Override
    protected void visitGetObjectModelAnnotations(MethodVisitor mv) {
        mv.visitAnnotation(ASMConstants.TRANSIENT_DESCRIPTOR, true);
    }

    @Override
    protected ClassVisitor onCreateClassAdapter(ClassVisitor cv) {
        return this.new ClassAdapter(super.onCreateClassAdapter(cv));
    }
    
    protected boolean isJPAAnnotation(String annotationDesc) {
        int lastSlashIndex = annotationDesc.lastIndexOf('/');
        if (lastSlashIndex != -1) {
            String packageName = annotationDesc.substring(1, lastSlashIndex);
            return packageName.equals("javax/persistence");
        }
        return false;
    }
    
    private class ClassAdapter extends ClassVisitor {

        private Map declaredFieldNodes;
        
        private Map getters;
        
        private Map setters;
        
        ClassAdapter(ClassVisitor cv) {
            
            super(Opcodes.ASM5, cv);
        
            MetadataClass metadataClass = AbstractObjectModel4JPAReplacer.this.getMetadataClass();
            
            ObjectModel4JPAInstrumenter instrumenter = 
                    (ObjectModel4JPAInstrumenter)AbstractObjectModel4JPAReplacer.this.getInstrumenter();
            ClassNode classNode = instrumenter.getClassNode(metadataClass.getName());
            if (Nulls.isNullOrEmpty(classNode.fields)) {
                this.declaredFieldNodes = MACollections.emptyMap();
            } else {
                Map fMap = new HashMap<>((classNode.fields.size() * 4 + 2) / 3);
                for (FieldNode fieldNode : classNode.fields) {
                    fMap.put(fieldNode.name, fieldNode);
                }
                this.declaredFieldNodes = fMap;
            }
            
            int capacity = (metadataClass.getDeclaredProperties().size() * 4 + 2) / 3;
            Map gMap = new HashMap<>(capacity);
            Map sMap = new HashMap<>(capacity);
            for (MetadataProperty metadataProperty : metadataClass.getDeclaredProperties().values()) {
                gMap.put(Identifiers.getterName(metadataProperty), metadataProperty);
                sMap.put(Identifiers.setterName(metadataProperty), metadataProperty);
            }
            this.getters = gMap;
            this.setters = sMap;
        }

        @Override
        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
            if (desc.charAt(1) == ')') {
                MetadataProperty metadataProperty = this.getters.get(name);
                if (metadataProperty != null && metadataProperty.getDescriptor().equals(desc.substring(2))) {
                    if ((access & Opcodes.ACC_STATIC) != 0) {
                        throw new IllegalClassException(
                                getterCannotBeStatic(
                                        metadataProperty.getDeclaringClass(), 
                                        ASMUtils.toClassName(metadataProperty.getDescriptor()) + ' ' + name + "()"
                                )
                        );
                    }
                    MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
                    FieldNode fieldNode = this.declaredFieldNodes.get(metadataProperty.getName());
                    if (fieldNode.visibleAnnotations != null) {
                        for (AnnotationNode annotationNode : fieldNode.visibleAnnotations) {
                            if (AbstractObjectModel4JPAReplacer.this.isJPAAnnotation(annotationNode.desc)) {
                                AnnotationVisitor av = mv.visitAnnotation(annotationNode.desc, true);
                                annotationNode.accept(av);
                                av.visitEnd();
                            }
                        }
                    }
                    this.getters.remove(name);
                    return mv;
                }
            } else if (desc.endsWith(")V")) {
                MetadataProperty metadataProperty = this.setters.get(name);
                if (metadataProperty != null && metadataProperty.getDescriptor().equals(desc.substring(1, desc.length() - 2))) {
                    if ((access & Opcodes.ACC_STATIC) != 0) {
                        throw new IllegalClassException(
                                setterCannotBeStatic(
                                        metadataProperty.getDeclaringClass(), 
                                        "void " + name + '(' + ASMUtils.toClassName(metadataProperty.getDescriptor()) + ')'
                                )
                        );
                    }
                    this.setters.remove(name);
                }
            }
            return super.visitMethod(access, name, desc, signature, exceptions);
        }

        @Override
        public void visitEnd() {
            if (!this.getters.isEmpty()) {
                List fieldNames = new ArrayList<>(this.getters.size());
                for (MetadataProperty metadataProperty : this.getters.values()) {
                    fieldNames.add(metadataProperty.getName());
                }
                throw new IllegalClassException(
                        missGetters(
                                AbstractObjectModel4JPAReplacer.this.getMetadataClass(),
                                fieldNames
                        )
                );
            }
            if (!this.setters.isEmpty()) {
                List fieldNames = new ArrayList<>(this.setters.size());
                for (MetadataProperty metadataProperty : this.setters.values()) {
                    fieldNames.add(metadataProperty.getName());
                }
                throw new IllegalClassException(
                        missSetters(
                                AbstractObjectModel4JPAReplacer.this.getMetadataClass(),
                                fieldNames
                        )
                );
            }
            super.visitEnd();
        }
    }
    
    @I18N
    private static native String getterCannotBeStatic(MetadataClass declaringClass, String method);
    
    @I18N
    private static native String setterCannotBeStatic(MetadataClass declaringClass, String method);
    
    @I18N
    private static native String missGetters(MetadataClass declaringClass, Collection fieldNames);
    
    @I18N
    private static native String missSetters(MetadataClass declaringClass, Collection fieldNames);
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy