001package io.ebean.enhance.querybean; 002 003import io.ebean.enhance.asm.AnnotationVisitor; 004import io.ebean.enhance.asm.ClassVisitor; 005import io.ebean.enhance.asm.ClassWriter; 006import io.ebean.enhance.asm.FieldVisitor; 007import io.ebean.enhance.asm.MethodVisitor; 008import io.ebean.enhance.asm.Opcodes; 009import io.ebean.enhance.common.AlreadyEnhancedException; 010import io.ebean.enhance.common.AnnotationInfo; 011import io.ebean.enhance.common.AnnotationInfoVisitor; 012import io.ebean.enhance.common.EnhanceContext; 013import io.ebean.enhance.common.NoEnhancementRequiredException; 014 015import static io.ebean.enhance.common.EnhanceConstants.INIT; 016 017/** 018 * Reads/visits the class and performs the appropriate enhancement if necessary. 019 */ 020public class TypeQueryClassAdapter extends ClassVisitor implements Constants { 021 022 private final EnhanceContext enhanceContext; 023 024 private boolean typeQueryRootBean; 025 private String className; 026 private String signature; 027 private ClassInfo classInfo; 028 029 private final AnnotationInfo annotationInfo = new AnnotationInfo(null); 030 031 public TypeQueryClassAdapter(ClassWriter cw, EnhanceContext enhanceContext) { 032 super(Opcodes.ASM7, cw); 033 this.enhanceContext = enhanceContext; 034 } 035 036 @Override 037 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 038 039 super.visit(version, access, name, signature, superName, interfaces); 040 this.typeQueryRootBean = TQ_ROOT_BEAN.equals(superName); 041 this.className = name; 042 this.signature = signature; 043 this.classInfo = new ClassInfo(enhanceContext, name); 044 } 045 046 /** 047 * Extract and return the associated entity bean class from the signature. 048 */ 049 protected String getDomainClass() { 050 int posStart = signature.indexOf('<'); 051 int posEnd = signature.indexOf(';', posStart + 1); 052 return signature.substring(posStart + 2, posEnd); 053 } 054 055 /** 056 * Look for TypeQueryBean annotation. 057 */ 058 @Override 059 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 060 boolean queryBean = classInfo.checkTypeQueryAnnotation(desc); 061 AnnotationVisitor av = super.visitAnnotation(desc, visible); 062 return (queryBean) ? new AnnotationInfoVisitor(null, annotationInfo, av) : av; 063 } 064 065 @Override 066 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { 067 if (classInfo.isAlreadyEnhanced()) { 068 throw new AlreadyEnhancedException(className); 069 } 070 if (classInfo.isTypeQueryBean()) { 071 // collect type query bean fields 072 classInfo.addField(access, name, desc, signature); 073 } 074 return super.visitField(access, name, desc, signature, value); 075 } 076 077 @Override 078 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 079 080 if (classInfo.isTypeQueryBean()) { 081 if ((access & Opcodes.ACC_STATIC) != 0) { 082 if (isLog(5)) { 083 log("ignore static methods on type query bean " + name + " " + desc); 084 } 085 return super.visitMethod(access, name, desc, signature, exceptions); 086 } 087 if (classInfo.addMarkerAnnotation()) { 088 addMarkerAnnotation(); 089 } 090 if (name.equals(INIT)) { 091 if (desc.equals("(Z)V")) { 092 // Constructor for alias initialises all the properties/fields 093 return new TypeQueryConstructorForAlias(classInfo, cv); 094 } 095 if (hasVersion()) { 096 // no enhancement on constructors required 097 return super.visitMethod(access, name, desc, signature, exceptions); 098 } 099 if (!typeQueryRootBean) { 100 return handleAssocBeanConstructor(access, name, desc, signature, exceptions); 101 } 102 return new TypeQueryConstructorAdapter(classInfo, getDomainClass(), cv, desc, signature); 103 } 104 if (!desc.startsWith("()L")) { 105 if (isLog(5)) { 106 log("leaving method as is - " + name + " " + desc + " " + signature); 107 } 108 return super.visitMethod(access, name, desc, signature, exceptions); 109 } 110 MethodDesc methodDesc = new MethodDesc(access, name, desc, signature, exceptions); 111 if (methodDesc.isGetter()) { 112 if (isLog(3)) { 113 log("overwrite getter method - " + name + " " + desc + " " + signature); 114 } 115 return new TypeQueryGetterAdapter(cv, classInfo, methodDesc); 116 } 117 } 118 119 if (isLog(8)) { 120 log("... checking method " + name + " " + desc); 121 } 122 MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 123 return new MethodAdapter(mv, enhanceContext, classInfo, name); 124 } 125 126 /** 127 * Return true if the TypeQueryBean has a value attribute with version description. 128 * True means we are using 11.39+ query bean generation. 129 */ 130 private boolean hasVersion() { 131 return annotationInfo.getValue("value") != null; 132 } 133 134 /** 135 * Handle the constructors for assoc type query beans. 136 */ 137 private MethodVisitor handleAssocBeanConstructor(int access, String name, String desc, String signature, String[] exceptions) { 138 139 if (desc.equals(ASSOC_BEAN_BASIC_CONSTRUCTOR_DESC)) { 140 classInfo.setHasBasicConstructor(); 141 return new TypeQueryAssocBasicConstructor(classInfo, cv, desc, signature); 142 } 143 if (desc.equals(ASSOC_BEAN_MAIN_CONSTRUCTOR_DESC)) { 144 classInfo.setHasMainConstructor(); 145 return new TypeQueryAssocMainConstructor(classInfo, cv, desc, signature); 146 } 147 // leave as is 148 return super.visitMethod(access, name, desc, signature, exceptions); 149 } 150 151 @Override 152 public void visitEnd() { 153 if (classInfo.isAlreadyEnhanced()) { 154 throw new AlreadyEnhancedException(className); 155 } 156 157 if (classInfo.isTypeQueryBean()) { 158 if (!typeQueryRootBean) { 159 classInfo.addAssocBeanExtras(cv); 160 } 161 TypeQueryAddMethods.add(cv, classInfo, typeQueryRootBean); 162 if (isLog(2)) { 163 classInfo.log("enhanced as type query bean"); 164 } 165 } else if (classInfo.isTypeQueryUser()) { 166 if (isLog(1)) { 167 classInfo.log("enhanced - getfield calls replaced"); 168 } 169 } else { 170 throw new NoEnhancementRequiredException("Not a type bean or caller of type beans"); 171 } 172 super.visitEnd(); 173 } 174 175 /** 176 * Add the marker annotation so that we don't enhance the type query bean twice. 177 */ 178 private void addMarkerAnnotation() { 179 180 if (isLog(4)) { 181 log("... adding marker annotation"); 182 } 183 AnnotationVisitor av = cv.visitAnnotation(ANNOTATION_ALREADY_ENHANCED_MARKER, true); 184 if (av != null) { 185 av.visitEnd(); 186 } 187 } 188 189 public boolean isLog(int level) { 190 return enhanceContext.isLog(level); 191 } 192 193 public void log(String msg) { 194 enhanceContext.log(className, msg); 195 } 196}