001: /*
002: * @(#)BeanClassGenerator.java 1.2 04/12/06
003: *
004: * Copyright (c) 2003,2004 Sun Microsystems, Inc. All Rights Reserved.
005: *
006: * See the file "LICENSE.txt" for information on usage and redistribution
007: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
008: */
009: package org.pnuts.lib;
010:
011: import pnuts.compiler.*;
012: import org.pnuts.lang.ClassFileLoader;
013: import java.io.*;
014: import java.util.*;
015:
016: public class BeanClassGenerator {
017:
018: public static ClassFile generateClassFile(Map typeMap,
019: String className, String super ClassName,
020: String[] interfaces, String[] constructorParams) {
021: if (super ClassName == null) {
022: super ClassName = "java.lang.Object";
023: }
024: ClassFile cf = new ClassFile(className, super ClassName, null,
025: Constants.ACC_PUBLIC);
026: if (interfaces != null) {
027: for (int i = 0; i < interfaces.length; i++) {
028: cf.addInterface(interfaces[i]);
029: }
030: }
031: cf.openMethod("<init>", "()V", Constants.ACC_PUBLIC);
032: cf.add(Opcode.ALOAD_0);
033: cf.add(Opcode.INVOKESPECIAL, super ClassName, "<init>", "()",
034: "V");
035: cf.add(Opcode.RETURN);
036: cf.closeMethod();
037:
038: if (constructorParams != null) {
039: boolean constructorParamsAreValid = true;
040: StringBuffer sbuf = new StringBuffer("(");
041: for (int i = 0; i < constructorParams.length; i++) {
042: String param = constructorParams[i];
043: Class type = (Class) typeMap.get(param);
044: if (type == null) {
045: constructorParamsAreValid = false;
046: break;
047: }
048: sbuf.append(ClassFile.signature(type));
049: }
050: if (constructorParamsAreValid) {
051: sbuf.append(")V");
052: cf.openMethod("<init>", sbuf.toString(),
053: Constants.ACC_PUBLIC);
054:
055: cf.add(Opcode.ALOAD_0);
056: cf.add(Opcode.INVOKESPECIAL, super ClassName, "<init>",
057: "()", "V");
058: for (int i = 0; i < constructorParams.length; i++) {
059: cf.add(Opcode.ALOAD_0);
060: String param = constructorParams[i];
061: Class type = (Class) typeMap.get(param);
062: if (type.isPrimitive()) {
063: loadPrimitive(cf, type, i + 1);
064: } else {
065: cf.loadLocal(i + 1);
066: }
067: cf.add(Opcode.PUTFIELD, cf.getClassName(), param,
068: ClassFile.signature(type));
069: }
070: cf.add(Opcode.RETURN);
071: cf.closeMethod();
072: }
073: }
074:
075: for (Iterator it = typeMap.entrySet().iterator(); it.hasNext();) {
076: Map.Entry entry = (Map.Entry) it.next();
077: String key = (String) entry.getKey();
078: Class type = (Class) entry.getValue();
079: emit(cf, key, type);
080: }
081: return cf;
082: }
083:
084: private static void loadPrimitive(ClassFile cf, Class primitive,
085: int index) {
086: if (primitive == int.class) {
087: cf.iloadLocal(index);
088: } else if (primitive == byte.class) {
089: cf.iloadLocal(index);
090: } else if (primitive == short.class) {
091: cf.iloadLocal(index);
092: } else if (primitive == char.class) {
093: cf.iloadLocal(index);
094: } else if (primitive == long.class) {
095: cf.lloadLocal(index);
096: } else if (primitive == float.class) {
097: cf.floadLocal(index);
098: } else if (primitive == double.class) {
099: cf.dloadLocal(index);
100: } else if (primitive == boolean.class) {
101: cf.iloadLocal(index);
102: }
103: }
104:
105: static void emit(ClassFile cf, String propertyName, Class type) {
106: cf.addField(propertyName, ClassFile.signature(type),
107: Constants.ACC_PRIVATE);
108: /*
109: * getter
110: */
111: cf.openMethod(getterName(propertyName, type), makeSignature(
112: null, type), (short) Constants.ACC_PUBLIC);
113: cf.add(Opcode.ALOAD_0);
114: cf.add(Opcode.GETFIELD, cf.getClassName(), propertyName,
115: ClassFile.signature(type));
116: if (!type.isPrimitive()) {
117: cf.add(Opcode.ARETURN);
118: } else {
119: if (type == long.class) {
120: cf.add(Opcode.LRETURN);
121: } else if (type == float.class) {
122: cf.add(Opcode.FRETURN);
123: } else if (type == double.class) {
124: cf.add(Opcode.DRETURN);
125: } else {
126: cf.add(Opcode.IRETURN);
127: }
128: }
129: cf.closeMethod();
130:
131: /*
132: * setter
133: */
134: cf.openMethod(setterName(propertyName), makeSignature(type,
135: void.class), Constants.ACC_PUBLIC);
136: cf.add(Opcode.ALOAD_0);
137: if (!type.isPrimitive()) {
138: cf.add(Opcode.ALOAD_1);
139: } else {
140: if (type == long.class) {
141: cf.add(Opcode.LLOAD_1);
142: } else if (type == float.class) {
143: cf.add(Opcode.FLOAD_1);
144: } else if (type == double.class) {
145: cf.add(Opcode.DLOAD_1);
146: } else {
147: cf.add(Opcode.ILOAD_1);
148: }
149: }
150: cf.add(Opcode.PUTFIELD, cf.getClassName(), propertyName,
151: ClassFile.signature(type));
152: cf.add(Opcode.RETURN);
153: cf.closeMethod();
154: }
155:
156: static String capitalize(String s) {
157: char chars[] = s.toCharArray();
158: chars[0] = Character.toUpperCase(chars[0]);
159: return new String(chars);
160: }
161:
162: static String getterName(String property, Class type) {
163: if (type == boolean.class) {
164: return "is" + capitalize(property);
165: } else {
166: return "get" + capitalize(property);
167: }
168: }
169:
170: static String setterName(String property) {
171: return "set" + capitalize(property);
172: }
173:
174: static String makeSignature(Class type, Class returnType) {
175: StringBuffer sbuf = new StringBuffer();
176: if (type == null) {
177: sbuf.append("()");
178: } else {
179: sbuf.append(ClassFile.signature(new Class[] { type }));
180: }
181: if (returnType == void.class) {
182: sbuf.append('V');
183: } else {
184: sbuf.append(ClassFile.signature(returnType));
185: }
186: return sbuf.toString();
187: }
188:
189: /**
190: * Generates a JavaBeans class from type map
191: *
192: * @param typeName [propertyName, type] mapping
193: * @param className the class name
194: * @param superClassName the super class mame, null implies java.lang.Object
195: * @param interfaces the array of interface names, null if none.
196: * @return the generated class
197: */
198: public static Class generate(Map typeMap, String className,
199: String super ClassName, String[] interfaces)
200: throws IOException {
201: return generate(typeMap, className, super ClassName, interfaces,
202: Thread.currentThread().getContextClassLoader());
203: }
204:
205: /**
206: * Generates a JavaBeans class from type map
207: *
208: * @param typeName [propertyName, type] mapping
209: * @param className the class name
210: * @param superClassName the super class mame, null implies java.lang.Object
211: * @param interfaces the array of interface names, null if none.
212: * @param loader the parent ClassLoader
213: * @return the generated class
214: */
215: public static Class generate(Map typeMap, String className,
216: String super ClassName, String[] interfaces,
217: ClassLoader loader) throws IOException {
218: ClassFile cf = generateClassFile(typeMap, className,
219: super ClassName, interfaces, null);
220: return (Class) new ClassFileLoader(loader).handle(cf);
221: }
222:
223: /**
224: * Generates a JavaBeans class from type map,
225: * and write the byte code to the specified output stream.
226: *
227: * @param typeName [propertyName, type] mapping
228: * @param className the class name
229: * @param superClassName the super class mame, null implies java.lang.Object
230: * @param interfaces the array of interface names, null if none.
231: * @param out the output stream
232: */
233: public static void generate(Map typeMap, String className,
234: String super ClassName, String[] interfaces, OutputStream out)
235: throws IOException {
236: ClassFile cf = generateClassFile(typeMap, className,
237: superClassName, interfaces, null);
238: cf.write(out);
239: }
240: }
|