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}