001: /**
002: * EasyBeans
003: * Copyright (C) 2006 Bull S.A.S.
004: * Contact: easybeans@ow2.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: CommonClassGenerator.java 2057 2007-11-21 15:35:32Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.enhancer;
025:
026: import org.ow2.easybeans.asm.ClassVisitor;
027: import org.ow2.easybeans.asm.ClassWriter;
028: import org.ow2.easybeans.asm.FieldVisitor;
029: import org.ow2.easybeans.asm.MethodVisitor;
030: import org.ow2.easybeans.asm.Opcodes;
031: import org.ow2.easybeans.asm.Type;
032: import org.ow2.easybeans.api.Factory;
033:
034: /**
035: * Class with useful routines for writing a class.
036: * @author Florent Benoit
037: */
038: public abstract class CommonClassGenerator implements Opcodes {
039:
040: /**
041: * Define an array of objects.
042: */
043: public static final String ARRAY_OBJECTS = "[Ljava/lang/Object;";
044:
045: /**
046: * Defines java.lang.Object class.
047: */
048: public static final String JAVA_LANG_OBJECT = "Ljava/lang/Object;";
049:
050: /**
051: * Defines a void method with JAVA_LANG_OBJECT as parameter.
052: */
053: public static final String VOID_METHOD_JAVA_LANG_OBJECT = "(Ljava/lang/Object;)V";
054:
055: /**
056: * Defines java.lang.Exception class.
057: */
058: public static final String JAVA_LANG_EXCEPTION = "Ljava/lang/Exception;";
059:
060: /**
061: * Define java.lang.reflect.Method.
062: */
063: public static final String JAVA_LANG_REFLECT_METHOD = "Ljava/lang/reflect/Method;";
064:
065: /**
066: * Factory class (used to make the bean factory available).
067: */
068: public static final String EASYBEANS_FACTORY = Type
069: .getDescriptor(Factory.class);
070:
071: /**
072: * Version used for generated class.
073: */
074: public static final int GENERATED_CLASS_VERSION = V1_5;
075:
076: /**
077: * The {@link org.ow2.easybeans.asm.ClassWriter} to which this adapter delegates
078: * calls.
079: */
080: private ClassWriter cw;
081:
082: /**
083: * Field visitor.
084: */
085: private FieldVisitor fv = null;
086:
087: /**
088: * Creates a default class with useful routines for writing a class.
089: * @param cw the class writer which will generate the class
090: */
091: public CommonClassGenerator(final ClassWriter cw) {
092: this .cw = cw;
093: }
094:
095: /**
096: * Adds an attribute in the current classwriter.
097: * @param access the field's access flags (see {@link Opcodes}). This
098: * parameter also indicates if the field is synthetic and/or
099: * deprecated.
100: * @param name the field's name.
101: * @param desc the field's descriptor (see {@link Type Type}).
102: */
103: protected void addAttribute(final int access, final String name,
104: final String desc) {
105: addAttribute(access, name, desc, null);
106: }
107:
108: /**
109: * Adds an attribute in the current classwriter.
110: * @param access the field's access flags (see {@link Opcodes}). This
111: * parameter also indicates if the field is synthetic and/or
112: * deprecated.
113: * @param name the field's name.
114: * @param desc the field's descriptor (see {@link Type Type}).
115: * @param value the field's initial value. This parameter, which may be
116: * <tt>null</tt> if the field does not have an initial value, must
117: * be an {@link Integer}, a {@link Float}, a {@link Long}, a
118: * {@link Double} or a {@link String} (for <tt>int</tt>,
119: * <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields
120: * respectively). <i>This parameter is only used for static fields</i>.
121: * Its value is ignored for non static fields, which must be
122: * initialized through bytecode instructions in constructors or
123: * methods.
124: */
125: protected void addAttribute(final int access, final String name,
126: final String desc, final Object value) {
127: fv = cw.visitField(access, name, desc, null, value);
128: fv.visitEnd();
129: }
130:
131: /**
132: * Encodes an internal classname into a descriptor.
133: * @param className internal class name
134: * @return desc for the given className
135: */
136: public static String encodeClassDesc(final String className) {
137: return "L" + className + ";";
138: }
139:
140: /**
141: * Encodes an internal classname into an array descriptor.
142: * @param className internal class name
143: * @return desc for the given className (array mode)
144: */
145: public static String encodeArrayClassDesc(final String className) {
146: return "[L" + className + ";";
147: }
148:
149: /**
150: * @return the class writer used by this generator
151: */
152: public ClassWriter getCW() {
153: return cw;
154: }
155:
156: /**
157: * Sends the OpCode used in an constructor to set the field.
158: * @param sortCode type of attribute to set
159: * @return op code
160: */
161: public static int putFieldLoadOpCode(final int sortCode) {
162: switch (sortCode) {
163: case Type.BOOLEAN:
164: case Type.BYTE:
165: case Type.CHAR:
166: case Type.SHORT:
167: case Type.INT:
168: return ILOAD;
169: case Type.FLOAT:
170: return FLOAD;
171: case Type.LONG:
172: return LLOAD;
173: case Type.DOUBLE:
174: return DLOAD;
175: // case ARRAY:
176: // case OBJECT:
177: default:
178: return ALOAD;
179: }
180: }
181:
182: /**
183: * If a type is one of the primitive object : boolean, int, long, etc, adds an instruction to transform type into an object.
184: * ie : from int to Integer by calling Integer.valueOf(i);
185: * @param type the object that need to be processed
186: * @param mv the method visitor on which there is a need to adds an instruction
187: */
188: public static void transformPrimitiveIntoObject(final Type type,
189: final MethodVisitor mv) {
190: switch (type.getSort()) {
191: case Type.BOOLEAN:
192: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean",
193: "valueOf", "(Z)Ljava/lang/Boolean;");
194: break;
195: case Type.BYTE:
196: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte",
197: "valueOf", "(B)Ljava/lang/Byte;");
198: break;
199: case Type.CHAR:
200: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character",
201: "valueOf", "(C)Ljava/lang/Character;");
202: break;
203: case Type.SHORT:
204: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short",
205: "valueOf", "(S)Ljava/lang/Short;");
206: break;
207: case Type.INT:
208: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer",
209: "valueOf", "(I)Ljava/lang/Integer;");
210: break;
211: case Type.FLOAT:
212: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float",
213: "valueOf", "(F)Ljava/lang/Float;");
214: break;
215: case Type.LONG:
216: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long",
217: "valueOf", "(J)Ljava/lang/Long;");
218: break;
219: case Type.DOUBLE:
220: mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double",
221: "valueOf", "(D)Ljava/lang/Double;");
222: break;
223: // case ARRAY:
224: // case OBJECT:
225: default:
226: // nothing as this is already objects !
227: break;
228: }
229: }
230:
231: /**
232: * If a type is one of the object type with something which could be linked to a primitive type, cast object into primitive.
233: * ie : from Integer to int by calling ((Integer) object).intValue();
234: * @param type the object that need to be processed
235: * @param mv the method visitor on which there is a need to adds an instruction
236: */
237: public static void transformObjectIntoPrimitive(final Type type,
238: final MethodVisitor mv) {
239: switch (type.getSort()) {
240: case Type.BOOLEAN:
241: mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
242: mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean",
243: "booleanValue", "()Z");
244: break;
245: case Type.BYTE:
246: mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
247: mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte",
248: "byteValue", "()B");
249: break;
250: case Type.CHAR:
251: mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
252: mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character",
253: "charValue", "()C");
254: break;
255: case Type.SHORT:
256: mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
257: mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short",
258: "shortValue", "()S");
259: break;
260: case Type.INT:
261: mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
262: mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer",
263: "intValue", "()I");
264: break;
265: case Type.FLOAT:
266: mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
267: mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float",
268: "floatValue", "()F");
269: break;
270: case Type.LONG:
271: mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
272: mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long",
273: "longValue", "()J");
274: break;
275: case Type.DOUBLE:
276: mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
277: mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double",
278: "doubleValue", "()D");
279: break;
280: case Type.VOID:
281: mv.visitInsn(POP);
282: break;
283: case Type.ARRAY:
284: mv.visitTypeInsn(CHECKCAST, type.getDescriptor());
285: break;
286: case Type.OBJECT:
287: mv.visitTypeInsn(CHECKCAST, type.getInternalName());
288: break;
289: default:
290: // nothing as this is already objects !
291: break;
292: }
293: }
294:
295: /**
296: * Adds a return entry depending of the type value.
297: * ie, for int : mv.visitInsn(IRETURN);
298: * for void : mv.visitInsn(RETURN);
299: * @param type the object that need to be processed
300: * @param mv the method visitor on which there is a need to adds an instruction
301: */
302: public static void addReturnType(final Type type,
303: final MethodVisitor mv) {
304: switch (type.getSort()) {
305: case Type.BOOLEAN:
306: case Type.BYTE:
307: case Type.CHAR:
308: case Type.SHORT:
309: case Type.INT:
310: mv.visitInsn(IRETURN);
311: break;
312: case Type.FLOAT:
313: mv.visitInsn(FRETURN);
314: break;
315: case Type.LONG:
316: mv.visitInsn(LRETURN);
317: break;
318: case Type.DOUBLE:
319: mv.visitInsn(DRETURN);
320: break;
321: case Type.VOID:
322: mv.visitInsn(RETURN);
323: break;
324: case Type.ARRAY:
325: case Type.OBJECT:
326: mv.visitInsn(ARETURN);
327: break;
328: default:
329: // nothing as this is already objects !
330: break;
331: }
332: }
333:
334: /**
335: * Allow to access to a class.<br>
336: * ie, for int.class it will access to Integer.TYPE.<br>
337: * For objects, it only calls the Object.
338: * @param type the type of the object from which we want the class
339: * @param mv object on which we add the instruction
340: */
341: public static void visitClassType(final Type type,
342: final MethodVisitor mv) {
343: switch (type.getSort()) {
344: case Type.BOOLEAN:
345: mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TYPE",
346: "Ljava/lang/Class;");
347: break;
348: case Type.BYTE:
349: mv.visitFieldInsn(GETSTATIC, "java/lang/Byte", "TYPE",
350: "Ljava/lang/Class;");
351: break;
352: case Type.CHAR:
353: mv.visitFieldInsn(GETSTATIC, "java/lang/Character", "TYPE",
354: "Ljava/lang/Class;");
355: break;
356: case Type.SHORT:
357: mv.visitFieldInsn(GETSTATIC, "java/lang/Short", "TYPE",
358: "Ljava/lang/Class;");
359: break;
360: case Type.INT:
361: mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE",
362: "Ljava/lang/Class;");
363: break;
364: case Type.FLOAT:
365: mv.visitFieldInsn(GETSTATIC, "java/lang/Float", "TYPE",
366: "Ljava/lang/Class;");
367: break;
368: case Type.LONG:
369: mv.visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE",
370: "Ljava/lang/Class;");
371: break;
372: case Type.DOUBLE:
373: mv.visitFieldInsn(GETSTATIC, "java/lang/Double", "TYPE",
374: "Ljava/lang/Class;");
375: break;
376: // case ARRAY:
377: // case OBJECT:
378: default:
379: mv.visitLdcInsn(type);
380: break;
381: }
382: }
383:
384: /**
385: * Returns the object by casting it or return null if method is of void type.
386: * @param returnType the kind of return for this method.
387: * @param mv object on which we add the instruction.
388: */
389: public static void returnsObject(final Type returnType,
390: final MethodVisitor mv) {
391: if (returnType.equals(Type.VOID_TYPE)) {
392: mv.visitInsn(ACONST_NULL);
393: } else {
394: transformPrimitiveIntoObject(returnType, mv);
395: }
396: mv.visitInsn(ARETURN);
397: }
398:
399: /**
400: * Adds a field with its getters/setters.
401: * @param cv the classvisitor to add field/methods
402: * @param beanClassName the name of the bean's class
403: * @param fieldName the name of the attribute
404: * @param clazz the class of the attribute.
405: */
406: public static void addFieldGettersSetters(final ClassVisitor cv,
407: final String beanClassName, final String fieldName,
408: final Class clazz) {
409: String className = Type.getDescriptor(clazz);
410: addFieldGettersSetters(cv, beanClassName, fieldName, className);
411: }
412:
413: /**
414: * Adds a field with its getters/setters.
415: * @param cv the classvisitor to add field/methods
416: * @param beanClassName the name of the bean's class
417: * @param fieldName the name of the attribute
418: * @param className the className of the attribute.
419: */
420: public static void addFieldGettersSetters(final ClassVisitor cv,
421: final String beanClassName, final String fieldName,
422: final String className) {
423:
424: // Get type of the class
425: Type type = Type.getType(className);
426:
427: // Add the fieldName attribute
428: // private CLASSNAME fieldName = null;
429: FieldVisitor fv = cv.visitField(ACC_PRIVATE, fieldName,
430: className, null, null);
431: fv.visitEnd();
432:
433: // build getterName
434: String appendName = fieldName.toUpperCase().charAt(0)
435: + fieldName.substring(1);
436: String getterName = "get" + appendName;
437:
438: // Add its getter :
439: // public CLASSNAME getterName() {
440: // return this.fieldName;
441: // }
442: MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, getterName, "()"
443: + className, null, null);
444: mv.visitCode();
445: mv.visitVarInsn(ALOAD, 0);
446: mv
447: .visitFieldInsn(GETFIELD, beanClassName, fieldName,
448: className);
449: // return type is depending of the type
450: addReturnType(type, mv);
451: mv.visitMaxs(0, 0);
452: mv.visitEnd();
453:
454: // Add the setter
455: // public void setterName(final CLASSNAME setterName) {
456: // this.fieldName = fieldName;
457: // }
458: String setterName = "set" + appendName;
459: mv = cv.visitMethod(ACC_PUBLIC, setterName, "(" + className
460: + ")V", null, null);
461: mv.visitCode();
462: mv.visitVarInsn(ALOAD, 0);
463: // Depends of the type
464: int opCode = putFieldLoadOpCode(type.getSort());
465: mv.visitVarInsn(opCode, 1);
466: mv
467: .visitFieldInsn(PUTFIELD, beanClassName, fieldName,
468: className);
469: mv.visitInsn(RETURN);
470: mv.visitMaxs(0, 0);
471: mv.visitEnd();
472: }
473:
474: /**
475: * Adds a getter for a given fieldName. If the fieldName is null, it's return null.
476: * @param cv the classvisitor to add the method
477: * @param getterName the name of the method
478: * @param clazz the class of the returned object.
479: */
480: public static void addNullGetter(final ClassVisitor cv,
481: final String getterName, final Class clazz) {
482: String returnedClassName = Type.getDescriptor(clazz);
483: // Add its getter :
484: // public returnedClassName getterName() {
485: // return null;
486: // }
487: MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, getterName, "()"
488: + returnedClassName, null, null);
489: mv.visitCode();
490: mv.visitInsn(ACONST_NULL);
491: mv.visitInsn(ARETURN);
492:
493: mv.visitMaxs(0, 0);
494: mv.visitEnd();
495: }
496:
497: }
|