You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@apex.apache.org by th...@apache.org on 2016/10/21 16:58:29 UTC
[4/4] apex-malhar git commit: APEXMALHAR-1818 Adding
BeanClassGenerator for dynamically creating class
APEXMALHAR-1818 Adding BeanClassGenerator for dynamically creating class
Project: http://git-wip-us.apache.org/repos/asf/apex-malhar/repo
Commit: http://git-wip-us.apache.org/repos/asf/apex-malhar/commit/b968e4c3
Tree: http://git-wip-us.apache.org/repos/asf/apex-malhar/tree/b968e4c3
Diff: http://git-wip-us.apache.org/repos/asf/apex-malhar/diff/b968e4c3
Branch: refs/heads/master
Commit: b968e4c39e983fa24e4c1136eafdd1dd45b7cafb
Parents: a059805
Author: Chandni Singh <cs...@apache.org>
Authored: Thu Oct 13 17:33:18 2016 +0530
Committer: Chinmay Kolhatkar <ch...@datatorrent.com>
Committed: Fri Oct 21 21:24:30 2016 +0530
----------------------------------------------------------------------
.../malhar/sql/codegen/BeanClassGenerator.java | 796 +++++++++++++++++++
.../sql/codegen/BeanClassGeneratorTest.java | 188 +++++
2 files changed, 984 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/apex-malhar/blob/b968e4c3/sql/src/main/java/org/apache/apex/malhar/sql/codegen/BeanClassGenerator.java
----------------------------------------------------------------------
diff --git a/sql/src/main/java/org/apache/apex/malhar/sql/codegen/BeanClassGenerator.java b/sql/src/main/java/org/apache/apex/malhar/sql/codegen/BeanClassGenerator.java
new file mode 100644
index 0000000..ffeafb5
--- /dev/null
+++ b/sql/src/main/java/org/apache/apex/malhar/sql/codegen/BeanClassGenerator.java
@@ -0,0 +1,796 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.apex.malhar.sql.codegen;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import org.codehaus.jettison.json.JSONException;
+
+import org.apache.apex.malhar.sql.schema.TupleSchemaRegistry;
+import org.apache.commons.io.IOUtils;
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.FSDataOutputStream;
+import org.apache.xbean.asm5.ClassWriter;
+import org.apache.xbean.asm5.Opcodes;
+import org.apache.xbean.asm5.tree.ClassNode;
+import org.apache.xbean.asm5.tree.FieldInsnNode;
+import org.apache.xbean.asm5.tree.FieldNode;
+import org.apache.xbean.asm5.tree.InsnNode;
+import org.apache.xbean.asm5.tree.IntInsnNode;
+import org.apache.xbean.asm5.tree.JumpInsnNode;
+import org.apache.xbean.asm5.tree.LabelNode;
+import org.apache.xbean.asm5.tree.LdcInsnNode;
+import org.apache.xbean.asm5.tree.MethodInsnNode;
+import org.apache.xbean.asm5.tree.MethodNode;
+import org.apache.xbean.asm5.tree.TypeInsnNode;
+import org.apache.xbean.asm5.tree.VarInsnNode;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+/**
+ * Creates a bean class on fly.
+ */
+@InterfaceStability.Evolving
+public class BeanClassGenerator
+{
+ public static final ImmutableMap<String, Character> PRIMITIVE_TYPES;
+
+ static {
+ Map<String, Character> types = Maps.newHashMap();
+ types.put("boolean", 'Z');
+ types.put("char", 'C');
+ types.put("byte", 'B');
+ types.put("short", 'S');
+ types.put("int", 'I');
+ types.put("float", 'F');
+ types.put("long", 'J');
+ types.put("double", 'D');
+ PRIMITIVE_TYPES = ImmutableMap.copyOf(types);
+ }
+
+ /**
+ * Creates a class from give field information and returns byte array of compiled class.
+ *
+ * @param fqcn fully qualified class name
+ * @param fieldList field list for which POJO needs to be generated.
+ *
+ * @return byte[] representing compiled class.
+ * @throws IOException
+ * @throws JSONException
+ */
+ public static byte[] createAndWriteBeanClass(String fqcn, List<TupleSchemaRegistry.SQLFieldInfo> fieldList)
+ throws IOException, JSONException
+ {
+ return createAndWriteBeanClass(fqcn, fieldList, null);
+ }
+
+ /**
+ * Creates a class from given field information and writes it to the output stream. Also returns byte[] of compiled
+ * class
+ *
+ * @param fqcn fully qualified class name
+ * @param fieldList field list describing the class
+ * @param outputStream stream to which the class is persisted
+ * @throws JSONException
+ * @throws IOException
+ */
+ @SuppressWarnings("unchecked")
+ public static byte[] createAndWriteBeanClass(String fqcn, List<TupleSchemaRegistry.SQLFieldInfo> fieldList,
+ FSDataOutputStream outputStream) throws JSONException, IOException
+ {
+ ClassNode classNode = new ClassNode();
+
+ classNode.version = Opcodes.V1_6; //generated class will only run on JRE 1.6 or above
+ classNode.access = Opcodes.ACC_PUBLIC;
+
+ classNode.name = fqcn.replace('.', '/');
+ classNode.superName = "java/lang/Object";
+
+ // add default constructor
+ addDefaultConstructor(classNode);
+
+ //for each field in json add a field to this class and a getter and setter for it.
+
+ for (TupleSchemaRegistry.SQLFieldInfo fieldInfo : fieldList) {
+ String fieldName = fieldInfo.getColumnName();
+ String fieldType = fieldInfo.getType().getJavaType().getName();
+ String fieldJavaType = getJavaType(fieldType);
+
+ // Add private field
+ FieldNode fieldNode = new FieldNode(Opcodes.ACC_PRIVATE, fieldName, fieldJavaType, null, null);
+ classNode.fields.add(fieldNode);
+
+ String fieldNameForMethods = Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
+
+ switch (fieldType) {
+ case "boolean":
+ addIntGetterNSetter(classNode, fieldName, fieldNameForMethods, fieldJavaType, true);
+ break;
+ case "byte":
+ case "char":
+ case "short":
+ case "int":
+ addIntGetterNSetter(classNode, fieldName, fieldNameForMethods, fieldJavaType, false);
+ break;
+ case "long":
+ addLongGetterNSetter(classNode, fieldName, fieldNameForMethods, fieldJavaType);
+ break;
+ case "float":
+ addFloatGetterNSetter(classNode, fieldName, fieldNameForMethods, fieldJavaType);
+ break;
+ case "double":
+ addDoubleGetterNSetter(classNode, fieldName, fieldNameForMethods, fieldJavaType);
+ break;
+ default:
+ if (fieldJavaType.equals(getJavaType("java.util.Date"))) {
+ addDateFields(classNode, fieldName, fieldNameForMethods, "java/util/Date");
+ } else {
+ addObjectGetterNSetter(classNode, fieldName, fieldNameForMethods, fieldJavaType);
+ }
+ break;
+ }
+ }
+
+ addToStringMethod(classNode, fieldList);
+ addHashCodeMethod(classNode, fieldList);
+ addEqualsMethod(classNode, fieldList);
+
+ //Write the class
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+ classNode.accept(cw);
+ cw.visitEnd();
+
+ byte[] classBytes = cw.toByteArray();
+
+ if (outputStream != null) {
+ outputStream.write(classBytes);
+ outputStream.close();
+ }
+
+ return classBytes;
+ }
+
+ private static void addDefaultConstructor(ClassNode classNode)
+ {
+ MethodNode constructorNode = new MethodNode(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
+ constructorNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ constructorNode.instructions
+ .add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false));
+ constructorNode.instructions.add(new InsnNode(Opcodes.RETURN));
+ classNode.methods.add(constructorNode);
+ }
+
+ /**
+ * Date field is explicitly handled and provided with 3 variants of types of same data.
+ * 1. java.util.Date format
+ * 2. long - Epoc time in ms
+ * 3. int - Epoc time in sec rounded to date
+ *
+ * This is purposefully done because SQL operations on Date etc happens on long or int based on whether its a SQL DATE
+ * field OR SQL TIMESTAMP field. Hence to cater to that 2 more variant of the same data is added to the POJO.
+ */
+ @SuppressWarnings("unchecked")
+ private static void addDateFields(ClassNode classNode, String fieldName, String fieldNameForMethods, String type)
+ {
+ FieldNode fieldNodeSec = new FieldNode(Opcodes.ACC_PRIVATE, fieldName + "Sec", getJavaType("java.lang.Integer"),
+ null, null);
+ classNode.fields.add(fieldNodeSec);
+ FieldNode fieldNodeMs = new FieldNode(Opcodes.ACC_PRIVATE, fieldName + "Ms", getJavaType("java.lang.Long"), null,
+ null);
+ classNode.fields.add(fieldNodeMs);
+
+ // Create getter for Date
+ MethodNode getterNodeDate = new MethodNode(Opcodes.ACC_PUBLIC, "get" + fieldNameForMethods, "()L" + type + ";",
+ null, null);
+ getterNodeDate.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ getterNodeDate.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, fieldName, "L" + type + ";"));
+ getterNodeDate.instructions.add(new InsnNode(Opcodes.ARETURN));
+ classNode.methods.add(getterNodeDate);
+
+ // Create getter for Sec
+ MethodNode getterNodeSec = new MethodNode(Opcodes.ACC_PUBLIC, "get" + fieldNameForMethods + "Sec",
+ "()Ljava/lang/Integer;", null, null);
+ getterNodeSec.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ getterNodeSec.instructions
+ .add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, fieldName + "Sec", "Ljava/lang/Integer;"));
+ getterNodeSec.instructions.add(new InsnNode(Opcodes.ARETURN));
+ classNode.methods.add(getterNodeSec);
+
+ // Create getter for Ms
+ MethodNode getterNodeMs = new MethodNode(Opcodes.ACC_PUBLIC, "get" + fieldNameForMethods + "Ms",
+ "()Ljava/lang/Long;", null, null);
+ getterNodeMs.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ getterNodeMs.instructions
+ .add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, fieldName + "Ms", "Ljava/lang/Long;"));
+ getterNodeMs.instructions.add(new InsnNode(Opcodes.ARETURN));
+ classNode.methods.add(getterNodeMs);
+
+ // Create setter for Date
+ MethodNode setterNodeDate = new MethodNode(Opcodes.ACC_PUBLIC, "set" + fieldNameForMethods,
+ "(L" + type + ";)V", null, null);
+ setterNodeDate.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ setterNodeDate.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ setterNodeDate.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, fieldName, "L" + type + ";"));
+
+ setterNodeDate.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ setterNodeDate.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ setterNodeDate.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, type, "getTime", "()J", false));
+ setterNodeDate.instructions.add(new LdcInsnNode(new Long(1000)));
+ setterNodeDate.instructions.add(new InsnNode(Opcodes.LDIV));
+ setterNodeDate.instructions.add(new InsnNode(Opcodes.L2I));
+ setterNodeDate.instructions
+ .add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false));
+ setterNodeDate.instructions
+ .add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, fieldName + "Sec", "Ljava/lang/Integer;"));
+
+ setterNodeDate.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ setterNodeDate.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ setterNodeDate.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, type, "getTime", "()J", false));
+ setterNodeDate.instructions
+ .add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false));
+ setterNodeDate.instructions
+ .add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, fieldName + "Ms", "Ljava/lang/Long;"));
+
+ setterNodeDate.instructions.add(new InsnNode(Opcodes.RETURN));
+ classNode.methods.add(setterNodeDate);
+
+ // Create setter for Sec
+ MethodNode setterNodeSec = new MethodNode(Opcodes.ACC_PUBLIC, "set" + fieldNameForMethods + "Sec",
+ "(Ljava/lang/Integer;)V", null, null);
+ setterNodeSec.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ setterNodeSec.instructions.add(new TypeInsnNode(Opcodes.NEW, type));
+ setterNodeSec.instructions.add(new InsnNode(Opcodes.DUP));
+ setterNodeSec.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ setterNodeSec.instructions
+ .add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false));
+ setterNodeSec.instructions.add(new InsnNode(Opcodes.I2L));
+ setterNodeSec.instructions.add(new LdcInsnNode(new Long(1000)));
+ setterNodeSec.instructions.add(new InsnNode(Opcodes.LMUL));
+ setterNodeSec.instructions
+ .add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/Date", "<init>", "(J)V", false));
+ setterNodeSec.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, fieldName, "L" + type + ";"));
+
+ setterNodeSec.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ setterNodeSec.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ setterNodeSec.instructions
+ .add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, fieldName + "Sec", "Ljava/lang/Integer;"));
+
+ setterNodeSec.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ setterNodeSec.instructions.add(new TypeInsnNode(Opcodes.NEW, "java/lang/Long"));
+ setterNodeSec.instructions.add(new InsnNode(Opcodes.DUP));
+ setterNodeSec.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ setterNodeSec.instructions
+ .add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false));
+ setterNodeSec.instructions.add(new InsnNode(Opcodes.I2L));
+ setterNodeSec.instructions.add(new LdcInsnNode(new Long(1000)));
+ setterNodeSec.instructions.add(new InsnNode(Opcodes.LMUL));
+ setterNodeSec.instructions
+ .add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/Long", "<init>", "(J)V", false));
+ setterNodeSec.instructions
+ .add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, fieldName + "Ms", "Ljava/lang/Long;"));
+
+ setterNodeSec.instructions.add(new InsnNode(Opcodes.RETURN));
+ classNode.methods.add(setterNodeSec);
+
+ // Create setter for Ms
+ MethodNode setterNodeMs = new MethodNode(Opcodes.ACC_PUBLIC, "set" + fieldNameForMethods + "Ms",
+ "(Ljava/lang/Long;)V", null, null);
+ setterNodeMs.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ setterNodeMs.instructions.add(new TypeInsnNode(Opcodes.NEW, type));
+ setterNodeMs.instructions.add(new InsnNode(Opcodes.DUP));
+ setterNodeMs.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ setterNodeMs.instructions
+ .add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false));
+ setterNodeMs.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/Date", "<init>", "(J)V", false));
+ setterNodeMs.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, fieldName, "L" + type + ";"));
+
+ setterNodeMs.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ setterNodeMs.instructions.add(new TypeInsnNode(Opcodes.NEW, "java/lang/Integer"));
+ setterNodeMs.instructions.add(new InsnNode(Opcodes.DUP));
+ setterNodeMs.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ setterNodeMs.instructions
+ .add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false));
+ setterNodeMs.instructions.add(new LdcInsnNode(new Long(1000)));
+ setterNodeMs.instructions.add(new InsnNode(Opcodes.LDIV));
+ setterNodeMs.instructions.add(new InsnNode(Opcodes.L2I));
+ setterNodeMs.instructions
+ .add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/Integer", "<init>", "(I)V", false));
+ setterNodeMs.instructions
+ .add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, fieldName + "Sec", "Ljava/lang/Integer;"));
+
+ setterNodeMs.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ setterNodeMs.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ setterNodeMs.instructions
+ .add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, fieldName + "Ms", "Ljava/lang/Long;"));
+
+ setterNodeMs.instructions.add(new InsnNode(Opcodes.RETURN));
+ classNode.methods.add(setterNodeMs);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void addIntGetterNSetter(ClassNode classNode, String fieldName, String fieldNameForMethods,
+ String fieldJavaType, boolean isBoolean)
+ {
+ // Create getter
+ String getterSignature = "()" + fieldJavaType;
+ MethodNode getterNode = new MethodNode(Opcodes.ACC_PUBLIC, (isBoolean ? "is" : "get") + fieldNameForMethods,
+ getterSignature, null, null);
+ getterNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ getterNode.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, fieldName, fieldJavaType));
+ getterNode.instructions.add(new InsnNode(Opcodes.IRETURN));
+ classNode.methods.add(getterNode);
+
+ // Create setter
+ String setterSignature = '(' + fieldJavaType + ')' + 'V';
+ MethodNode setterNode = new MethodNode(Opcodes.ACC_PUBLIC, "set" + fieldNameForMethods, setterSignature, null,
+ null);
+ setterNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ setterNode.instructions.add(new VarInsnNode(Opcodes.ILOAD, 1));
+ setterNode.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, fieldName, fieldJavaType));
+ setterNode.instructions.add(new InsnNode(Opcodes.RETURN));
+ classNode.methods.add(setterNode);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void addLongGetterNSetter(ClassNode classNode, String fieldName, String fieldNameForMethods,
+ String fieldJavaType)
+ {
+ // Create getter
+ String getterSignature = "()" + fieldJavaType;
+ MethodNode getterNode = new MethodNode(Opcodes.ACC_PUBLIC, "get" + fieldNameForMethods, getterSignature, null,
+ null);
+ getterNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ getterNode.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, fieldName, fieldJavaType));
+ getterNode.instructions.add(new InsnNode(Opcodes.LRETURN));
+ classNode.methods.add(getterNode);
+
+ // Create setter
+ String setterSignature = '(' + fieldJavaType + ')' + 'V';
+ MethodNode setterNode = new MethodNode(Opcodes.ACC_PUBLIC, "set" + fieldNameForMethods, setterSignature, null,
+ null);
+ setterNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ setterNode.instructions.add(new VarInsnNode(Opcodes.LLOAD, 1));
+ setterNode.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, fieldName, fieldJavaType));
+ setterNode.instructions.add(new InsnNode(Opcodes.RETURN));
+ classNode.methods.add(setterNode);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void addFloatGetterNSetter(ClassNode classNode, String fieldName, String fieldNameForMethods,
+ String fieldJavaType)
+ {
+ // Create getter
+ String getterSignature = "()" + fieldJavaType;
+ MethodNode getterNode = new MethodNode(Opcodes.ACC_PUBLIC, "get" + fieldNameForMethods, getterSignature, null,
+ null);
+ getterNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ getterNode.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, fieldName, fieldJavaType));
+ getterNode.instructions.add(new InsnNode(Opcodes.FRETURN));
+ classNode.methods.add(getterNode);
+
+ // Create setter
+ String setterSignature = '(' + fieldJavaType + ')' + 'V';
+ MethodNode setterNode = new MethodNode(Opcodes.ACC_PUBLIC, "set" + fieldNameForMethods, setterSignature, null,
+ null);
+ setterNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ setterNode.instructions.add(new VarInsnNode(Opcodes.FLOAD, 1));
+ setterNode.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, fieldName, fieldJavaType));
+ setterNode.instructions.add(new InsnNode(Opcodes.RETURN));
+ classNode.methods.add(setterNode);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void addDoubleGetterNSetter(ClassNode classNode, String fieldName, String fieldNameForMethods,
+ String fieldJavaType)
+ {
+ // Create getter
+ String getterSignature = "()" + fieldJavaType;
+ MethodNode getterNode = new MethodNode(Opcodes.ACC_PUBLIC, "get" + fieldNameForMethods, getterSignature, null,
+ null);
+ getterNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ getterNode.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, fieldName, fieldJavaType));
+ getterNode.instructions.add(new InsnNode(Opcodes.DRETURN));
+ classNode.methods.add(getterNode);
+
+ // Create setter
+ String setterSignature = '(' + fieldJavaType + ')' + 'V';
+ MethodNode setterNode = new MethodNode(Opcodes.ACC_PUBLIC, "set" + fieldNameForMethods, setterSignature, null,
+ null);
+ setterNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ setterNode.instructions.add(new VarInsnNode(Opcodes.DLOAD, 1));
+ setterNode.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, fieldName, fieldJavaType));
+ setterNode.instructions.add(new InsnNode(Opcodes.RETURN));
+ classNode.methods.add(setterNode);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void addObjectGetterNSetter(ClassNode classNode, String fieldName, String fieldNameForMethods,
+ String fieldJavaType)
+ {
+ // Create getter
+ String getterSignature = "()" + fieldJavaType;
+ MethodNode getterNode = new MethodNode(Opcodes.ACC_PUBLIC, "get" + fieldNameForMethods, getterSignature, null,
+ null);
+ getterNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ getterNode.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, fieldName, fieldJavaType));
+ getterNode.instructions.add(new InsnNode(Opcodes.ARETURN));
+ classNode.methods.add(getterNode);
+
+ // Create setter
+ String setterSignature = '(' + fieldJavaType + ')' + 'V';
+ MethodNode setterNode = new MethodNode(Opcodes.ACC_PUBLIC, "set" + fieldNameForMethods, setterSignature, null,
+ null);
+ setterNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ setterNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ setterNode.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, fieldName, fieldJavaType));
+ setterNode.instructions.add(new InsnNode(Opcodes.RETURN));
+ classNode.methods.add(setterNode);
+ }
+
+ /**
+ * Adds a toString method to underlying class. Uses StringBuilder to generate the final string.
+ *
+ * @param classNode
+ * @param fieldList
+ * @throws JSONException
+ */
+ @SuppressWarnings("unchecked")
+ private static void addToStringMethod(ClassNode classNode, List<TupleSchemaRegistry.SQLFieldInfo> fieldList)
+ throws JSONException
+ {
+ MethodNode toStringNode = new MethodNode(Opcodes.ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null);
+ toStringNode.visitAnnotation("Ljava/lang/Override;", true);
+
+ toStringNode.instructions.add(new TypeInsnNode(Opcodes.NEW, "java/lang/StringBuilder"));
+ toStringNode.instructions.add(new InsnNode(Opcodes.DUP));
+ toStringNode.instructions.add(new LdcInsnNode(classNode.name + "{"));
+ toStringNode.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder",
+ "<init>", "(Ljava/lang/String;)V", false));
+ toStringNode.instructions.add(new VarInsnNode(Opcodes.ASTORE, 1));
+ toStringNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+
+ for (int i = 0; i < fieldList.size(); i++) {
+ TupleSchemaRegistry.SQLFieldInfo info = fieldList.get(i);
+ String fieldName = info.getColumnName();
+ String fieldType = info.getType().getJavaType().getName();
+ String fieldJavaType = getJavaType(fieldType);
+
+ if (i != 0) {
+ toStringNode.instructions.add(new LdcInsnNode(", "));
+ toStringNode.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false));
+ }
+
+ toStringNode.instructions.add(new LdcInsnNode(fieldName + "="));
+ toStringNode.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false));
+ toStringNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ toStringNode.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, fieldName, fieldJavaType));
+
+ // There is no StringBuilder.append method for short and byte. It takes it as int.
+ if (fieldJavaType.equals("S") || fieldJavaType.equals("B")) {
+ fieldJavaType = "I";
+ }
+
+ Character pchar = PRIMITIVE_TYPES.get(fieldType);
+ if (pchar == null) {
+ // It's not a primitive type. StringBuilder.append method signature takes Object type.
+ fieldJavaType = "Ljava/lang/Object;";
+ }
+
+ toStringNode.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+ "(" + fieldJavaType + ")Ljava/lang/StringBuilder;", false));
+ }
+
+ toStringNode.instructions.add(new LdcInsnNode("}"));
+ toStringNode.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false));
+
+ toStringNode.instructions.add(new InsnNode(Opcodes.POP));
+ toStringNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ toStringNode.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString",
+ "()Ljava/lang/String;", false));
+ toStringNode.instructions.add(new InsnNode(Opcodes.ARETURN));
+
+ classNode.methods.add(toStringNode);
+ }
+
+ /**
+ * This will add a hashCode method for class being generated. <br>
+ * Algorithm is as follows: <br>
+ * <i><p>
+ * int hashCode = 7;
+ * for (field: all fields) {
+ * hashCode = 23 * hashCode + field.hashCode()
+ * }
+ * </p></i>
+ * <br>
+ * <b> For primitive field, hashcode implemenented is similar to the one present in its wrapper class. </b>
+ *
+ * @param classNode
+ * @param fieldList
+ * @throws JSONException
+ */
+ @SuppressWarnings("unchecked")
+ private static void addHashCodeMethod(ClassNode classNode, List<TupleSchemaRegistry.SQLFieldInfo> fieldList)
+ throws JSONException
+ {
+ MethodNode hashCodeNode = new MethodNode(Opcodes.ACC_PUBLIC, "hashCode", "()I", null, null);
+ hashCodeNode.visitAnnotation("Ljava/lang/Override;", true);
+
+ hashCodeNode.instructions.add(new IntInsnNode(Opcodes.BIPUSH, 7));
+ hashCodeNode.instructions.add(new VarInsnNode(Opcodes.ISTORE, 1));
+
+ for (TupleSchemaRegistry.SQLFieldInfo fieldInfo : fieldList) {
+ String fieldName = fieldInfo.getColumnName();
+ String fieldType = fieldInfo.getType().getJavaType().getName();
+ String fieldJavaType = getJavaType(fieldType);
+
+ hashCodeNode.instructions.add(new IntInsnNode(Opcodes.BIPUSH, 23));
+ hashCodeNode.instructions.add(new VarInsnNode(Opcodes.ILOAD, 1));
+ hashCodeNode.instructions.add(new InsnNode(Opcodes.IMUL));
+ hashCodeNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ hashCodeNode.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, fieldName, fieldJavaType));
+
+ switch (fieldType) {
+ case "boolean":
+ LabelNode falseNode = new LabelNode();
+ LabelNode trueNode = new LabelNode();
+ hashCodeNode.instructions.add(new JumpInsnNode(Opcodes.IFEQ, falseNode));
+ hashCodeNode.instructions.add(new IntInsnNode(Opcodes.SIPUSH, 1231));
+ hashCodeNode.instructions.add(new JumpInsnNode(Opcodes.GOTO, trueNode));
+ hashCodeNode.instructions.add(falseNode);
+ hashCodeNode.instructions.add(new IntInsnNode(Opcodes.SIPUSH, 1237));
+ hashCodeNode.instructions.add(trueNode);
+ break;
+ case "byte":
+ case "char":
+ case "short":
+ case "int":
+ break;
+ case "float":
+ hashCodeNode.instructions
+ .add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/Float", "floatToIntBits", "(F)I", false));
+ break;
+ case "long":
+ hashCodeNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ hashCodeNode.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, fieldName, fieldJavaType));
+ hashCodeNode.instructions.add(new IntInsnNode(Opcodes.BIPUSH, 32));
+ hashCodeNode.instructions.add(new InsnNode(Opcodes.LUSHR));
+ hashCodeNode.instructions.add(new InsnNode(Opcodes.LXOR));
+ hashCodeNode.instructions.add(new InsnNode(Opcodes.L2I));
+ break;
+ case "double":
+ hashCodeNode.instructions
+ .add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/Double", "doubleToLongBits", "(D)J", false));
+ hashCodeNode.instructions.add(new InsnNode(Opcodes.DUP2));
+ hashCodeNode.instructions.add(new VarInsnNode(Opcodes.LSTORE, 2));
+ hashCodeNode.instructions.add(new VarInsnNode(Opcodes.LLOAD, 2));
+ hashCodeNode.instructions.add(new IntInsnNode(Opcodes.BIPUSH, 32));
+ hashCodeNode.instructions.add(new InsnNode(Opcodes.LUSHR));
+ hashCodeNode.instructions.add(new InsnNode(Opcodes.LXOR));
+ hashCodeNode.instructions.add(new InsnNode(Opcodes.L2I));
+ break;
+ default:
+ String objectOwnerType = fieldType.replace('.', '/');
+ LabelNode nullNode = new LabelNode();
+ LabelNode continueNode = new LabelNode();
+ hashCodeNode.instructions.add(new JumpInsnNode(Opcodes.IFNULL, nullNode));
+ hashCodeNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ hashCodeNode.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, fieldName, fieldJavaType));
+ hashCodeNode.instructions
+ .add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, objectOwnerType, "hashCode", "()I", false));
+ hashCodeNode.instructions.add(new JumpInsnNode(Opcodes.GOTO, continueNode));
+ hashCodeNode.instructions.add(nullNode);
+ hashCodeNode.instructions.add(new InsnNode(Opcodes.ICONST_0));
+ hashCodeNode.instructions.add(continueNode);
+ break;
+ }
+ hashCodeNode.instructions.add(new InsnNode(Opcodes.IADD));
+ hashCodeNode.instructions.add(new VarInsnNode(Opcodes.ISTORE, 1));
+ }
+ hashCodeNode.instructions.add(new VarInsnNode(Opcodes.ILOAD, 1));
+ hashCodeNode.instructions.add(new InsnNode(Opcodes.IRETURN));
+
+ classNode.methods.add(hashCodeNode);
+ }
+
+ /**
+ * Adds a equals method to underlying class. <br>
+ * Algorithm is as follows: <br>
+ * <i><p>
+ * if (this == other) return true;
+ * if (other == null) return false;
+ * if (other is not instanceof <this class>) return false;
+ * for (field: all fields) {
+ * if (other.getField() != this.field) return false;
+ * }
+ * return true;
+ * </p></i>
+ * <br>
+ *
+ * @param classNode
+ * @param fieldList
+ * @throws JSONException
+ */
+ @SuppressWarnings("unchecked")
+ private static void addEqualsMethod(ClassNode classNode, List<TupleSchemaRegistry.SQLFieldInfo> fieldList)
+ throws JSONException
+ {
+ MethodNode equalsNode = new MethodNode(Opcodes.ACC_PUBLIC, "equals", "(Ljava/lang/Object;)Z", null, null);
+ equalsNode.visitAnnotation("Ljava/lang/Override;", true);
+
+ LabelNode l0 = new LabelNode();
+ LabelNode l1 = new LabelNode();
+ LabelNode l2 = new LabelNode();
+ LabelNode l3 = new LabelNode();
+ LabelNode l4 = new LabelNode();
+
+ equalsNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+
+ // if (this == other) return true;
+ equalsNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ equalsNode.instructions.add(new JumpInsnNode(Opcodes.IF_ACMPNE, l0));
+ equalsNode.instructions.add(new InsnNode(Opcodes.ICONST_1));
+ equalsNode.instructions.add(new InsnNode(Opcodes.IRETURN));
+
+ equalsNode.instructions.add(l0);
+ // if (other == null) return false;
+ equalsNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ equalsNode.instructions.add(new JumpInsnNode(Opcodes.IFNULL, l1));
+ // if (!(other instanceof <this class>)) return false;
+ equalsNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ equalsNode.instructions.add(new TypeInsnNode(Opcodes.INSTANCEOF, classNode.name));
+ equalsNode.instructions.add(new JumpInsnNode(Opcodes.IFNE, l2));
+
+ equalsNode.instructions.add(l1);
+ equalsNode.instructions.add(new InsnNode(Opcodes.ICONST_0));
+ equalsNode.instructions.add(new InsnNode(Opcodes.IRETURN));
+
+ equalsNode.instructions.add(l2);
+ // Check if it other object can cast to <this class>
+ equalsNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1));
+ equalsNode.instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, classNode.name));
+ equalsNode.instructions.add(new VarInsnNode(Opcodes.ASTORE, 2));
+
+ for (int i = 0; i < fieldList.size(); i++) {
+ boolean isLast = ((i + 1) == fieldList.size());
+ TupleSchemaRegistry.SQLFieldInfo info = fieldList.get(i);
+ String fieldName = info.getColumnName();
+ String fieldType = info.getType().getJavaType().getName();
+ String fieldJavaType = getJavaType(fieldType);
+
+ String getterMethodName = (fieldType.equals("boolean") ? "is" : "get") +
+ Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
+ equalsNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 2));
+ equalsNode.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, classNode.name, getterMethodName,
+ "()" + fieldJavaType, false));
+ equalsNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ equalsNode.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, fieldName, fieldJavaType));
+
+ switch (fieldType) {
+ case "boolean":
+ case "byte":
+ case "char":
+ case "short":
+ case "int":
+ equalsNode.instructions
+ .add(new JumpInsnNode(isLast ? Opcodes.IF_ICMPEQ : Opcodes.IF_ICMPNE, isLast ? l4 : l3));
+ break;
+ case "long":
+ equalsNode.instructions.add(new InsnNode(Opcodes.LCMP));
+ equalsNode.instructions.add(new JumpInsnNode(isLast ? Opcodes.IFEQ : Opcodes.IFNE, isLast ? l4 : l3));
+ break;
+ case "float":
+ equalsNode.instructions.add(new InsnNode(Opcodes.FCMPL));
+ equalsNode.instructions.add(new JumpInsnNode(isLast ? Opcodes.IFEQ : Opcodes.IFNE, isLast ? l4 : l3));
+ break;
+ case "double":
+ equalsNode.instructions.add(new InsnNode(Opcodes.DCMPL));
+ equalsNode.instructions.add(new JumpInsnNode(isLast ? Opcodes.IFEQ : Opcodes.IFNE, isLast ? l4 : l3));
+ break;
+ default:
+ String objectOwnerType = fieldType.replace('.', '/');
+
+ LabelNode nonNullNode = new LabelNode();
+ LabelNode continueNode = new LabelNode();
+
+ equalsNode.instructions.add(new JumpInsnNode(Opcodes.IFNONNULL, nonNullNode));
+ equalsNode.instructions.add(new JumpInsnNode(isLast ? Opcodes.IFNULL : Opcodes.IFNONNULL, isLast ? l4 : l3));
+
+ equalsNode.instructions.add(new JumpInsnNode(Opcodes.GOTO, continueNode));
+
+ equalsNode.instructions.add(nonNullNode);
+ equalsNode.instructions.add(new InsnNode(Opcodes.POP));
+ equalsNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
+ equalsNode.instructions.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, fieldName, fieldJavaType));
+ equalsNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 2));
+ equalsNode.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, classNode.name, getterMethodName,
+ "()" + fieldJavaType, false));
+ equalsNode.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, objectOwnerType, "equals",
+ "(Ljava/lang/Object;)Z", false));
+ equalsNode.instructions.add(new JumpInsnNode(isLast ? Opcodes.IFNE : Opcodes.IFEQ, isLast ? l4 : l3));
+
+ equalsNode.instructions.add(continueNode);
+ break;
+ }
+ }
+
+ equalsNode.instructions.add(l3);
+ equalsNode.instructions.add(new InsnNode(Opcodes.ICONST_0));
+ equalsNode.instructions.add(new InsnNode(Opcodes.IRETURN));
+
+ equalsNode.instructions.add(l4);
+ equalsNode.instructions.add(new InsnNode(Opcodes.ICONST_1));
+ equalsNode.instructions.add(new InsnNode(Opcodes.IRETURN));
+
+ classNode.methods.add(equalsNode);
+ }
+
+ private static String getJavaType(String fieldType)
+ {
+ Character pchar = PRIMITIVE_TYPES.get(fieldType);
+ if (pchar != null) {
+ //it is a primitive type
+ return Character.toString(pchar);
+ }
+ //non-primitive so find the internal name of the class.
+ return 'L' + fieldType.replace('.', '/') + ';';
+ }
+
+ /**
+ * Given the class name it reads and loads the class from the input stream.
+ *
+ * @param fqcn fully qualified class name.
+ * @param inputStream stream from which class is read.
+ * @return loaded class
+ * @throws IOException
+ */
+ public static Class<?> readBeanClass(String fqcn, FSDataInputStream inputStream) throws IOException
+ {
+ byte[] bytes = IOUtils.toByteArray(inputStream);
+ inputStream.close();
+ return new ByteArrayClassLoader().defineClass(fqcn, bytes);
+ }
+
+ /**
+ * Given the class name it reads and loads the class from given byte array.
+ *
+ * @param fqcn fully qualified class name.
+ * @param inputClass byte[] from which class is read.
+ * @return loaded class
+ * @throws IOException
+ */
+ public static Class<?> readBeanClass(String fqcn, byte[] inputClass) throws IOException
+ {
+ return new ByteArrayClassLoader().defineClass(fqcn, inputClass);
+ }
+
+ private static class ByteArrayClassLoader extends ClassLoader
+ {
+ Class<?> defineClass(String name, byte[] ba)
+ {
+ return defineClass(name, ba, 0, ba.length);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/apex-malhar/blob/b968e4c3/sql/src/test/java/org/apache/apex/malhar/sql/codegen/BeanClassGeneratorTest.java
----------------------------------------------------------------------
diff --git a/sql/src/test/java/org/apache/apex/malhar/sql/codegen/BeanClassGeneratorTest.java b/sql/src/test/java/org/apache/apex/malhar/sql/codegen/BeanClassGeneratorTest.java
new file mode 100644
index 0000000..8fcb7f8
--- /dev/null
+++ b/sql/src/test/java/org/apache/apex/malhar/sql/codegen/BeanClassGeneratorTest.java
@@ -0,0 +1,188 @@
+/**
+ * Copyright (c) 2015 DataTorrent, Inc.
+ * All rights reserved.
+ */
+package org.apache.apex.malhar.sql.codegen;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Date;
+
+import org.codehaus.jettison.json.JSONException;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import org.apache.apex.malhar.sql.schema.TupleSchemaRegistry;
+
+import static org.junit.Assert.assertEquals;
+
+public class BeanClassGeneratorTest
+{
+
+ protected class TestMeta extends TestWatcher
+ {
+ String generatedDir;
+
+ @Override
+ protected void starting(Description description)
+ {
+ super.starting(description);
+ generatedDir = "target/" + description.getClassName() + "/" + description.getMethodName();
+ }
+
+ @Override
+ protected void finished(Description description)
+ {
+ super.finished(description);
+ }
+ }
+
+ @Rule
+ public TestMeta testMeta = new TestMeta();
+
+ @Test
+ public void test() throws IOException, JSONException, IllegalAccessException, InstantiationException,
+ NoSuchFieldException, NoSuchMethodException, InvocationTargetException
+ {
+ String addressClassName = TupleSchemaRegistry.FQCN_PACKAGE + "Address_v1";
+
+ TupleSchemaRegistry.Schema schema = new TupleSchemaRegistry.Schema();
+ schema.addField("streetNumber", long.class);
+ schema.addField("streetName", String.class);
+ schema.addField("city", String.class);
+ schema.addField("state", String.class);
+ schema.addField("zip", String.class);
+
+ byte[] beanClass = BeanClassGenerator.createAndWriteBeanClass(addressClassName, schema.fieldList);
+
+ Class<?> clazz = BeanClassGenerator.readBeanClass(addressClassName, beanClass);
+
+ Object o = clazz.newInstance();
+ Field f = clazz.getDeclaredField("streetNumber");
+ Assert.assertNotNull(f);
+
+ Method m = clazz.getDeclaredMethod("setStreetNumber", Long.class);
+ m.invoke(o, 343L);
+
+ m = clazz.getMethod("getStreetNumber");
+ Long result = (Long)m.invoke(o);
+
+ assertEquals("reflect getVal invoke", 343, result.longValue());
+ }
+
+ @Test
+ public void testPrimitive() throws IOException, JSONException, IllegalAccessException, InstantiationException,
+ NoSuchFieldException, NoSuchMethodException, InvocationTargetException
+ {
+ String addressClassName = TupleSchemaRegistry.FQCN_PACKAGE + "Energy_v1";
+
+ TupleSchemaRegistry.Schema schema = new TupleSchemaRegistry.Schema();
+ schema.addField("streetNumber", Integer.class);
+ schema.addField("houseNumber", Long.class);
+ schema.addField("condo", Boolean.class);
+ schema.addField("water-usage", Float.class);
+ schema.addField("electricity-usage", Double.class);
+ schema.addField("startDate", Date.class);
+
+ byte[] beanClass = BeanClassGenerator.createAndWriteBeanClass(addressClassName, schema.fieldList);
+
+ Class<?> clazz = BeanClassGenerator.readBeanClass(addressClassName, beanClass);
+
+ Object o = clazz.newInstance();
+ Field f = clazz.getDeclaredField("streetNumber");
+ Assert.assertNotNull(f);
+
+ //int setter and getter
+ Method m = clazz.getDeclaredMethod("setStreetNumber", Integer.class);
+ m.invoke(o, 343);
+ m = clazz.getMethod("getStreetNumber");
+ Integer result = (Integer)m.invoke(o);
+
+ assertEquals("reflect getStreetNumber invoke", 343, result.intValue());
+
+ //long setter and getter
+ m = clazz.getDeclaredMethod("setHouseNumber", Long.class);
+ m.invoke(o, 123L);
+ m = clazz.getMethod("getHouseNumber");
+ Long houseNum = (Long)m.invoke(o);
+
+ assertEquals("reflect getHouseNumber invoke", 123L, houseNum.longValue());
+
+ //boolean setter and getter
+ m = clazz.getDeclaredMethod("setCondo", Boolean.class);
+ m.invoke(o, true);
+ m = clazz.getMethod("getCondo");
+ Boolean isCondo = (Boolean)m.invoke(o);
+
+ assertEquals("reflect getCondo invoke", true, isCondo);
+
+ //float setter and getter
+ m = clazz.getDeclaredMethod("setWater-usage", Float.class);
+ m.invoke(o, 88.34F);
+ m = clazz.getMethod("getWater-usage");
+ Float waterUsage = (Float)m.invoke(o);
+
+ assertEquals("reflect getWaterUsage invoke", 88.34F, waterUsage.floatValue(), 0);
+
+ //double setter and getter
+ m = clazz.getDeclaredMethod("setElectricity-usage", Double.class);
+ m.invoke(o, 88.343243);
+ m = clazz.getMethod("getElectricity-usage");
+ Double electricityUsage = (Double)m.invoke(o);
+
+ assertEquals("reflect getWaterUsage invoke", 88.343243, electricityUsage, 0);
+
+ Date now = new Date();
+ m = clazz.getDeclaredMethod("setStartDate", Date.class);
+ m.invoke(o, now);
+
+ m = clazz.getMethod("getStartDate");
+ Date startDate = (Date)m.invoke(o);
+ assertEquals("reflect getStartDate invoke", now, startDate);
+
+ m = clazz.getMethod("getStartDateMs");
+ long startDateMs = (long)m.invoke(o);
+ assertEquals("reflect getStartDateMs invoke", now.getTime(), startDateMs, 0);
+
+ m = clazz.getMethod("getStartDateSec");
+ int startDateSec = (int)m.invoke(o);
+ assertEquals("reflect getStartDateSec invoke", now.getTime() / 1000, startDateSec, 0);
+
+ m = clazz.getDeclaredMethod("setStartDateMs", Long.class);
+ m.invoke(o, now.getTime());
+
+ m = clazz.getMethod("getStartDate");
+ startDate = (Date)m.invoke(o);
+ assertEquals("reflect getStartDate invoke", now, startDate);
+
+ m = clazz.getMethod("getStartDateMs");
+ startDateMs = (long)m.invoke(o);
+ assertEquals("reflect getStartDateMs invoke", now.getTime(), startDateMs, 0);
+
+ m = clazz.getMethod("getStartDateSec");
+ startDateSec = (int)m.invoke(o);
+ assertEquals("reflect getStartDateSec invoke", now.getTime() / 1000, startDateSec, 0);
+
+ m = clazz.getDeclaredMethod("setStartDateSec", Integer.class);
+ m.invoke(o, (int)(now.getTime() / 1000));
+
+ now = new Date(now.getTime() / 1000 * 1000);
+ m = clazz.getMethod("getStartDate");
+ startDate = (Date)m.invoke(o);
+ assertEquals("reflect getStartDate invoke", now, startDate);
+
+ m = clazz.getMethod("getStartDateMs");
+ startDateMs = (long)m.invoke(o);
+ assertEquals("reflect getStartDateMs invoke", now.getTime(), startDateMs, 0);
+
+ m = clazz.getMethod("getStartDateSec");
+ startDateSec = (int)m.invoke(o);
+ assertEquals("reflect getStartDateSec invoke", now.getTime() / 1000, startDateSec, 0);
+
+ }
+}