diff --git a/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtil.java b/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtil.java index 5efef792..a58b4bd5 100644 --- a/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtil.java +++ b/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtil.java @@ -5,6 +5,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.reflect.Modifier; import java.math.BigInteger; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -196,9 +197,10 @@ public class PackUnpackUtil { setSuperclass(enhCtClass, origCtClass); setInterfaces(enhCtClass); addConstructor(enhCtClass); - addMessagePackMethod(enhCtClass, origCtClass); - addMessageUnpackMethod(enhCtClass, origCtClass); - addMessageConvertMethod(enhCtClass, origCtClass); + CtField[] fields = getDeclaredFields(origCtClass); + addMessagePackMethod(enhCtClass, origCtClass, fields); + addMessageUnpackMethod(enhCtClass, origCtClass, fields); + addMessageConvertMethod(enhCtClass, origCtClass, fields); return createClass(enhCtClass); } @@ -279,9 +281,54 @@ public class PackUnpackUtil { enhCtClass.addConstructor(newCtCons); } + private CtField[] getDeclaredFields(CtClass origCtClass) { + ArrayList allFields = new ArrayList(); + try { + CtClass nextCtClass = origCtClass; + while (!nextCtClass + .equals(pool.get(Constants.TYPE_NAME_OBJECT))) { + CtField[] fields = nextCtClass.getDeclaredFields(); + for (CtField field : fields) { + try { + checkFieldValidation(field, allFields); + allFields.add(field); + } catch (PackUnpackUtilException e) { // ignore + } + } + nextCtClass = nextCtClass.getSuperclass(); + } + + } catch (NotFoundException e) { + throw new PackUnpackUtilException(e.getMessage(), e); + } + return allFields.toArray(new CtField[0]); + } + + private void checkFieldValidation(CtField field, + ArrayList allFields) { + // 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)) { + throwFieldValidationException(field); + } + // check same name + for (CtField f : allFields) { + if (f.getName().equals(field.getName())) { + throwFieldValidationException(field); + } + } + } + + private static void throwFieldValidationException(CtField field) { + throw new PackUnpackUtilException("it must be a public field: " + + field.getName()); + } + private void addMessagePackMethod(CtClass enhCtClass, - CtClass origCtClass) throws CannotCompileException, - NotFoundException { + CtClass origCtClass, CtField[] fields) + throws CannotCompileException, NotFoundException { StringBuilder sb = new StringBuilder(); sb.append(Constants.KEYWORD_MODIFIER_PUBLIC).append( Constants.CHAR_NAME_SPACE).append(Constants.TYPE_NAME_VOID) @@ -299,7 +346,6 @@ public class PackUnpackUtil { Constants.CHAR_NAME_SPACE).append( Constants.CHAR_NAME_LEFT_CURLY_BRACHET).append( Constants.CHAR_NAME_SPACE); - CtField[] fields = origCtClass.getDeclaredFields(); sb.append(Constants.VARIABLE_NAME_PK).append( Constants.CHAR_NAME_DOT).append( Constants.METHOD_NAME_PACKARRAY).append( @@ -328,8 +374,8 @@ public class PackUnpackUtil { } private void addMessageUnpackMethod(CtClass enhCtClass, - CtClass origCtClass) throws CannotCompileException, - NotFoundException { + CtClass origCtClass, CtField[] fields) + throws CannotCompileException, NotFoundException { StringBuilder sb = new StringBuilder(); sb.append(Constants.KEYWORD_MODIFIER_PUBLIC).append( Constants.CHAR_NAME_SPACE).append(Constants.TYPE_NAME_VOID) @@ -350,7 +396,6 @@ public class PackUnpackUtil { Constants.CHAR_NAME_SPACE).append( Constants.CHAR_NAME_LEFT_CURLY_BRACHET).append( Constants.CHAR_NAME_SPACE); - CtField[] fields = origCtClass.getFields(); sb.append(Constants.VARIABLE_NAME_PK).append( Constants.CHAR_NAME_DOT).append( Constants.METHOD_NAME_UNPACKARRAY).append( @@ -370,21 +415,53 @@ public class PackUnpackUtil { private void insertCodeOfMessageUnpack(CtField field, StringBuilder sb) throws NotFoundException { CtClass type = field.getType(); - sb.append(field.getName()).append(Constants.CHAR_NAME_SPACE) - .append(Constants.CHAR_NAME_EQUAL).append( - Constants.CHAR_NAME_SPACE); + insertRightVariable(sb, field, type); insertValueOfMethodAndLeftParenthesis(sb, type); sb.append(Constants.VARIABLE_NAME_PK).append( Constants.CHAR_NAME_DOT); insertUnpackMethod(sb, type); - sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS).append( - Constants.CHAR_NAME_RIGHT_PARENTHESIS); + sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS); + insertUnpackMethodArgumenet(sb, field, type); + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); insertValueOfMethodAndRightParenthesis(sb, type); sb.append(Constants.CHAR_NAME_SEMICOLON).append( Constants.CHAR_NAME_SPACE); } + private void insertRightVariable(StringBuilder sb, CtField field, + CtClass type) throws NotFoundException { + if (type.isPrimitive()) { // primitive type + sb.append(field.getName()).append(Constants.CHAR_NAME_SPACE) + .append(Constants.CHAR_NAME_EQUAL).append( + Constants.CHAR_NAME_SPACE); + } else { // reference type + if (type.equals(pool.get(Constants.TYPE_NAME_BOOLEAN2)) // Boolean + || type.equals(pool.get(Constants.TYPE_NAME_BYTE2)) // Byte + || type.equals(pool.get(Constants.TYPE_NAME_DOUBLE2)) // Double + || type.equals(pool.get(Constants.TYPE_NAME_FLOAT2)) // Float + || type.equals(pool.get(Constants.TYPE_NAME_INT2)) // Integer + || type.equals(pool.get(Constants.TYPE_NAME_LONG2)) // Long + || type.equals(pool.get(Constants.TYPE_NAME_SHORT2)) // Short + || type + .equals(pool + .get(Constants.TYPE_NAME_BIGINTEGER)) // BigInteger + || type.equals(pool.get(Constants.TYPE_NAME_STRING)) // String + || type.equals(pool.get(Constants.TYPE_NAME_BYTEARRAY)) // byte[] + || type.subtypeOf(pool.get(Constants.TYPE_NAME_LIST)) // List + || type.subtypeOf(pool.get(Constants.TYPE_NAME_MAP)) // Map + ) { + sb.append(field.getName()) + .append(Constants.CHAR_NAME_SPACE).append( + Constants.CHAR_NAME_EQUAL).append( + Constants.CHAR_NAME_SPACE); + } else { // MessageUnpackable + return; + } + } + + } + private void insertValueOfMethodAndLeftParenthesis(StringBuilder sb, CtClass type) throws NotFoundException { if (type.isPrimitive()) { // primitive type @@ -424,6 +501,7 @@ public class PackUnpackUtil { } else if (type.equals(CtClass.shortType)) { // short sb.append(Constants.METHOD_NAME_UNPACKSHORT); } else { // reference type + Class c = null; if (type.equals(pool.get(Constants.TYPE_NAME_BOOLEAN2))) { // Boolean sb.append(Constants.METHOD_NAME_UNPACKBOOLEAN); } else if (type.equals(pool.get(Constants.TYPE_NAME_BYTE2))) { // Byte @@ -450,8 +528,9 @@ public class PackUnpackUtil { } else if (type.subtypeOf(pool.get(Constants.TYPE_NAME_MAP))) { // Map sb.append(Constants.METHOD_NAME_UNPACKMAP); } else if (type.subtypeOf(pool - .get(Constants.TYPE_NAME_MSGUNPACKABLE))) { // MessageUnpackable - sb.append(Constants.METHOD_NAME_UNPACKOBJECT); + .get(Constants.TYPE_NAME_MSGUNPACKABLE)) + || ((c = getCache(type.getName())) != null)) { // MessageUnpackable + sb.append(Constants.METHOD_NAME_UNPACK); } else { throw new NotFoundException("unknown type: " + type.getName()); @@ -459,6 +538,22 @@ public class PackUnpackUtil { } } + private void insertUnpackMethodArgumenet(StringBuilder sb, + CtField field, CtClass type) throws NotFoundException { + if (type.isPrimitive()) { // primitive type + return; + } else { // reference type + Class c = null; + if (type.equals(pool.get(Constants.TYPE_NAME_MSGUNPACKABLE)) + || ((c = getCache(type.getName())) != null)) { + sb.append("(org.msgpack.MessageUnpackable)"); + sb.append(field.getName()); + } else { + return; + } + } + } + private void insertValueOfMethodAndRightParenthesis(StringBuilder sb, CtClass type) throws NotFoundException { if (type.isPrimitive()) { // primitive type @@ -480,7 +575,8 @@ public class PackUnpackUtil { } private void addMessageConvertMethod(CtClass enhCtClass, - CtClass origCtClass) throws CannotCompileException { + CtClass origCtClass, CtField[] fields) + throws CannotCompileException { StringBuilder sb = new StringBuilder(); sb.append(Constants.KEYWORD_MODIFIER_PUBLIC).append( Constants.CHAR_NAME_SPACE).append(Constants.TYPE_NAME_VOID) @@ -547,33 +643,33 @@ public class PackUnpackUtil { } } - public static void main(final String[] args) throws Exception { - @MessagePackUnpackable - class Image { - public String uri = ""; + @MessagePackUnpackable + public static class Image { + public String uri = ""; - public String title = ""; + public String title = ""; - public int width = 0; + public int width = 0; - public int height = 0; + public int height = 0; - public int size = 0; + public int size = 0; - public boolean equals(Image obj) { - return uri.equals(obj.uri) && title.equals(obj.title) - && width == obj.width && height == obj.height - && size == obj.size; - } - - public boolean equals(Object obj) { - if (obj.getClass() != Image.class) { - return false; - } - return equals((Image) obj); - } + public boolean equals(Image obj) { + return uri.equals(obj.uri) && title.equals(obj.title) + && width == obj.width && height == obj.height + && size == obj.size; } + public boolean equals(Object obj) { + if (obj.getClass() != Image.class) { + return false; + } + return equals((Image) obj); + } + } + + public static void main(final String[] args) throws Exception { PackUnpackUtil.getEnhancedClass(Image.class); Image src = (Image) PackUnpackUtil.newEnhancedInstance(Image.class); src.title = "msgpack"; diff --git a/java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java b/java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java index 1ec7d64e..4f6e74ef 100644 --- a/java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java +++ b/java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java @@ -3,6 +3,8 @@ package org.msgpack.util.annotation; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.math.BigInteger; +import java.util.List; +import java.util.Map; import junit.framework.TestCase; @@ -14,7 +16,7 @@ import org.msgpack.Unpacker; public class TestMessagePackUnpackable extends TestCase { @Test - public void testGeneralPrimitiveTypeFieldsClass() throws Exception { + public void testGeneralPrimitiveTypeFields() throws Exception { GeneralPrimitiveTypeFieldsClass src = (GeneralPrimitiveTypeFieldsClass) PackUnpackUtil .newEnhancedInstance(GeneralPrimitiveTypeFieldsClass.class); src.f0 = (byte) 0; @@ -101,8 +103,21 @@ public class TestMessagePackUnpackable extends TestCase { } } + public void testListAndMap() throws Exception { + // TODO + } + + @MessagePackUnpackable + public static class ListAndMapClass { + public List f0; + public Map f1; + + public ListAndMapClass() { + } + } + @Test - public void testPublicDefaultConstructorClass() throws Exception { + public void testDefaultConstructorModifiers() throws Exception { try { PackUnpackUtil.newEnhancedInstance(NoDefaultConstructorClass.class); fail(); @@ -111,21 +126,24 @@ public class TestMessagePackUnpackable extends TestCase { } assertTrue(true); try { - PackUnpackUtil.newEnhancedInstance(PrivateDefaultConstructorClass.class); + PackUnpackUtil + .newEnhancedInstance(PrivateDefaultConstructorClass.class); fail(); } catch (PackUnpackUtilException e) { assertTrue(true); } assertTrue(true); try { - PackUnpackUtil.newEnhancedInstance(ProtectedDefaultConstructorClass.class); + PackUnpackUtil + .newEnhancedInstance(ProtectedDefaultConstructorClass.class); assertTrue(true); } catch (PackUnpackUtilException e) { fail(); } assertTrue(true); try { - PackUnpackUtil.newEnhancedInstance(PackageDefaultConstructorClass.class); + PackUnpackUtil + .newEnhancedInstance(PackageDefaultConstructorClass.class); fail(); } catch (PackUnpackUtilException e) { assertTrue(true); @@ -158,7 +176,7 @@ public class TestMessagePackUnpackable extends TestCase { } @Test - public void testPublicModifierClass() throws Exception { + public void testClassModifiers() throws Exception { try { PackUnpackUtil.newEnhancedInstance(PrivateModifierClass.class); fail(); @@ -197,7 +215,7 @@ public class TestMessagePackUnpackable extends TestCase { } @Test - public void testFinalAndAbstractModifierClass() throws Exception { + public void testFinalClassAndAbstractClass() throws Exception { try { PackUnpackUtil.newEnhancedInstance(FinalModifierClass.class); fail(); @@ -223,7 +241,7 @@ public class TestMessagePackUnpackable extends TestCase { } @Test - public void testInterfaceAndEnum() throws Exception { + public void testInterfaceAndEnumType() throws Exception { try { PackUnpackUtil.newEnhancedInstance(SampleInterface.class); fail(); @@ -249,32 +267,128 @@ public class TestMessagePackUnpackable extends TestCase { } @Test - public void testFinalFieldClass() throws Exception { - + public void testFieldModifiers() throws Exception { + FieldModifiersClass src = (FieldModifiersClass) PackUnpackUtil + .newEnhancedInstance(FieldModifiersClass.class); + src.f0 = 0; + src.f2 = 2; + src.f3 = 3; + src.f4 = 4; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + FieldModifiersClass dst = (FieldModifiersClass) PackUnpackUtil + .newEnhancedInstance(FieldModifiersClass.class); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + pac.unpack((MessageUnpackable) dst); + assertTrue(src.f0 == dst.f0); + assertTrue(src.f1 == dst.f1); + assertTrue(src.f2 != dst.f2); + assertTrue(src.f3 == dst.f3); + assertTrue(src.f4 != dst.f4); } - @Test - public void testPrivateFieldClass() throws Exception { - - } - - @Test - public void testProtectedFieldClass() throws Exception { - - } - - @Test - public void testNonModifierFieldClass() throws Exception { + @MessagePackUnpackable + public static class FieldModifiersClass { + public int f0; + public final int f1 = 1; + private int f2; + protected int f3; + int f4; + public FieldModifiersClass() { + } } @Test public void testNestedAnnotatedFieldClass() throws Exception { + NestedClass src2 = (NestedClass) PackUnpackUtil + .newEnhancedInstance(NestedClass.class); + BaseClass src = (BaseClass) PackUnpackUtil + .newEnhancedInstance(BaseClass.class); + src.f0 = 0; + src2.f2 = 2; + src.f1 = src2; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + NestedClass dst2 = (NestedClass) PackUnpackUtil + .newEnhancedInstance(NestedClass.class); + BaseClass dst = (BaseClass) PackUnpackUtil + .newEnhancedInstance(BaseClass.class); + dst.f1 = dst2; + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + pac.unpack((MessageUnpackable) dst); + assertTrue(src.f0 == dst.f0); + assertTrue(src2.f2 == dst.f1.f2); + } + @MessagePackUnpackable + public static class BaseClass { + public int f0; + public NestedClass f1; + + public BaseClass() { + } + } + + @MessagePackUnpackable + public static class NestedClass { + public int f2; + + public NestedClass() { + } } @Test - public void testSuperClass() throws Exception { + public void testExtendedClass() throws Exception { + SampleSubClass src = (SampleSubClass) PackUnpackUtil + .newEnhancedInstance(SampleSubClass.class); + src.f0 = 0; + src.f2 = 2; + src.f3 = 3; + src.f4 = 4; + src.f5 = 5; + src.f8 = 8; + src.f9 = 9; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + SampleSubClass dst = (SampleSubClass) PackUnpackUtil + .newEnhancedInstance(SampleSubClass.class); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + pac.unpack((MessageUnpackable) dst); + assertTrue(src.f0 == dst.f0); + assertTrue(src.f1 == dst.f1); + assertTrue(src.f2 != dst.f2); + assertTrue(src.f3 == dst.f3); + assertTrue(src.f4 != dst.f4); + assertTrue(src.f5 == dst.f5); + assertTrue(src.f6 == dst.f6); + assertTrue(src.f8 == dst.f8); + assertTrue(src.f9 != dst.f9); + } + @MessagePackUnpackable + public static class SampleSubClass extends SampleSuperClass { + public int f0; + public final int f1 = 1; + private int f2; + protected int f3; + int f4; + + public SampleSubClass() { + } + } + + public static class SampleSuperClass { + public int f5; + public final int f6 = 2; + private int f7; + protected int f8; + int f9; + + public SampleSuperClass() { + } } }