001package io.ebean.enhance.querybean;
002
003import io.ebean.enhance.common.EnhanceContext;
004import io.ebean.enhance.asm.ClassVisitor;
005import io.ebean.enhance.asm.Opcodes;
006
007import java.util.ArrayList;
008import java.util.HashSet;
009import java.util.List;
010import java.util.Set;
011
012/**
013 * Holds meta information for a class.
014 */
015public class ClassInfo implements Constants {
016
017  /**
018  * Detect entity beans as we will ignore them for this enhancement.
019  */
020  static Set<String> entityBeanAnnotations = new HashSet<>();
021  static {
022    entityBeanAnnotations.add(ENTITY_ANNOTATION);
023    entityBeanAnnotations.add(EMBEDDABLE_ANNOTATION);
024    entityBeanAnnotations.add(MAPPEDSUPERCLASS_ANNOTATION);
025  }
026
027
028  private final EnhanceContext enhanceContext;
029
030  private final String className;
031
032  private boolean addedMarkerAnnotation;
033
034  private boolean typeQueryBean;
035
036  private boolean typeQueryUser;
037
038  private boolean alreadyEnhanced;
039
040  private List<FieldInfo> fields;
041
042  private boolean hasBasicConstructor;
043
044  private boolean hasMainConstructor;
045
046  public ClassInfo(EnhanceContext enhanceContext, String className) {
047    this.enhanceContext = enhanceContext;
048    this.className = className;
049  }
050
051  /**
052  * Return the className.
053  */
054  public String getClassName() {
055    return className;
056  }
057
058  /**
059   * Return the short name of the class.
060   */
061  public String getShortName() {
062    int pos = className.lastIndexOf("/");
063    return className.substring(pos + 1);
064  }
065
066  /**
067  * Return true if the bean is already enhanced.
068  */
069  public boolean isAlreadyEnhanced() {
070    return alreadyEnhanced;
071  }
072
073  public boolean addMarkerAnnotation() {
074    if (addedMarkerAnnotation) {
075      return false;
076    }
077    addedMarkerAnnotation = true;
078    return true;
079  }
080
081  /**
082  * Return true if the bean is a type query bean.
083  */
084  public boolean isTypeQueryBean() {
085    return typeQueryBean;
086  }
087
088  /**
089  * Return true if the class is explicitly annotated with TypeQueryUser annotation.
090  */
091  public boolean isTypeQueryUser() {
092    return typeQueryUser;
093  }
094
095  /**
096   * Mark this class as having enhancement for query beans.
097   */
098  public void markTypeQueryEnhanced() {
099    typeQueryUser = true;
100  }
101
102  /**
103  * Check for the type query bean and type query user annotations.
104   */
105  public boolean checkTypeQueryAnnotation(String desc) {
106    if (isTypeQueryBeanAnnotation(desc)) {
107      typeQueryBean = true;
108    } else if (isAlreadyEnhancedAnnotation(desc)) {
109      alreadyEnhanced = true;
110    }
111    return typeQueryBean;
112  }
113
114  /**
115  * Add the type query bean field. We will create a 'property access' method for each field.
116  */
117  public void addField(int access, String name, String desc, String signature) {
118
119    if (((access & Opcodes.ACC_PUBLIC) != 0)) {
120      if (fields == null) {
121        fields = new ArrayList<>();
122      }
123      if ((access & Opcodes.ACC_STATIC) == 0) {
124        fields.add(new FieldInfo(this, name, desc, signature));
125      }
126    }
127  }
128
129  /**
130  * Return true if the annotation is the TypeQueryBean annotation.
131  */
132  private boolean isAlreadyEnhancedAnnotation(String desc) {
133    return ANNOTATION_ALREADY_ENHANCED_MARKER.equals(desc);
134  }
135
136  /**
137  * Return true if the annotation is the TypeQueryBean annotation.
138  */
139  private boolean isTypeQueryBeanAnnotation(String desc) {
140    return ANNOTATION_TYPE_QUERY_BEAN.equals(desc);
141  }
142
143  /**
144  * Return true if this is one of the entity bean annotations.
145  */
146  private boolean isEntityBeanAnnotation(String desc) {
147    return entityBeanAnnotations.contains(desc);
148  }
149
150  /**
151  * Return the fields for a type query bean.
152  */
153  public List<FieldInfo> getFields() {
154    return fields;
155  }
156
157  /**
158  * Note that a GETFIELD call has been replaced to method call.
159  */
160  public void addGetFieldIntercept(String owner, String name) {
161
162    if (isLog(4)) {
163      log("change getfield " + owner + " name:" + name);
164    }
165    typeQueryUser = true;
166  }
167
168  public boolean isLog(int level) {
169    return enhanceContext.isLog(level);
170  }
171
172  public void log(String msg) {
173    enhanceContext.log(className, msg);
174  }
175
176  /**
177  * There is a basic constructor on the assoc bean which is being overwritten (so don't need to add later).
178  */
179  public void setHasBasicConstructor() {
180    if (isLog(3)) {
181      log("replace assoc bean basic constructor");
182    }
183    hasBasicConstructor = true;
184  }
185
186  /**
187  * There is a main constructor on the assoc bean which is being overwritten (so don't need to add later).
188  */
189  public void setHasMainConstructor() {
190    if (isLog(3)) {
191      log("replace assoc bean main constructor");
192    }
193    hasMainConstructor = true;
194  }
195
196  /**
197  * Add fields and constructors to assoc type query beans as necessary.
198  */
199  public void addAssocBeanExtras(ClassVisitor cv) {
200
201    if (isLog(3)) {
202      String msg = "... add fields";
203      if (!hasBasicConstructor) {
204        msg += ", basic constructor";
205      }
206      if (!hasMainConstructor) {
207        msg += ", main constructor";
208      }
209      log(msg);
210    }
211
212    if (!hasBasicConstructor) {
213      // add the assoc bean basic constructor
214      new TypeQueryAssocBasicConstructor(this, cv, ASSOC_BEAN_BASIC_CONSTRUCTOR_DESC, ASSOC_BEAN_BASIC_SIG).visitCode();
215    }
216    if (!hasMainConstructor) {
217      // add the assoc bean main constructor
218      new TypeQueryAssocMainConstructor(this, cv, ASSOC_BEAN_MAIN_CONSTRUCTOR_DESC, ASSOC_BEAN_MAIN_SIG).visitCode();
219    }
220
221  }
222
223}