001package io.ebean.enhance.querybean;
002
003import io.ebean.enhance.asm.MethodVisitor;
004import io.ebean.enhance.asm.Opcodes;
005import io.ebean.enhance.common.EnhanceContext;
006
007import java.util.HashSet;
008import java.util.Set;
009
010import static io.ebean.enhance.querybean.Constants.SET_LABEL;
011
012/**
013 * Adapter that changes GETFIELD calls to type query beans to instead use the generated
014 * 'property access' methods.
015 */
016public class MethodAdapter extends MethodVisitor implements Opcodes {
017
018  private static Set<String> FINDER_METHODS = new HashSet<>();
019
020  static {
021    // exclude findEach, findEachWhile which take closures
022    FINDER_METHODS.add("findList");
023    FINDER_METHODS.add("findSet");
024    FINDER_METHODS.add("findMap");
025    FINDER_METHODS.add("findIds");
026    FINDER_METHODS.add("findCount");
027    FINDER_METHODS.add("findOne");
028    FINDER_METHODS.add("findOneOrEmpty");
029    FINDER_METHODS.add("findSingleAttribute");
030    FINDER_METHODS.add("findSingleAttributeList");
031    FINDER_METHODS.add("findIterate");
032  }
033
034  private final EnhanceContext enhanceContext;
035
036  private final ClassInfo classInfo;
037
038  private final String methodName;
039
040  private boolean labelSet;
041
042  public MethodAdapter(MethodVisitor mv, EnhanceContext enhanceContext, ClassInfo classInfo, String methodName) {
043    super(ASM7, mv);
044    this.enhanceContext = enhanceContext;
045    this.classInfo = classInfo;
046    this.methodName = methodName;
047  }
048
049  @Override
050  public void visitFieldInsn(int opcode, String owner, String name, String desc) {
051
052    if (opcode == GETFIELD && enhanceContext.isQueryBean(owner)) {
053      classInfo.addGetFieldIntercept(owner, name);
054      mv.visitMethodInsn(INVOKEVIRTUAL, owner, "_" + name, "()" + desc, false);
055    } else {
056      super.visitFieldInsn(opcode, owner, name, desc);
057    }
058  }
059
060  @Override
061  public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
062
063    if (!isInterface && enhanceContext.isEnableQueryAutoLabel()) {
064      if (SET_LABEL.equals(name) && enhanceContext.isQueryBean(owner)) {
065        // label set explicitly in code so don't auto set it
066        labelSet = true;
067      }
068      if (!labelSet && isFinderMethod(name) && enhanceContext.isQueryBean(owner)) {
069        // set a label on the query
070        classInfo.markTypeQueryEnhanced();
071        mv.visitLdcInsn(classInfo.getShortName() + "." + methodName);
072        mv.visitMethodInsn(INVOKEVIRTUAL, owner, SET_LABEL, "(Ljava/lang/String;)Ljava/lang/Object;", false);
073        mv.visitTypeInsn(CHECKCAST, owner);
074      }
075    }
076
077    super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
078  }
079
080  private boolean isFinderMethod(String name) {
081    return FINDER_METHODS.contains(name);
082  }
083}