001: /*
002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: * $Id: LazyLoadMethodAdapter.java 3685 2007-03-08 13:00:42Z gbevin $
007: */
008: package com.uwyn.rife.database.querymanagers.generic.instrument;
009:
010: import static com.uwyn.rife.database.querymanagers.generic.instrument.LazyLoadAccessorsBytecodeTransformer.GQM_VAR_NAME;
011: import static com.uwyn.rife.database.querymanagers.generic.instrument.LazyLoadAccessorsBytecodeTransformer.LAZYLOADED_VAR_NAME;
012:
013: import java.util.Map;
014:
015: import com.uwyn.rife.asm.*;
016:
017: class LazyLoadMethodAdapter extends MethodAdapter implements Opcodes {
018: private String mClassName = null;
019: private String mMethodName = null;
020: private String mMethodDescriptor = null;
021: private String mPropertyName = null;
022: private boolean mIsGetter = false;
023: private boolean mIsSetter = false;
024:
025: LazyLoadMethodAdapter(String className, String methodName,
026: String methodDescriptor, MethodVisitor visitor) {
027: super (visitor);
028:
029: mClassName = className;
030: mMethodName = methodName;
031: mMethodDescriptor = methodDescriptor;
032: mPropertyName = uncapitalize(methodName.substring(3)); // the length of the get/set accessor prefix
033:
034: mIsGetter = mMethodName.startsWith("get");
035: mIsSetter = mMethodName.startsWith("set");
036: }
037:
038: private static String uncapitalize(String source) {
039: if (source == null || source.length() == 0) {
040: return source;
041: }
042:
043: if (source.length() > 1
044: && Character.isLowerCase(source.charAt(0))) {
045: return source;
046: }
047:
048: char chars[] = source.toCharArray();
049: chars[0] = Character.toLowerCase(chars[0]);
050: return new String(chars);
051: }
052:
053: public void visitInsn(int opcode) {
054: if (ARETURN == opcode && mIsGetter) {
055: Label label_begin = new Label();
056: Label label_retrieve = new Label();
057: Label label_return = new Label();
058: Label label_end = new Label();
059:
060: Type type_object = Type.getType(Object.class);
061:
062: mv.visitLabel(label_begin);
063:
064: // don't fetch a value if the current returned value is not null
065: mv.visitInsn(DUP);
066: mv.visitJumpInsn(IFNONNULL, label_end);
067:
068: // don't fetch a value if there's no generic query manager stored in the bean
069: mv.visitVarInsn(ALOAD, 0);
070: mv
071: .visitFieldInsn(GETFIELD, mClassName, GQM_VAR_NAME,
072: "Lcom/uwyn/rife/database/querymanagers/generic/GenericQueryManager;");
073: mv.visitJumpInsn(IFNULL, label_end);
074:
075: // don't fetch a value if there's already one stored in the map
076: mv.visitVarInsn(ALOAD, 0);
077: mv.visitFieldInsn(GETFIELD, mClassName,
078: LAZYLOADED_VAR_NAME, Type.getDescriptor(Map.class));
079: mv.visitLdcInsn(mPropertyName);
080: mv.visitMethodInsn(INVOKEINTERFACE, Type
081: .getInternalName(Map.class), "containsKey", Type
082: .getMethodDescriptor(Type.BOOLEAN_TYPE,
083: new Type[] { type_object }));
084: mv.visitJumpInsn(IFNE, label_retrieve);
085:
086: // load the value from the database
087: mv.visitVarInsn(ALOAD, 0);
088: mv
089: .visitFieldInsn(GETFIELD, mClassName, GQM_VAR_NAME,
090: "Lcom/uwyn/rife/database/querymanagers/generic/GenericQueryManager;");
091: mv.visitVarInsn(ALOAD, 0);
092: mv.visitLdcInsn(mPropertyName);
093: mv.visitLdcInsn(Type.getReturnType(mMethodDescriptor)
094: .getClassName());
095: mv
096: .visitMethodInsn(
097: INVOKESTATIC,
098: "com/uwyn/rife/database/querymanagers/generic/GenericQueryManagerRelationalUtils",
099: "restoreLazyManyToOneProperty",
100: "(Lcom/uwyn/rife/database/querymanagers/generic/GenericQueryManager;Lcom/uwyn/rife/site/Constrained;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;");
101: mv.visitInsn(DUP);
102:
103: // store the lazily loaded value in the hash map
104: mv.visitVarInsn(ALOAD, 0);
105: mv.visitFieldInsn(GETFIELD, mClassName,
106: LAZYLOADED_VAR_NAME, Type.getDescriptor(Map.class));
107: mv.visitInsn(SWAP);
108: mv.visitLdcInsn(mPropertyName);
109: mv.visitInsn(SWAP);
110: mv.visitMethodInsn(INVOKEINTERFACE, Type
111: .getInternalName(Map.class), "put", Type
112: .getMethodDescriptor(type_object, new Type[] {
113: type_object, type_object }));
114: mv.visitInsn(POP);
115: mv.visitJumpInsn(GOTO, label_return);
116:
117: // fetch the existing value from the map
118: mv.visitLabel(label_retrieve);
119: mv.visitVarInsn(ALOAD, 0);
120: mv.visitFieldInsn(GETFIELD, mClassName,
121: LAZYLOADED_VAR_NAME, Type.getDescriptor(Map.class));
122: mv.visitLdcInsn(mPropertyName);
123: mv.visitMethodInsn(INVOKEINTERFACE, Type
124: .getInternalName(Map.class), "get", Type
125: .getMethodDescriptor(type_object,
126: new Type[] { type_object }));
127:
128: // return the value
129: mv.visitLabel(label_return);
130: mv.visitTypeInsn(CHECKCAST, Type.getReturnType(
131: mMethodDescriptor).getInternalName());
132: mv.visitInsn(ARETURN);
133:
134: mv.visitLabel(label_end);
135: }
136:
137: super .visitInsn(opcode);
138: }
139:
140: public void visitCode() {
141: if (mIsSetter) {
142: Type type_object = Type.getType(Object.class);
143:
144: mv.visitVarInsn(ALOAD, 0);
145: mv.visitFieldInsn(GETFIELD, mClassName,
146: LAZYLOADED_VAR_NAME, Type.getDescriptor(Map.class));
147: Label l1 = new Label();
148: mv.visitJumpInsn(IFNULL, l1);
149: Label l2 = new Label();
150: mv.visitLabel(l2);
151: mv.visitLineNumber(55, l2);
152: mv.visitVarInsn(ALOAD, 0);
153: mv.visitFieldInsn(GETFIELD, mClassName,
154: LAZYLOADED_VAR_NAME, Type.getDescriptor(Map.class));
155: mv.visitLdcInsn(mPropertyName);
156: mv.visitMethodInsn(INVOKEINTERFACE, Type
157: .getInternalName(Map.class), "remove", Type
158: .getMethodDescriptor(type_object,
159: new Type[] { type_object }));
160: mv.visitInsn(POP);
161: mv.visitLabel(l1);
162: }
163: super.visitCode();
164: }
165: }
|