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