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