001: /*
002: * Copyright 2006-2007 Pentaho Corporation. All rights reserved.
003: * This software was developed by Pentaho Corporation and is provided under the terms
004: * of the Mozilla Public License, Version 1.1, or any later version. You may not use
005: * this file except in compliance with the license. If you need a copy of the license,
006: * please go to http://www.mozilla.org/MPL/MPL-1.1.txt.
007: *
008: * Software distributed under the Mozilla Public License is distributed on an "AS IS"
009: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
010: * the license for the specific language governing your rights and limitations.
011: *
012: * Additional Contributor(s): Martin Schmid gridvision engineering GmbH
013: */
014: package org.pentaho.reportdesigner.crm.report.model.functions;
015:
016: import org.jetbrains.annotations.NotNull;
017: import org.objectweb.asm.ClassWriter;
018: import org.objectweb.asm.FieldVisitor;
019: import org.objectweb.asm.MethodVisitor;
020: import org.objectweb.asm.Opcodes;
021: import org.objectweb.asm.Type;
022: import org.pentaho.reportdesigner.crm.report.PropertyKeys;
023: import org.pentaho.reportdesigner.crm.report.model.ReportFunctionElement;
024: import org.pentaho.reportdesigner.lib.client.undo.Undo;
025: import org.pentaho.reportdesigner.lib.client.util.IOUtil;
026:
027: import java.io.FileOutputStream;
028: import java.io.IOException;
029: import java.lang.reflect.Constructor;
030: import java.lang.reflect.InvocationTargetException;
031: import java.lang.reflect.Method;
032: import java.util.LinkedHashMap;
033:
034: /**
035: * User: Martin
036: * Date: 26.07.2006
037: * Time: 17:43:35
038: */
039: @SuppressWarnings({"HardCodedStringLiteral","UseOfSystemOutOrSystemErr"})
040: public class FunctionGenerator {
041: public static void main(@NotNull
042: String[] args) throws IOException, IllegalAccessException,
043: InvocationTargetException, InstantiationException,
044: ClassNotFoundException, NoSuchMethodException {
045: Property property1 = new Property(Integer.class, "testField",
046: PropertyKeys.GROUP_UNKNOWN, -1);
047: Property property2 = new Property(int.class, "intField",
048: PropertyKeys.GROUP_UNKNOWN, -1);
049:
050: FunctionGenerator functionGenerator = new FunctionGenerator(
051: "ch.gridvision.bla.TestClass");
052: functionGenerator.addProperty(property1);
053: functionGenerator.addProperty(property2);
054: byte[] bytes = functionGenerator.generateClass();
055: //noinspection IOResourceOpenedButNotSafelyClosed
056: FileOutputStream fos = new FileOutputStream("TestClass.class");
057: fos.write(bytes);
058: fos.close();
059:
060: byte[] bytesBeanInfo = functionGenerator.generateBeanInfo();
061: //noinspection IOResourceOpenedButNotSafelyClosed
062: FileOutputStream fosBeanInfo = new FileOutputStream(
063: "TestClassBeanInfo.class");
064: fosBeanInfo.write(bytesBeanInfo);
065: fosBeanInfo.close();
066:
067: Class<?> aClass = FunctionGenerator
068: .defineClass(functionGenerator);
069: ReportFunctionElement rfe = (ReportFunctionElement) aClass
070: .newInstance();
071: Undo undo = new Undo();
072: rfe.setUndo(undo);
073:
074: Class<?> beanInfoClass = Class
075: .forName("ch.gridvision.bla.TestClassBeanInfo");
076: Constructor<?> constructor = beanInfoClass.getConstructor();
077: System.out.println("constructor = " + constructor);
078:
079: Object beanInfo = beanInfoClass.newInstance();
080: System.out.println("beanInfo = " + beanInfo);
081:
082: Method[] methods = aClass.getDeclaredMethods();
083: for (Method method : methods) {
084: System.out.println("method = " + method);
085: if (method.getParameterTypes().length == 1)//setter
086: {
087: Object o = method.invoke(rfe, Integer.valueOf(123));
088: System.out.println("o = " + o);
089: }
090: }
091:
092: for (Method method : methods) {
093: System.out.println("method = " + method);
094: if (method.getParameterTypes().length == 0)//getter
095: {
096: Object o = method.invoke(rfe);
097: System.out.println("o = " + o);
098: }
099: }
100:
101: undo.debugPrint();
102: }
103:
104: private static final boolean DEBUG_WRITE_CLASS_FILES = false;
105: private static final boolean DEBUG_WRITE_BEAN_INFO_CLASS_FILES = false;
106:
107: @NotNull
108: public static final String REPORT_FUNCTION_ELEMENT_CLASS_PATH = "org/pentaho/reportdesigner/crm/report/model/ReportFunctionElement";
109: @NotNull
110: public static final String UNDO_CLASS_PATH = "org/pentaho/reportdesigner/lib/client/undo/Undo";
111: @NotNull
112: public static final String UNDO_ENTRY_CLASS_PATH = "org/pentaho/reportdesigner/lib/client/undo/UndoEntry";
113: @NotNull
114: public static final String REFLECTION_UNDO_ENTRY_CLASS_PATH = "org/pentaho/reportdesigner/lib/client/undo/ReflectionUndoEntry";
115:
116: @NotNull
117: public static final String GROUPING_PROPERTY_DESCRIPTOR_CLASS_PATH = "org/pentaho/reportdesigner/crm/report/model/GroupingPropertyDescriptor";
118:
119: @NotNull
120: public static final String PACKAGE_PREFIX = "generated.";
121:
122: @NotNull
123: public static Class<ReportFunctionElement> defineClass(@NotNull
124: FunctionGenerator functionGenerator) {
125: byte[] bytes = functionGenerator.generateClass();
126: byte[] bytesBeanInfo = functionGenerator.generateBeanInfo();
127:
128: if (DEBUG_WRITE_CLASS_FILES) {
129: debugWriteClassFile(functionGenerator.getClassname(), bytes);
130: }
131: if (DEBUG_WRITE_BEAN_INFO_CLASS_FILES) {
132: debugWriteClassFile(functionGenerator
133: .getClassnameBeanInfo(), bytesBeanInfo);
134: }
135:
136: ClassLoader classLoader = FunctionGenerator.class
137: .getClassLoader();
138: if (classLoader == null) {
139: classLoader = Thread.currentThread()
140: .getContextClassLoader();
141: }
142: if (classLoader == null) {
143: throw new IllegalStateException(
144: "no classloader found for function injection");
145: }
146:
147: try {
148: Method method = ClassLoader.class.getDeclaredMethod(
149: "defineClass", String.class, byte[].class,
150: int.class, int.class);//NON-NLS
151: method.setAccessible(true);
152: Object clazz = method.invoke(classLoader, functionGenerator
153: .getClassname(), bytes, Integer.valueOf(0), Integer
154: .valueOf(bytes.length));
155: /*Object clazzBeanInfo = */
156: method.invoke(classLoader, functionGenerator
157: .getClassnameBeanInfo(), bytesBeanInfo, Integer
158: .valueOf(0), Integer.valueOf(bytesBeanInfo.length));
159: //noinspection unchecked
160: return (Class<ReportFunctionElement>) clazz;
161: } catch (Exception e) {
162: throw new IllegalStateException(
163: "class generation failed miserably", e);
164: }
165: }
166:
167: private static void debugWriteClassFile(@NotNull
168: String classname, @NotNull
169: byte[] bytes) {
170: FileOutputStream fos = null;
171: try {
172: int i = classname.lastIndexOf('.');
173: if (i != -1) {
174: classname = classname.substring(i + 1);
175: }
176: //noinspection IOResourceOpenedButNotSafelyClosed
177: fos = new FileOutputStream(classname + ".class");
178: fos.write(bytes);
179: fos.close();
180: } catch (IOException e) {
181: //noinspection CallToPrintStackTrace
182: e.printStackTrace();
183: } finally {
184: IOUtil.closeStream(fos);
185: }
186: }
187:
188: @NotNull
189: private LinkedHashMap<String, Property> properties;
190: @NotNull
191: private String classname;
192:
193: @NotNull
194: private PropertyMethodEmitter propertyMethodEmitterPrimitive;
195: @NotNull
196: private PropertyMethodEmitter propertyMethodEmitterReference;
197:
198: public FunctionGenerator(@NotNull
199: String classname) {
200: this .classname = classname;
201: this .properties = new LinkedHashMap<String, Property>();
202:
203: propertyMethodEmitterPrimitive = new PropertyMethodEmitterPrimitive();
204: propertyMethodEmitterReference = new PropertyMethodEmitterReference();
205: }
206:
207: @NotNull
208: public String getClassname() {
209: return classname;
210: }
211:
212: @NotNull
213: public String getClassnameBeanInfo() {
214: return classname + "BeanInfo";
215: }
216:
217: public void addProperty(@NotNull
218: Property property) {
219: properties.put(property.getName(), property);
220: }
221:
222: @NotNull
223: private byte[] generateClass() {
224: ClassWriter cw = new ClassWriter(false);
225: MethodVisitor mv;
226:
227: String classPathName = classname.replace('.', '/');
228: int index = classname.lastIndexOf('.');
229: String simpleClassName;
230: if (index == -1) {
231: simpleClassName = classname;
232: } else {
233: simpleClassName = classname.substring(index + 1);
234: }
235:
236: cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER,
237: classPathName, null,
238: REPORT_FUNCTION_ELEMENT_CLASS_PATH, null);
239:
240: cw.visitSource(simpleClassName + ".java", null);//NON-NLS
241:
242: for (Property property : properties.values()) {
243: //name is already a field on the superclass, so we don't generate field/getter/setter
244: if (!"name".equals(property.getName())) {
245: /*fv = */
246: cw.visitField(Opcodes.ACC_PRIVATE, property.getName(),
247: property.getTypeString(), null, null);
248: }
249: }
250:
251: mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null,
252: null);//NON-NLS
253: mv.visitCode();
254: mv.visitVarInsn(Opcodes.ALOAD, 0);
255: mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
256: REPORT_FUNCTION_ELEMENT_CLASS_PATH, "<init>", "()V");//NON-NLS
257: mv.visitInsn(Opcodes.RETURN);
258: mv.visitMaxs(1, 1);
259: mv.visitEnd();
260:
261: for (Property property : properties.values()) {
262: //name is already a field on the superclass, so we don't generate field/getter/setter
263: if (!"name".equals(property.getName())) {
264: if (property.isPrimitiveType()) {
265: propertyMethodEmitterPrimitive.emit(cw,
266: classPathName, property);
267: } else {
268: propertyMethodEmitterReference.emit(cw,
269: classPathName, property);
270: }
271: }
272: }
273:
274: cw.visitEnd();
275:
276: return cw.toByteArray();
277: }
278:
279: @NotNull
280: private byte[] generateBeanInfo() {
281: ClassWriter cw = new ClassWriter(false);
282: FieldVisitor fv;
283: MethodVisitor mv;
284:
285: String classname = this .classname + "BeanInfo";
286:
287: String classPathNameBeanInfo = classname.replace('.', '/');
288: String classPathName = this .classname.replace('.', '/');
289:
290: int index = classname.lastIndexOf('.');
291: String simpleClassName;
292: if (index == -1) {
293: simpleClassName = classname;
294: } else {
295: simpleClassName = classname.substring(index + 1);
296: }
297:
298: cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER,
299: classPathNameBeanInfo, null,
300: "java/beans/SimpleBeanInfo", null);
301:
302: cw.visitSource(simpleClassName + ".java", null);
303:
304: fv = cw.visitField(Opcodes.ACC_PRIVATE,
305: "groupingPropertyDescriptor",
306: "[L" + GROUPING_PROPERTY_DESCRIPTOR_CLASS_PATH + ";",
307: null, null);
308: fv.visitEnd();
309:
310: //invoke super constructor
311: mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null,
312: new String[] { "java/beans/IntrospectionException" });
313: mv.visitCode();
314: mv.visitVarInsn(Opcodes.ALOAD, 0);
315: mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
316: "java/beans/SimpleBeanInfo", "<init>", "()V");
317:
318: //ArrayList<GroupingPropertyDescriptor> groupingPropertyDescriptorsList = new ArrayList<GroupingPropertyDescriptor>();
319: mv.visitTypeInsn(Opcodes.NEW, "java/util/ArrayList");
320: mv.visitInsn(Opcodes.DUP);
321: mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
322: "java/util/ArrayList", "<init>", "()V");
323: mv.visitVarInsn(Opcodes.ASTORE, 1);
324:
325: for (Property property : properties.values()) {
326: addPropertyDescriptor(classPathName, property, mv);
327: }
328:
329: //groupingPropertyDescriptor = groupingPropertyDescriptorsList.toArray(new GroupingPropertyDescriptor[groupingPropertyDescriptorsList.size()]);
330: mv.visitVarInsn(Opcodes.ALOAD, 0);
331: mv.visitVarInsn(Opcodes.ALOAD, 1);
332: mv.visitVarInsn(Opcodes.ALOAD, 1);
333: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
334: "java/util/ArrayList", "size", "()I");
335: mv.visitTypeInsn(Opcodes.ANEWARRAY,
336: GROUPING_PROPERTY_DESCRIPTOR_CLASS_PATH);
337: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
338: "java/util/ArrayList", "toArray",
339: "([Ljava/lang/Object;)[Ljava/lang/Object;");
340: mv.visitTypeInsn(Opcodes.CHECKCAST, "[L"
341: + GROUPING_PROPERTY_DESCRIPTOR_CLASS_PATH + ";");
342: mv
343: .visitFieldInsn(Opcodes.PUTFIELD,
344: classPathNameBeanInfo,
345: "groupingPropertyDescriptor",
346: "[L" + GROUPING_PROPERTY_DESCRIPTOR_CLASS_PATH
347: + ";");
348: mv.visitInsn(Opcodes.RETURN);
349: mv.visitMaxs(8, 2);
350: mv.visitEnd();
351:
352: //public BeanDescriptor getBeanDescriptor()
353: //{
354: // return new BeanDescriptor(AllTypesFunction.class);
355: //}
356: mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "getBeanDescriptor",
357: "()Ljava/beans/BeanDescriptor;", null, null);
358: mv.visitCode();
359: mv.visitTypeInsn(Opcodes.NEW, "java/beans/BeanDescriptor");
360: mv.visitInsn(Opcodes.DUP);
361: mv.visitLdcInsn(Type.getType("L" + classPathName + ";"));
362: mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
363: "java/beans/BeanDescriptor", "<init>",
364: "(Ljava/lang/Class;)V");
365: mv.visitInsn(Opcodes.ARETURN);
366: mv.visitMaxs(3, 1);
367: mv.visitEnd();
368:
369: //public PropertyDescriptor[] getPropertyDescriptors()
370: //{
371: // return groupingPropertyDescriptor;
372: //}
373: mv = cw.visitMethod(Opcodes.ACC_PUBLIC,
374: "getPropertyDescriptors",
375: "()[Ljava/beans/PropertyDescriptor;", null, null);
376: mv.visitCode();
377: mv.visitVarInsn(Opcodes.ALOAD, 0);
378: mv
379: .visitFieldInsn(Opcodes.GETFIELD,
380: classPathNameBeanInfo,
381: "groupingPropertyDescriptor",
382: "[L" + GROUPING_PROPERTY_DESCRIPTOR_CLASS_PATH
383: + ";");
384: mv.visitInsn(Opcodes.ARETURN);
385: mv.visitMaxs(1, 1);
386: mv.visitEnd();
387:
388: cw.visitEnd();
389:
390: return cw.toByteArray();
391: }
392:
393: private void addPropertyDescriptor(@NotNull
394: String classPathName, @NotNull
395: Property property, @NotNull
396: MethodVisitor mv) {
397: mv.visitVarInsn(Opcodes.ALOAD, 1);
398: mv.visitTypeInsn(Opcodes.NEW,
399: GROUPING_PROPERTY_DESCRIPTOR_CLASS_PATH);
400: mv.visitInsn(Opcodes.DUP);
401: mv.visitLdcInsn(property.getName());
402: mv.visitLdcInsn(property.getGroup());
403: mv.visitInsn(Opcodes.ICONST_0);
404: if (property.getSortingID() == -1) {
405: mv.visitInsn(Opcodes.ICONST_M1);
406: } else {
407: mv.visitIntInsn(Opcodes.BIPUSH, property.getSortingID());
408: }
409: mv.visitLdcInsn(Type.getType("L" + classPathName + ";"));
410: mv
411: .visitMethodInsn(Opcodes.INVOKESPECIAL,
412: GROUPING_PROPERTY_DESCRIPTOR_CLASS_PATH,
413: "<init>",
414: "(Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Class;)V");
415: mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
416: "java/util/ArrayList", "add", "(Ljava/lang/Object;)Z");
417: mv.visitInsn(Opcodes.POP);
418: }
419: }
|