diff --git a/java/src/main/java/org/msgpack/CustomConverter.java b/java/src/main/java/org/msgpack/CustomConverter.java index 4351464c..abbc88ab 100644 --- a/java/src/main/java/org/msgpack/CustomConverter.java +++ b/java/src/main/java/org/msgpack/CustomConverter.java @@ -17,23 +17,22 @@ // package org.msgpack; -import java.util.Map; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; // FIXME package private? public class CustomConverter { - public static void register(Class target, MessageConverter converter) { - map.put(target, converter); + private static ConcurrentHashMap, MessageConverter> map = new ConcurrentHashMap, MessageConverter>(); + + public static void register(Class target, MessageConverter converter) { + map.putIfAbsent(target, converter); } - public static MessageConverter get(Class target) { + public static MessageConverter get(Class target) { return map.get(target); } - public static boolean isRegistered(Class target) { + public static boolean isRegistered(Class target) { return map.containsKey(target); } - - private static Map map = new HashMap(); } diff --git a/java/src/main/java/org/msgpack/CustomMessage.java b/java/src/main/java/org/msgpack/CustomMessage.java index f87898cd..53f83236 100644 --- a/java/src/main/java/org/msgpack/CustomMessage.java +++ b/java/src/main/java/org/msgpack/CustomMessage.java @@ -17,14 +17,19 @@ // package org.msgpack; +import java.lang.annotation.Annotation; + public class CustomMessage { - public static void registerPacker(Class target, MessagePacker packer) { + public static void registerPacker(Class target, MessagePacker packer) { CustomPacker.register(target, packer); } - public static void registerTemplate(Class target, Template tmpl) { + public static void registerTemplate(Class target, Template tmpl) { CustomUnpacker.register(target, tmpl); CustomConverter.register(target, tmpl); } -} + static boolean isAnnotated(Class target, Class with) { + return target.getAnnotation(with) != null; + } +} diff --git a/java/src/main/java/org/msgpack/CustomUnpacker.java b/java/src/main/java/org/msgpack/CustomUnpacker.java index b6047b84..b45292b5 100644 --- a/java/src/main/java/org/msgpack/CustomUnpacker.java +++ b/java/src/main/java/org/msgpack/CustomUnpacker.java @@ -17,23 +17,21 @@ // package org.msgpack; -import java.util.Map; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; // FIXME package private? public class CustomUnpacker { - public static void register(Class target, MessageUnpacker converter) { - map.put(target, converter); + private static ConcurrentHashMap, MessageUnpacker> map = new ConcurrentHashMap, MessageUnpacker>(); + + public static void register(Class target, MessageUnpacker converter) { + map.putIfAbsent(target, converter); } - public static MessageUnpacker get(Class target) { + public static MessageUnpacker get(Class target) { return map.get(target); } - public static boolean isRegistered(Class target) { + public static boolean isRegistered(Class target) { return map.containsKey(target); } - - private static Map map = new HashMap(); } - diff --git a/java/src/main/java/org/msgpack/MessageConverter.java b/java/src/main/java/org/msgpack/MessageConverter.java index 8ad60ae0..5e5f4378 100644 --- a/java/src/main/java/org/msgpack/MessageConverter.java +++ b/java/src/main/java/org/msgpack/MessageConverter.java @@ -17,9 +17,7 @@ // package org.msgpack; -import java.io.IOException; - public interface MessageConverter { - public Object convert(MessagePackObject from) throws MessageTypeException; + Object convert(MessagePackObject from) throws MessageTypeException; } diff --git a/java/src/main/java/org/msgpack/Packer.java b/java/src/main/java/org/msgpack/Packer.java index b4526cde..9ea4ea21 100644 --- a/java/src/main/java/org/msgpack/Packer.java +++ b/java/src/main/java/org/msgpack/Packer.java @@ -485,22 +485,21 @@ public class Packer { if(packer != null) { packer.pack(this, o); return this; - } else if (isAnnotated(klass, MessagePackMessage.class)) { + } else if (CustomMessage.isAnnotated(klass, MessagePackMessage.class)) { packer = ReflectionPacker.create(klass); packer.pack(this, o); return this; - } else if (isAnnotated(klass, MessagePackDelegate.class)) { + } else if (CustomMessage.isAnnotated(klass, MessagePackDelegate.class)) { + // FIXME DelegatePacker throw new UnsupportedOperationException("not supported yet. : " + klass.getName()); - } else if (isAnnotated(klass, MessagePackOrdinalEnum.class)) { + } else if (CustomMessage.isAnnotated(klass, MessagePackOrdinalEnum.class)) { + // FIXME OrdinalEnumPacker throw new UnsupportedOperationException("not supported yet. : " + klass.getName()); } - CustomPacker.register(klass, packer); - // FIXME check annotations -> code generation -> CustomMessage.registerPacker - + if (packer != null) { + CustomMessage.registerPacker(klass, packer); + } throw new MessageTypeException("unknown object "+o+" ("+o.getClass()+")"); } - - static boolean isAnnotated(Class target, Class with) { - return target.getAnnotation(with) != null; - } + } diff --git a/java/src/main/java/org/msgpack/Unpacker.java b/java/src/main/java/org/msgpack/Unpacker.java index 4e397488..add33125 100644 --- a/java/src/main/java/org/msgpack/Unpacker.java +++ b/java/src/main/java/org/msgpack/Unpacker.java @@ -18,12 +18,17 @@ package org.msgpack; import java.lang.Iterable; +import java.lang.annotation.Annotation; import java.io.InputStream; import java.io.IOException; import java.util.Iterator; import java.nio.ByteBuffer; import java.math.BigInteger; +import org.msgpack.annotation.MessagePackDelegate; +import org.msgpack.annotation.MessagePackMessage; +import org.msgpack.annotation.MessagePackOrdinalEnum; + /** * Unpacker enables you to deserialize objects from stream. * @@ -573,7 +578,7 @@ public class Unpacker implements Iterable { obj.messageUnpack(this); } - final public Object unpack(Class klass) throws IOException, MessageTypeException, InstantiationException, IllegalAccessException { + final public Object unpack(Class klass) throws IOException, MessageTypeException, InstantiationException, IllegalAccessException { if(MessageUnpackable.class.isAssignableFrom(klass)) { Object obj = klass.newInstance(); ((MessageUnpackable)obj).messageUnpack(this); @@ -584,14 +589,25 @@ public class Unpacker implements Iterable { if(unpacker != null) { return unpacker.unpack(this); } - - // FIXME check annotations -> code generation -> CustomMessage.registerTemplate - + + Template tmpl = null; + if (CustomMessage.isAnnotated(klass, MessagePackMessage.class)) { + tmpl = ReflectionTemplate.create(klass); + return tmpl.unpack(this); + } else if (CustomMessage.isAnnotated(klass, MessagePackDelegate.class)) { + // FIXME DelegateTemplate + throw new UnsupportedOperationException("not supported yet. : " + klass.getName()); + } else if (CustomMessage.isAnnotated(klass, MessagePackOrdinalEnum.class)) { + // FIXME OrdinalEnumTemplate + throw new UnsupportedOperationException("not supported yet. : " + klass.getName()); + } + if (tmpl != null) { + CustomMessage.registerTemplate(klass, tmpl); + } MessageConverter converter = CustomConverter.get(klass); if(converter != null) { return converter.convert(unpackObject()); } - throw new MessageTypeException(); } } diff --git a/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtilException.java b/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtilException.java deleted file mode 100644 index df3af058..00000000 --- a/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtilException.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.msgpack.util.annotation; - -public class PackUnpackUtilException extends RuntimeException { - - public PackUnpackUtilException(String reason) { - super(reason); - } - - public PackUnpackUtilException(String reason, Throwable t) { - super(reason, t); - } -} diff --git a/java/src/main/java/org/msgpack/util/codegen/BasicConstants.java b/java/src/main/java/org/msgpack/util/codegen/BasicConstants.java new file mode 100644 index 00000000..53a8a4ce --- /dev/null +++ b/java/src/main/java/org/msgpack/util/codegen/BasicConstants.java @@ -0,0 +1,160 @@ +package org.msgpack.util.codegen; + +public interface BasicConstants { + + String KEYWORD_MODIFIER_PUBLIC = "public"; + + String KEYWORD_CATCH = "catch"; + + String KEYWORD_ELSE = "else"; + + String KEYWORD_ELSEIF = "else if"; + + String KEYWORD_FOR = "for"; + + String KEYWORD_IF = "if"; + + String KEYWORD_INSTANCEOF = "instanceof"; + + String KEYWORD_NEW = "new"; + + String KEYWORD_NULL = "null"; + + String KEYWORD_THROW = "throw"; + + String KEYWORD_THROWS = "throws"; + + String KEYWORD_TRY = "try"; + + String CHAR_NAME_SPACE = " "; + + String CHAR_NAME_COMMA = ","; + + String CHAR_NAME_EQUAL = "="; + + String CHAR_NAME_PLUS = "+"; + + String CHAR_NAME_LESSTHAN = "<"; + + String CHAR_NAME_RIGHT_PARENTHESIS = ")"; + + String CHAR_NAME_LEFT_PARENTHESIS = "("; + + String CHAR_NAME_RIGHT_CURLY_BRACKET = "}"; + + String CHAR_NAME_LEFT_CURLY_BRACKET = "{"; + + String CHAR_NAME_RIGHT_SQUARE_BRACKET = "]"; + + String CHAR_NAME_LEFT_SQUARE_BRACKET = "["; + + String CHAR_NAME_DOT = "."; + + String CHAR_NAME_SEMICOLON = ";"; + + String VARIABLE_NAME_PK = "_$$_pk"; + + String VARIABLE_NAME_OBJ = "_$$_obj"; + + String VARIABLE_NAME_TARGET = "_$$_target"; + + String VARIABLE_NAME_SIZE = "_$$_len"; + + String VARIABLE_NAME_ARRAY = "_$$_ary"; + + String VARIABLE_NAME_LIST = "_$$_list"; + + String VARIABLE_NAME_MAP = "_$$_map"; + + String VARIABLE_NAME_KEY = "_$$_key"; + + String VARIABLE_NAME_VAL = "_$$_val"; + + String VARIABLE_NAME_ITER = "_$$_iter"; + + String VARIABLE_NAME_MPO = "_$$_mpo"; + + String VARIABLE_NAME_I = "i"; + + String METHOD_NAME_VALUEOF = "valueOf"; + + String METHOD_NAME_ADD = "add"; + + String METHOD_NAME_PUT = "put"; + + String METHOD_NAME_GET = "get"; + + String METHOD_NAME_SIZE = "size"; + + String METHOD_NAME_KEYSET = "keySet"; + + String METHOD_NAME_ITERATOR = "iterator"; + + String METHOD_NAME_HASNEXT = "hasNext"; + + String METHOD_NAME_NEXT = "next"; + + String METHOD_NAME_MSGPACK = "messagePack"; + + String METHOD_NAME_MSGUNPACK = "messageUnpack"; + + String METHOD_NAME_MSGCONVERT = "messageConvert"; + + String METHOD_NAME_PACK = "pack"; + + String METHOD_NAME_PACKARRAY = "packArray"; + + String METHOD_NAME_UNPACK = "unpack"; + + String METHOD_NAME_UNPACKBOOLEAN = "unpackBoolean"; + + String METHOD_NAME_UNPACKBYTE = "unpackByte"; + + String METHOD_NAME_UNPACKDOUBLE = "unpackDouble"; + + String METHOD_NAME_UNPACKFLOAT = "unpackFloat"; + + String METHOD_NAME_UNPACKINT = "unpackInt"; + + String METHOD_NAME_UNPACKLONG = "unpackLong"; + + String METHOD_NAME_UNPACKSHORT = "unpackShort"; + + String METHOD_NAME_UNPACKSTRING = "unpackString"; + + String METHOD_NAME_UNPACKBIGINTEGER = "unpackBigInteger"; + + String METHOD_NAME_UNPACKOBJECT = "unpackObject"; + + String METHOD_NAME_UNPACKBYTEARRAY = "unpackByteArray"; + + String METHOD_NAME_UNPACKARRAY = "unpackArray"; + + String METHOD_NAME_UNPACKMAP = "unpackMap"; + + String METHOD_NAME_ASARRAY = "asArray"; + + String METHOD_NAME_ASBOOLEAN = "asBoolean"; + + String METHOD_NAME_ASBYTE = "asByte"; + + String METHOD_NAME_ASSHORT = "asShort"; + + String METHOD_NAME_ASINT = "asInt"; + + String METHOD_NAME_ASFLOAT = "asFloat"; + + String METHOD_NAME_ASLONG = "asLong"; + + String METHOD_NAME_ASDOUBLE = "asDouble"; + + String METHOD_NAME_ASSTRING = "asString"; + + String METHOD_NAME_ASBYTEARRAY = "asByteArray"; + + String METHOD_NAME_ASBIGINTEGER = "asBigInteger"; + + String METHOD_NAME_ASLIST = "asList"; + + String METHOD_NAME_ASMAP = "asMap"; +} diff --git a/java/src/main/java/org/msgpack/util/codegen/Constants.java b/java/src/main/java/org/msgpack/util/codegen/Constants.java new file mode 100644 index 00000000..ddeef31f --- /dev/null +++ b/java/src/main/java/org/msgpack/util/codegen/Constants.java @@ -0,0 +1,7 @@ +package org.msgpack.util.codegen; + +public interface Constants extends BasicConstants { + String POSTFIX_TYPE_NAME_PACKER = "_$$_Packer"; + + String POSTFIX_TYPE_NAME_ENHANCER = "_$$_Enhanced"; +} diff --git a/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGen.java b/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGen.java new file mode 100644 index 00000000..9f63242d --- /dev/null +++ b/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGen.java @@ -0,0 +1,232 @@ +package org.msgpack.util.codegen; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import javassist.CannotCompileException; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtConstructor; +import javassist.CtMethod; +import javassist.CtNewConstructor; +import javassist.CtNewMethod; +import javassist.NotFoundException; + +import org.msgpack.MessagePacker; +import org.msgpack.Packer; +import org.msgpack.Template; + +public class DynamicCodeGen extends DynamicCodeGenBase implements Constants { + + private static DynamicCodeGen INSTANCE; + + private static AtomicInteger COUNTER; + + public static DynamicCodeGen getInstance() { + if (INSTANCE == null) { + INSTANCE = new DynamicCodeGen(); + } + return INSTANCE; + } + + private ClassPool pool; + + private DynamicCodeGen() { + this.pool = ClassPool.getDefault(); + } + + public Class generateMessagePackerClass(Class origClass) { + try { + String origName = origClass.getName(); + String packerName = origName + POSTFIX_TYPE_NAME_PACKER; + checkClassValidation(origClass); + checkDefaultConstructorValidation(origClass); + CtClass packerCtClass = pool.makeClass(packerName); + setInterface(packerCtClass, MessagePacker.class); + addDefaultConstructor(packerCtClass); + Field[] fields = getDeclaredFields(origClass); + addPackMethod(packerCtClass, origClass, fields); + return createClass(packerCtClass); + } catch (NotFoundException e) { + throw new DynamicCodeGenException(e.getMessage(), e); + } catch (CannotCompileException e) { + throw new DynamicCodeGenException(e.getMessage(), e); + } + } + + public Class generateTemplateClass(Class origClass) { + try { + String origName = origClass.getName(); + String packerName = origName + POSTFIX_TYPE_NAME_PACKER; + checkClassValidation(origClass); + checkDefaultConstructorValidation(origClass); + CtClass packerCtClass = pool.makeClass(packerName); + setInterface(packerCtClass, Template.class); + addDefaultConstructor(packerCtClass); + Field[] fields = getDeclaredFields(origClass); + addPackMethod(packerCtClass, origClass, fields); + return createClass(packerCtClass); + } catch (NotFoundException e) { + throw new DynamicCodeGenException(e.getMessage(), e); + } catch (CannotCompileException e) { + throw new DynamicCodeGenException(e.getMessage(), e); + } + } + + private void checkClassValidation(Class origClass) { + // not public, abstract, final + int mod = origClass.getModifiers(); + if ((!(Modifier.isPublic(mod) || Modifier.isProtected(mod))) + || Modifier.isAbstract(mod) || Modifier.isFinal(mod)) { + throwClassValidationException(origClass, + "it must be a public class"); + } + // interface, enum + if (origClass.isInterface() || origClass.isEnum()) { + throwClassValidationException(origClass, + "it must not be an interface or enum"); + } + } + + private static void throwClassValidationException(Class origClass, + String msg) { + throw new DynamicCodeGenException(msg + ": " + origClass.getName()); + } + + private void checkDefaultConstructorValidation(Class origClass) { + Constructor cons = null; + try { + cons = origClass.getDeclaredConstructor(new Class[0]); + } catch (Exception e1) { + throwConstructoValidationException(origClass); + } + + int mod = cons.getModifiers(); + if (!(Modifier.isPublic(mod) || Modifier.isProtected(mod))) { + throwConstructoValidationException(origClass); + } + } + + private static void throwConstructoValidationException(Class origClass) { + throw new DynamicCodeGenException( + "it must have a public zero-argument constructor: " + + origClass.getName()); + } + + private void setInterface(CtClass packerCtClass, Class infClass) + throws NotFoundException { + CtClass infCtClass = pool.get(infClass.getName()); + packerCtClass.addInterface(infCtClass); + } + + private void addDefaultConstructor(CtClass enhCtClass) + throws CannotCompileException { + CtConstructor newCtCons = CtNewConstructor + .defaultConstructor(enhCtClass); + enhCtClass.addConstructor(newCtCons); + } + + private Field[] getDeclaredFields(Class origClass) { + ArrayList allFields = new ArrayList(); + Class nextClass = origClass; + while (nextClass != Object.class) { + Field[] fields = nextClass.getDeclaredFields(); + for (Field field : fields) { + try { + checkFieldValidation(field, allFields); + allFields.add(field); + } catch (DynamicCodeGenException e) { // ignore + } + } + nextClass = nextClass.getSuperclass(); + } + return allFields.toArray(new Field[0]); + } + + private void checkFieldValidation(Field field, List fields) { + // check modifiers (public or protected) + int mod = field.getModifiers(); + if ((!(Modifier.isPublic(mod) || Modifier.isProtected(mod))) + || Modifier.isStatic(mod) || Modifier.isFinal(mod) + || Modifier.isTransient(mod) || field.isSynthetic()) { + throwFieldValidationException(field); + } + // check same name + for (Field f : fields) { + if (f.getName().equals(field.getName())) { + throwFieldValidationException(field); + } + } + } + + private static void throwFieldValidationException(Field field) { + throw new DynamicCodeGenException("it must be a public field: " + + field.getName()); + } + + private void addPackMethod(CtClass packerCtClass, Class c, Field[] fs) + throws CannotCompileException, NotFoundException { + StringBuilder sb = new StringBuilder(); + sb.append(KEYWORD_MODIFIER_PUBLIC); + sb.append(CHAR_NAME_SPACE); + sb.append(void.class.getName()); + sb.append(CHAR_NAME_SPACE); + sb.append(METHOD_NAME_PACK); + sb.append(CHAR_NAME_LEFT_PARENTHESIS); + sb.append(Packer.class.getName()); + sb.append(CHAR_NAME_SPACE); + sb.append(VARIABLE_NAME_PK); + sb.append(CHAR_NAME_COMMA); + sb.append(CHAR_NAME_SPACE); + sb.append(Object.class.getName()); + sb.append(CHAR_NAME_SPACE); + sb.append(VARIABLE_NAME_OBJ); + sb.append(CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(CHAR_NAME_SPACE); + sb.append(KEYWORD_THROWS); + sb.append(CHAR_NAME_SPACE); + sb.append(IOException.class.getName()); + sb.append(CHAR_NAME_SPACE); + sb.append(CHAR_NAME_LEFT_CURLY_BRACKET); + sb.append(CHAR_NAME_SPACE); + insertPackMethodBody(sb, c, fs); + sb.append(CHAR_NAME_RIGHT_CURLY_BRACKET); + // System.out.println("pack method: " + sb.toString()); + CtMethod newCtMethod = CtNewMethod.make(sb.toString(), packerCtClass); + packerCtClass.addMethod(newCtMethod); + } + + private void insertPackMethodBody(StringBuilder sb, Class c, Field[] fs) { + insertLocalVariableDecl(sb, c, VARIABLE_NAME_TARGET); + StringBuilder mc = new StringBuilder(); + insertTypeCast(mc, c, VARIABLE_NAME_OBJ); + insertValueInsertion(sb, mc.toString()); + insertSemicolon(sb); + insertMethodCall(sb, VARIABLE_NAME_PK, METHOD_NAME_PACKARRAY, + new String[] { new Integer(fs.length).toString() }); + insertSemicolon(sb); + for (Field f : fs) { + insertCodeOfPackCall(sb, f); + } + } + + private void insertCodeOfPackCall(StringBuilder sb, Field field) { + StringBuilder aname = new StringBuilder(); + aname.append(VARIABLE_NAME_TARGET); + aname.append(CHAR_NAME_DOT); + aname.append(field.getName()); + insertMethodCall(sb, VARIABLE_NAME_PK, METHOD_NAME_PACK, + new String[] { aname.toString() }); + insertSemicolon(sb); + } + + private Class createClass(CtClass packerCtClass) + throws CannotCompileException { + return packerCtClass.toClass(null, null); + } +} diff --git a/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenBase.java b/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenBase.java new file mode 100644 index 00000000..24ae579a --- /dev/null +++ b/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenBase.java @@ -0,0 +1,189 @@ +package org.msgpack.util.codegen; + +import java.math.BigInteger; +import java.util.List; +import java.util.Map; + +public class DynamicCodeGenBase implements BasicConstants { + public DynamicCodeGenBase() { + } + + public void insertSemicolon(StringBuilder sb) { + sb.append(CHAR_NAME_SEMICOLON); + sb.append(CHAR_NAME_SPACE); + } + + public void insertPublicFieldDecl(StringBuilder sb, Class type, + String name) { + sb.append(KEYWORD_MODIFIER_PUBLIC); + sb.append(CHAR_NAME_SPACE); + sb.append(type.getName()); + sb.append(CHAR_NAME_SPACE); + sb.append(name); + } + + public void insertLocalVariableDecl(StringBuilder sb, Class type, + String name) { + sb.append(type.getName()); + sb.append(CHAR_NAME_SPACE); + sb.append(name); + } + + public void insertValueInsertion(StringBuilder sb, String expr) { + // = expr + sb.append(CHAR_NAME_SPACE); + sb.append(CHAR_NAME_EQUAL); + sb.append(CHAR_NAME_SPACE); + sb.append(expr); + } + + public void insertDefaultConsCall(StringBuilder sb, Class type) { + // new tname() + insertConsCall(sb, type, null); + } + + public void insertConsCall(StringBuilder sb, Class type, String expr) { + // new tname(expr) + sb.append(KEYWORD_NEW); + sb.append(CHAR_NAME_SPACE); + sb.append(type.getName()); + sb.append(CHAR_NAME_LEFT_PARENTHESIS); + if (expr != null) { + sb.append(expr); + } + sb.append(CHAR_NAME_RIGHT_PARENTHESIS); + } + + public void insertMethodCall(StringBuilder sb, String tname, String mname, + String[] anames) { + // tname.mname(anames[0], anames[1], ...) + int len = anames.length; + sb.append(tname); + sb.append(CHAR_NAME_DOT); + sb.append(mname); + sb.append(CHAR_NAME_LEFT_PARENTHESIS); + for (int i = 0; i < len; ++i) { + sb.append(anames[i]); + if (i + 1 != len) { + sb.append(CHAR_NAME_COMMA); + sb.append(CHAR_NAME_SPACE); + } + } + sb.append(CHAR_NAME_RIGHT_PARENTHESIS); + } + + public void insertTypeCast(StringBuilder sb, Class type) { + // (type) + sb.append(CHAR_NAME_LEFT_PARENTHESIS); + sb.append(type.getName()); + sb.append(CHAR_NAME_RIGHT_PARENTHESIS); + } + + public void insertTypeCast(StringBuilder sb, Class type, String varName) { + // ((type)var) + sb.append(CHAR_NAME_LEFT_PARENTHESIS); + sb.append(CHAR_NAME_LEFT_PARENTHESIS); + sb.append(type.getName()); + sb.append(CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(varName); + sb.append(CHAR_NAME_RIGHT_PARENTHESIS); + } + + public void insertTypeConvToObjectType(StringBuilder sb, Class type, + String expr) throws DynamicCodeGenException { + if (type.isPrimitive()) { // primitive type + if (type.equals(boolean.class)) { + // new Boolean(expr) + insertConsCall(sb, Boolean.class, expr); + } else if (type.equals(byte.class)) { + insertConsCall(sb, Byte.class, expr); + } else if (type.equals(short.class)) { + insertConsCall(sb, Short.class, expr); + } else if (type.equals(int.class)) { + insertConsCall(sb, Integer.class, expr); + } else if (type.equals(long.class)) { + insertConsCall(sb, Long.class, expr); + } else if (type.equals(float.class)) { + insertConsCall(sb, Float.class, expr); + } else if (type.equals(double.class)) { + insertConsCall(sb, Double.class, expr); + } else { + throw new DynamicCodeGenException("Type error: " + + type.getName()); + } + } else { // reference type + sb.append(expr); + } + } + + public void insertTryCatchBlocks(StringBuilder sb, String tryBody, + List> types, List names, List catchBodies) { + int len = types.size(); + sb.append(KEYWORD_TRY); + sb.append(CHAR_NAME_SPACE); + sb.append(CHAR_NAME_LEFT_CURLY_BRACKET); + sb.append(CHAR_NAME_SPACE); + sb.append(tryBody); + sb.append(CHAR_NAME_RIGHT_CURLY_BRACKET); + sb.append(CHAR_NAME_SPACE); + for (int i = 0; i < len; ++i) { + sb.append(KEYWORD_CATCH); + sb.append(CHAR_NAME_SPACE); + sb.append(CHAR_NAME_LEFT_PARENTHESIS); + sb.append(types.get(i).getName()); + sb.append(CHAR_NAME_SPACE); + sb.append(names.get(i)); + sb.append(CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(CHAR_NAME_SPACE); + sb.append(CHAR_NAME_LEFT_CURLY_BRACKET); + sb.append(CHAR_NAME_SPACE); + sb.append(catchBodies.get(i)); + sb.append(CHAR_NAME_RIGHT_CURLY_BRACKET); + sb.append(CHAR_NAME_SPACE); + } + } + + public String getAsMethod(Class c) throws DynamicCodeGenException { + if (c.equals(boolean.class)) { + return METHOD_NAME_ASBOOLEAN; + } else if (c.equals(byte.class)) { + return METHOD_NAME_ASBYTE; + } else if (c.equals(short.class)) { + return METHOD_NAME_ASSHORT; + } else if (c.equals(int.class)) { + return METHOD_NAME_ASINT; + } else if (c.equals(float.class)) { + return METHOD_NAME_ASFLOAT; + } else if (c.equals(long.class)) { + return METHOD_NAME_ASLONG; + } else if (c.equals(double.class)) { + return METHOD_NAME_ASDOUBLE; + } else if (c.equals(Boolean.class)) { + return METHOD_NAME_ASBOOLEAN; + } else if (c.equals(Byte.class)) { + return METHOD_NAME_ASBYTE; + } else if (c.equals(Short.class)) { + return METHOD_NAME_ASSHORT; + } else if (c.equals(Integer.class)) { + return METHOD_NAME_ASINT; + } else if (c.equals(Float.class)) { + return METHOD_NAME_ASFLOAT; + } else if (c.equals(Long.class)) { + return METHOD_NAME_ASLONG; + } else if (c.equals(Double.class)) { + return METHOD_NAME_ASDOUBLE; + } else if (c.equals(String.class)) { + return METHOD_NAME_ASSTRING; + } else if (c.equals(byte[].class)) { + return METHOD_NAME_ASBYTEARRAY; + } else if (c.equals(BigInteger.class)) { + return METHOD_NAME_ASBIGINTEGER; + } else if (List.class.isAssignableFrom(c)) { + return METHOD_NAME_ASLIST; + } else if (Map.class.isAssignableFrom(c)) { + return METHOD_NAME_ASMAP; + } else { + throw new DynamicCodeGenException("Type error: " + c.getName()); + } + } +} diff --git a/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenException.java b/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenException.java new file mode 100644 index 00000000..1877ed50 --- /dev/null +++ b/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenException.java @@ -0,0 +1,12 @@ +package org.msgpack.util.codegen; + +public class DynamicCodeGenException extends RuntimeException { + + public DynamicCodeGenException(String reason) { + super(reason); + } + + public DynamicCodeGenException(String reason, Throwable t) { + super(reason, t); + } +} diff --git a/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenPacker.java b/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenPacker.java new file mode 100644 index 00000000..2a014b8f --- /dev/null +++ b/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenPacker.java @@ -0,0 +1,18 @@ +package org.msgpack.util.codegen; + +import org.msgpack.MessagePacker; + +public class DynamicCodeGenPacker { + + public static MessagePacker create(Class c) { + try { + DynamicCodeGen gen = DynamicCodeGen.getInstance(); + Class packerClass = gen.generateMessagePackerClass(c); + return (MessagePacker)packerClass.newInstance(); + } catch (InstantiationException e) { + throw new DynamicCodeGenException(e.getMessage(), e); + } catch (IllegalAccessException e) { + throw new DynamicCodeGenException(e.getMessage(), e); + } + } +} diff --git a/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenTemplate.java b/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenTemplate.java new file mode 100644 index 00000000..1e12f2ed --- /dev/null +++ b/java/src/main/java/org/msgpack/util/codegen/DynamicCodeGenTemplate.java @@ -0,0 +1,17 @@ +package org.msgpack.util.codegen; + +import org.msgpack.Template; + +public class DynamicCodeGenTemplate { + public static Template create(Class c) { + try { + DynamicCodeGen gen = DynamicCodeGen.getInstance(); + Class tmplClass = gen.generateTemplateClass(c); + return (Template) tmplClass.newInstance(); + } catch (InstantiationException e) { + throw new DynamicCodeGenException(e.getMessage(), e); + } catch (IllegalAccessException e) { + throw new DynamicCodeGenException(e.getMessage(), e); + } + } +} diff --git a/java/src/main/java/org/msgpack/util/annotation/MessagePackOptional.java b/java/src/main/java/org/msgpack/util/codegen/MessagePackOptional.java similarity index 88% rename from java/src/main/java/org/msgpack/util/annotation/MessagePackOptional.java rename to java/src/main/java/org/msgpack/util/codegen/MessagePackOptional.java index a565292d..736c2ed3 100644 --- a/java/src/main/java/org/msgpack/util/annotation/MessagePackOptional.java +++ b/java/src/main/java/org/msgpack/util/codegen/MessagePackOptional.java @@ -1,4 +1,4 @@ -package org.msgpack.util.annotation; +package org.msgpack.util.codegen; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/java/src/main/java/org/msgpack/util/annotation/MessagePackRequired.java b/java/src/main/java/org/msgpack/util/codegen/MessagePackRequired.java similarity index 88% rename from java/src/main/java/org/msgpack/util/annotation/MessagePackRequired.java rename to java/src/main/java/org/msgpack/util/codegen/MessagePackRequired.java index 03e50cf3..a0c956f2 100644 --- a/java/src/main/java/org/msgpack/util/annotation/MessagePackRequired.java +++ b/java/src/main/java/org/msgpack/util/codegen/MessagePackRequired.java @@ -1,4 +1,4 @@ -package org.msgpack.util.annotation; +package org.msgpack.util.codegen; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/java/src/main/java/org/msgpack/util/annotation/MessagePackUnpackable.java b/java/src/main/java/org/msgpack/util/codegen/MessagePackUnpackable.java similarity index 87% rename from java/src/main/java/org/msgpack/util/annotation/MessagePackUnpackable.java rename to java/src/main/java/org/msgpack/util/codegen/MessagePackUnpackable.java index 77f6e6de..e5aff401 100644 --- a/java/src/main/java/org/msgpack/util/annotation/MessagePackUnpackable.java +++ b/java/src/main/java/org/msgpack/util/codegen/MessagePackUnpackable.java @@ -1,4 +1,4 @@ -package org.msgpack.util.annotation; +package org.msgpack.util.codegen; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtil.java b/java/src/main/java/org/msgpack/util/codegen/PackUnpackUtil.java similarity index 99% rename from java/src/main/java/org/msgpack/util/annotation/PackUnpackUtil.java rename to java/src/main/java/org/msgpack/util/codegen/PackUnpackUtil.java index 0aeb6d87..2062ee40 100644 --- a/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtil.java +++ b/java/src/main/java/org/msgpack/util/codegen/PackUnpackUtil.java @@ -1,4 +1,4 @@ -package org.msgpack.util.annotation; +package org.msgpack.util.codegen; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -230,7 +230,7 @@ public class PackUnpackUtil { } private static void throwClassValidationException(Class origClass) { - throw new PackUnpackUtilException( + throw new DynamicCodeGenException( "it must be a public class and have @" + MessagePackUnpackable.class.getName() + ": " + origClass.getName()); @@ -260,7 +260,7 @@ public class PackUnpackUtil { private static void throwConstructoValidationException( Class origClass) { - throw new PackUnpackUtilException( + throw new DynamicCodeGenException( "it must have a public zero-argument constructor: " + origClass.getName()); } @@ -295,7 +295,7 @@ public class PackUnpackUtil { try { checkFieldValidation(field, allFields); allFields.add(field); - } catch (PackUnpackUtilException e) { // ignore + } catch (DynamicCodeGenException e) { // ignore } } nextClass = nextClass.getSuperclass(); @@ -320,7 +320,7 @@ public class PackUnpackUtil { } private static void throwFieldValidationException(Field field) { - throw new PackUnpackUtilException("it must be a public field: " + throw new DynamicCodeGenException("it must be a public field: " + field.getName()); } @@ -1267,9 +1267,9 @@ public class PackUnpackUtil { try { enhClass = enhancer.generate(origClass, packUnpackable); } catch (NotFoundException e) { - throw new PackUnpackUtilException(e.getMessage(), e); + throw new DynamicCodeGenException(e.getMessage(), e); } catch (CannotCompileException e) { - throw new PackUnpackUtilException(e.getMessage(), e); + throw new DynamicCodeGenException(e.getMessage(), e); } // set the generated class to the cache enhancer.setCache(origName, enhClass); @@ -1306,9 +1306,9 @@ public class PackUnpackUtil { // create a new object of the generated class return enhClass.newInstance(); } catch (InstantiationException e) { - throw new PackUnpackUtilException(e.getMessage(), e); + throw new DynamicCodeGenException(e.getMessage(), e); } catch (IllegalAccessException e) { - throw new PackUnpackUtilException(e.getMessage(), e); + throw new DynamicCodeGenException(e.getMessage(), e); } } diff --git a/java/src/test/java/org/msgpack/util/codegen/TestDynamicCodeGenPacker.java b/java/src/test/java/org/msgpack/util/codegen/TestDynamicCodeGenPacker.java new file mode 100644 index 00000000..d4e9c166 --- /dev/null +++ b/java/src/test/java/org/msgpack/util/codegen/TestDynamicCodeGenPacker.java @@ -0,0 +1,51 @@ +package org.msgpack.util.codegen; + +import static org.msgpack.Templates.tString; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + +import org.junit.Test; +import org.msgpack.MessagePacker; +import org.msgpack.Packer; +import org.msgpack.ReflectionPacker; +import org.msgpack.ReflectionTemplate; +import org.msgpack.Template; +import org.msgpack.Unpacker; + +import junit.framework.TestCase; + + +public class TestDynamicCodeGenPacker extends TestCase { + public static class StringFieldClass { + public String s1; + public String s2; + public StringFieldClass() { } + } + + @Test + public void testPackConvert() throws Exception { + tString(); // FIXME link StringTemplate + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + MessagePacker packer = DynamicCodeGenPacker.create(StringFieldClass.class); + + StringFieldClass src = new StringFieldClass(); + + src.s1 = "kumofs"; + src.s2 = "frsyuki"; + + packer.pack(new Packer(out), src); + + Template tmpl = ReflectionTemplate.create(StringFieldClass.class); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + + Object obj = tmpl.unpack(new Unpacker(in)); + assertEquals(obj.getClass(), StringFieldClass.class); + + StringFieldClass dst = (StringFieldClass)obj; + assertEquals(src.s1, dst.s1); + assertEquals(src.s2, dst.s2); + } +} diff --git a/java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java b/java/src/test/java/org/msgpack/util/codegen/TestMessagePackUnpackable.java similarity index 96% rename from java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java rename to java/src/test/java/org/msgpack/util/codegen/TestMessagePackUnpackable.java index 08c4fa82..7e0baccd 100644 --- a/java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java +++ b/java/src/test/java/org/msgpack/util/codegen/TestMessagePackUnpackable.java @@ -1,4 +1,4 @@ -package org.msgpack.util.annotation; +package org.msgpack.util.codegen; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -16,6 +16,9 @@ import org.msgpack.MessagePackObject; import org.msgpack.MessageUnpackable; import org.msgpack.Packer; import org.msgpack.Unpacker; +import org.msgpack.util.codegen.MessagePackUnpackable; +import org.msgpack.util.codegen.PackUnpackUtil; +import org.msgpack.util.codegen.DynamicCodeGenException; public class TestMessagePackUnpackable extends TestCase { @@ -350,7 +353,7 @@ public class TestMessagePackUnpackable extends TestCase { try { PackUnpackUtil.newEnhancedInstance(NoDefaultConstructorClass.class); fail(); - } catch (PackUnpackUtilException e) { + } catch (DynamicCodeGenException e) { assertTrue(true); } assertTrue(true); @@ -358,7 +361,7 @@ public class TestMessagePackUnpackable extends TestCase { PackUnpackUtil .newEnhancedInstance(PrivateDefaultConstructorClass.class); fail(); - } catch (PackUnpackUtilException e) { + } catch (DynamicCodeGenException e) { assertTrue(true); } assertTrue(true); @@ -366,7 +369,7 @@ public class TestMessagePackUnpackable extends TestCase { PackUnpackUtil .newEnhancedInstance(ProtectedDefaultConstructorClass.class); assertTrue(true); - } catch (PackUnpackUtilException e) { + } catch (DynamicCodeGenException e) { fail(); } assertTrue(true); @@ -374,7 +377,7 @@ public class TestMessagePackUnpackable extends TestCase { PackUnpackUtil .newEnhancedInstance(PackageDefaultConstructorClass.class); fail(); - } catch (PackUnpackUtilException e) { + } catch (DynamicCodeGenException e) { assertTrue(true); } assertTrue(true); @@ -409,21 +412,21 @@ public class TestMessagePackUnpackable extends TestCase { try { PackUnpackUtil.newEnhancedInstance(PrivateModifierClass.class); fail(); - } catch (PackUnpackUtilException e) { + } catch (DynamicCodeGenException e) { assertTrue(true); } assertTrue(true); try { PackUnpackUtil.newEnhancedInstance(ProtectedModifierClass.class); assertTrue(true); - } catch (PackUnpackUtilException e) { + } catch (DynamicCodeGenException e) { fail(); } assertTrue(true); try { PackUnpackUtil.newEnhancedInstance(PackageModifierClass.class); fail(); - } catch (PackUnpackUtilException e) { + } catch (DynamicCodeGenException e) { assertTrue(true); } assertTrue(true); @@ -448,14 +451,14 @@ public class TestMessagePackUnpackable extends TestCase { try { PackUnpackUtil.newEnhancedInstance(FinalModifierClass.class); fail(); - } catch (PackUnpackUtilException e) { + } catch (DynamicCodeGenException e) { assertTrue(true); } assertTrue(true); try { PackUnpackUtil.newEnhancedInstance(AbstractModifierClass.class); fail(); - } catch (PackUnpackUtilException e) { + } catch (DynamicCodeGenException e) { assertTrue(true); } assertTrue(true); @@ -474,14 +477,14 @@ public class TestMessagePackUnpackable extends TestCase { try { PackUnpackUtil.newEnhancedInstance(SampleInterface.class); fail(); - } catch (PackUnpackUtilException e) { + } catch (DynamicCodeGenException e) { assertTrue(true); } assertTrue(true); try { PackUnpackUtil.newEnhancedInstance(SampleEnum.class); fail(); - } catch (PackUnpackUtilException e) { + } catch (DynamicCodeGenException e) { assertTrue(true); } assertTrue(true);