001package io.ebean.enhance.common;
002
003import io.ebean.enhance.asm.AnnotationVisitor;
004import io.ebean.enhance.asm.ClassVisitor;
005import io.ebean.enhance.asm.FieldVisitor;
006import io.ebean.enhance.asm.MethodVisitor;
007import io.ebean.enhance.asm.Opcodes;
008
009/**
010 * Used by ClassMetaReader to read information about a class.
011 * <p>
012 * Reads the information by visiting the byte codes rather than using
013 * ClassLoader. This gets around issues where the annotations are not seen
014 * (silently ignored) if they are not in the class path.
015 * </p>
016 */
017public class ClassMetaReaderVisitor extends ClassVisitor implements EnhanceConstants {
018
019        private final ClassMeta classMeta;
020
021        private final boolean readMethodMeta;
022        
023        public ClassMetaReaderVisitor(boolean readMethodMeta, EnhanceContext context) {
024                super(Opcodes.ASM6);
025                this.readMethodMeta = readMethodMeta;
026                this.classMeta = context.createClassMeta();
027        }
028
029        public ClassMeta getClassMeta() {
030                return classMeta;
031        }
032
033        public boolean isLog(int level) {
034                return classMeta.isLog(level);
035        }
036
037        public void log(String msg) {
038                classMeta.log(msg);
039        }
040
041        /**
042         * Create the class definition replacing the className and super class.
043         */
044        @Override
045        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
046
047                classMeta.setClassName(name, superName);
048                super.visit(version, access, name, signature, superName, interfaces);
049        }
050
051        @Override
052        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
053                
054                classMeta.addClassAnnotation(desc);
055                
056                AnnotationVisitor av = super.visitAnnotation(desc, visible);
057                
058                if (desc.equals(AVAJE_TRANSACTIONAL_ANNOTATION)) {
059                        // we have class level Transactional annotation
060                        // which will act as default for all methods in this class
061                        return new AnnotationInfoVisitor(null, classMeta.getAnnotationInfo(), av);
062
063                } else {
064                        return av;
065                }
066        }
067
068        /**
069         * The ebeanIntercept field is added once but thats all. Note the other
070         * fields are defined in the superclass.
071         */
072        @Override
073        public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
074
075                if ((access & Opcodes.ACC_STATIC) != 0) {
076                        // no interception of static fields
077                        if (isLog(2)) {
078                                log("Skip static field " + name);
079                        }
080                        return super.visitField(access, name, desc, signature, value);
081                }
082    if ((access & Opcodes.ACC_TRANSIENT) != 0) {
083      if (isLog(2)) {
084        log("Skip transient field " + name);
085      }
086      // no interception of transient fields
087      return super.visitField(access, name, desc, signature, value);
088    }
089                return classMeta.createLocalFieldVisitor(name, desc);
090        }
091
092        /**
093         * Look for equals/hashCode implementations.
094         */
095        @Override
096        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
097
098                boolean staticAccess = ((access & Opcodes.ACC_STATIC) != 0);
099                
100    if (name.equals("hashCode") && desc.equals("()I")) {
101      classMeta.setHasEqualsOrHashcode(true);
102    }
103
104    if (name.equals("equals") && desc.equals("(Ljava/lang/Object;)Z")) {
105      classMeta.setHasEqualsOrHashcode(true);
106    }
107                
108                MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
109                if (!staticAccess && readMethodMeta){
110                        return classMeta.createMethodVisitor(mv, access, name, desc);
111                        
112                } else {
113                        // not interested in the methods...
114                        return mv;
115                }
116        }
117
118}