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 37b9e0d5..5efef792 100644 --- a/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtil.java +++ b/java/src/main/java/org/msgpack/util/annotation/PackUnpackUtil.java @@ -3,6 +3,7 @@ package org.msgpack.util.annotation; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.lang.reflect.Modifier; import java.math.BigInteger; import java.util.List; import java.util.Map; @@ -124,6 +125,8 @@ public class PackUnpackUtil { static final String VARIABLE_NAME_OBJ = "obj"; + static final String METHOD_NAME_VALUEOF = "valueOf"; + static final String METHOD_NAME_MSGPACK = "messagePack"; static final String METHOD_NAME_MSGUNPACK = "messageUnpack"; @@ -187,7 +190,8 @@ public class PackUnpackUtil { String origName = origClass.getName(); String enhName = origName + Constants.POSTFIX_TYPE_NAME_ENHANCER; CtClass origCtClass = pool.get(origName); - checkPackUnpackAnnotation(origCtClass); + checkClassValidation(origCtClass); + checkDefaultConstructorValidation(origCtClass); CtClass enhCtClass = pool.makeClass(enhName); setSuperclass(enhCtClass, origCtClass); setInterfaces(enhCtClass); @@ -198,6 +202,28 @@ public class PackUnpackUtil { return createClass(enhCtClass); } + private void checkClassValidation(CtClass origCtClass) { + // not public, abstract, final + int mod = origCtClass.getModifiers(); + if ((!Modifier.isPublic(mod)) || Modifier.isAbstract(mod) + || Modifier.isFinal(mod)) { + throwClassValidationException(origCtClass); + } + // interface, enum + if (origCtClass.isInterface() || origCtClass.isEnum()) { + throwClassValidationException(origCtClass); + } + // annotation + checkPackUnpackAnnotation(origCtClass); + } + + private static void throwClassValidationException(CtClass origCtClass) { + throw new PackUnpackUtilException( + "it must be a public class and have @" + + MessagePackUnpackable.class.getName() + ": " + + origCtClass.getName()); + } + private void checkPackUnpackAnnotation(CtClass origCtClass) { try { Object[] objs = origCtClass.getAnnotations(); @@ -206,14 +232,32 @@ public class PackUnpackUtil { return; } } - throw new PackUnpackUtilException( - "Not annotated with this class: " - + origCtClass.getName()); + throwClassValidationException(origCtClass); } catch (ClassNotFoundException e) { throw new PackUnpackUtilException(e.getMessage(), e); } } + private void checkDefaultConstructorValidation(CtClass origCtClass) { + CtConstructor cons = null; + try { + cons = origCtClass.getDeclaredConstructor(new CtClass[0]); + } catch (NotFoundException e) { + throwConstructoValidationException(origCtClass); + } + int mod = cons.getModifiers(); + if (!(Modifier.isPublic(mod) || Modifier.isProtected(mod))) { + throwConstructoValidationException(origCtClass); + } + } + + private static void throwConstructoValidationException( + CtClass origCtClass) { + throw new PackUnpackUtilException( + "it must have a public zero-argument constructor: " + + origCtClass.getName()); + } + private void setSuperclass(CtClass enhCtClass, CtClass origCtClass) throws CannotCompileException { enhCtClass.setSuperclass(origCtClass); @@ -328,9 +372,43 @@ public class PackUnpackUtil { CtClass type = field.getType(); sb.append(field.getName()).append(Constants.CHAR_NAME_SPACE) .append(Constants.CHAR_NAME_EQUAL).append( - Constants.CHAR_NAME_SPACE).append( - Constants.VARIABLE_NAME_PK).append( - Constants.CHAR_NAME_DOT); + Constants.CHAR_NAME_SPACE); + 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); + insertValueOfMethodAndRightParenthesis(sb, type); + sb.append(Constants.CHAR_NAME_SEMICOLON).append( + Constants.CHAR_NAME_SPACE); + + } + + private void insertValueOfMethodAndLeftParenthesis(StringBuilder sb, + CtClass type) throws NotFoundException { + if (type.isPrimitive()) { // primitive type + return; + } 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 + ) { + sb.append(type.getName()).append(Constants.CHAR_NAME_DOT) + .append(Constants.METHOD_NAME_VALUEOF).append( + Constants.CHAR_NAME_LEFT_PARENTHESIS); + } else { + return; + } + } + } + + private void insertUnpackMethod(StringBuilder sb, CtClass type) + throws NotFoundException { if (type.equals(CtClass.booleanType)) { // boolean sb.append(Constants.METHOD_NAME_UNPACKBOOLEAN); } else if (type.equals(CtClass.byteType)) { // byte @@ -379,10 +457,26 @@ public class PackUnpackUtil { + type.getName()); } } - sb.append(Constants.CHAR_NAME_LEFT_PARENTHESIS).append( - Constants.CHAR_NAME_RIGHT_PARENTHESIS).append( - Constants.CHAR_NAME_SEMICOLON).append( - Constants.CHAR_NAME_SPACE); + } + + private void insertValueOfMethodAndRightParenthesis(StringBuilder sb, + CtClass type) throws NotFoundException { + if (type.isPrimitive()) { // primitive type + return; + } 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 + ) { + sb.append(Constants.CHAR_NAME_RIGHT_PARENTHESIS); + } else { + return; + } + } } private void addMessageConvertMethod(CtClass enhCtClass, diff --git a/java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java b/java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java new file mode 100644 index 00000000..1ec7d64e --- /dev/null +++ b/java/src/test/java/org/msgpack/util/annotation/TestMessagePackUnpackable.java @@ -0,0 +1,280 @@ +package org.msgpack.util.annotation; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.math.BigInteger; + +import junit.framework.TestCase; + +import org.junit.Test; +import org.msgpack.MessageUnpackable; +import org.msgpack.Packer; +import org.msgpack.Unpacker; + +public class TestMessagePackUnpackable extends TestCase { + + @Test + public void testGeneralPrimitiveTypeFieldsClass() throws Exception { + GeneralPrimitiveTypeFieldsClass src = (GeneralPrimitiveTypeFieldsClass) PackUnpackUtil + .newEnhancedInstance(GeneralPrimitiveTypeFieldsClass.class); + src.f0 = (byte) 0; + src.f1 = 1; + src.f2 = 2; + src.f3 = 3; + src.f4 = 4; + src.f5 = 5; + src.f6 = false; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + GeneralPrimitiveTypeFieldsClass dst = (GeneralPrimitiveTypeFieldsClass) PackUnpackUtil + .newEnhancedInstance(GeneralPrimitiveTypeFieldsClass.class); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + pac.unpack((MessageUnpackable) dst); + assertEquals(src.f0, dst.f0); + assertEquals(src.f1, dst.f1); + assertEquals(src.f2, dst.f2); + assertEquals(src.f3, dst.f3); + assertEquals(src.f4, dst.f4); + assertEquals(src.f5, dst.f5); + assertEquals(src.f6, dst.f6); + } + + @MessagePackUnpackable + public static class GeneralPrimitiveTypeFieldsClass { + public byte f0; + public short f1; + public int f2; + public long f3; + public float f4; + public double f5; + public boolean f6; + + public GeneralPrimitiveTypeFieldsClass() { + } + } + + @Test + public void testGeneralReferenceTypeFieldsClass() throws Exception { + GeneralReferenceTypeFieldsClass src = (GeneralReferenceTypeFieldsClass) PackUnpackUtil + .newEnhancedInstance(GeneralReferenceTypeFieldsClass.class); + src.f0 = 0; + src.f1 = 1; + src.f2 = 2; + src.f3 = (long) 3; + src.f4 = (float) 4; + src.f5 = (double) 5; + src.f6 = false; + src.f7 = new BigInteger("7"); + src.f8 = "8"; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Packer(out).pack(src); + GeneralReferenceTypeFieldsClass dst = (GeneralReferenceTypeFieldsClass) PackUnpackUtil + .newEnhancedInstance(GeneralReferenceTypeFieldsClass.class); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Unpacker pac = new Unpacker(in); + pac.unpack((MessageUnpackable) dst); + assertEquals(src.f0, dst.f0); + assertEquals(src.f1, dst.f1); + assertEquals(src.f2, dst.f2); + assertEquals(src.f3, dst.f3); + assertEquals(src.f4, dst.f4); + assertEquals(src.f5, dst.f5); + assertEquals(src.f6, dst.f6); + assertEquals(src.f7, dst.f7); + assertEquals(src.f8, dst.f8); + } + + @MessagePackUnpackable + public static class GeneralReferenceTypeFieldsClass { + public Byte f0; + public Short f1; + public Integer f2; + public Long f3; + public Float f4; + public Double f5; + public Boolean f6; + public BigInteger f7; + public String f8; + + public GeneralReferenceTypeFieldsClass() { + } + } + + @Test + public void testPublicDefaultConstructorClass() throws Exception { + try { + PackUnpackUtil.newEnhancedInstance(NoDefaultConstructorClass.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + try { + PackUnpackUtil.newEnhancedInstance(PrivateDefaultConstructorClass.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + try { + PackUnpackUtil.newEnhancedInstance(ProtectedDefaultConstructorClass.class); + assertTrue(true); + } catch (PackUnpackUtilException e) { + fail(); + } + assertTrue(true); + try { + PackUnpackUtil.newEnhancedInstance(PackageDefaultConstructorClass.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + } + + @MessagePackUnpackable + public static class NoDefaultConstructorClass { + public NoDefaultConstructorClass(int i) { + } + } + + @MessagePackUnpackable + public static class PrivateDefaultConstructorClass { + private PrivateDefaultConstructorClass() { + } + } + + @MessagePackUnpackable + public static class ProtectedDefaultConstructorClass { + protected ProtectedDefaultConstructorClass() { + } + } + + @MessagePackUnpackable + public static class PackageDefaultConstructorClass { + PackageDefaultConstructorClass() { + } + } + + @Test + public void testPublicModifierClass() throws Exception { + try { + PackUnpackUtil.newEnhancedInstance(PrivateModifierClass.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + try { + PackUnpackUtil.newEnhancedInstance(ProtectedModifierClass.class); + assertTrue(true); + } catch (PackUnpackUtilException e) { + fail(); + } + assertTrue(true); + try { + PackUnpackUtil.newEnhancedInstance(PackageModifierClass.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + } + + @MessagePackUnpackable + private static class PrivateModifierClass { + } + + @MessagePackUnpackable + protected static class ProtectedModifierClass { + protected ProtectedModifierClass() { + } + } + + @MessagePackUnpackable + static class PackageModifierClass { + } + + @Test + public void testFinalAndAbstractModifierClass() throws Exception { + try { + PackUnpackUtil.newEnhancedInstance(FinalModifierClass.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + try { + PackUnpackUtil.newEnhancedInstance(AbstractModifierClass.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + } + + @MessagePackUnpackable + public final static class FinalModifierClass { + } + + @MessagePackUnpackable + public abstract static class AbstractModifierClass { + } + + @Test + public void testInterfaceAndEnum() throws Exception { + try { + PackUnpackUtil.newEnhancedInstance(SampleInterface.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + try { + PackUnpackUtil.newEnhancedInstance(SampleEnum.class); + fail(); + } catch (PackUnpackUtilException e) { + assertTrue(true); + } + assertTrue(true); + } + + @MessagePackUnpackable + public interface SampleInterface { + } + + @MessagePackUnpackable + public enum SampleEnum { + } + + @Test + public void testFinalFieldClass() throws Exception { + + } + + @Test + public void testPrivateFieldClass() throws Exception { + + } + + @Test + public void testProtectedFieldClass() throws Exception { + + } + + @Test + public void testNonModifierFieldClass() throws Exception { + + } + + @Test + public void testNestedAnnotatedFieldClass() throws Exception { + + } + + @Test + public void testSuperClass() throws Exception { + + } +}