From 703fdbc01d68340a98fd30f705226c7fac62f846 Mon Sep 17 00:00:00 2001
From: FURUHASHI Sadayuki <frsyuki@users.sourceforge.jp>
Date: Wed, 8 Dec 2010 19:38:12 +0900
Subject: [PATCH] java: array type support

---
 .../template/BuiltInTemplateLoader.java       |   1 -
 .../template/JavassistTemplateBuilder.java    |  69 +++-
 .../msgpack/template/ObjectArrayTemplate.java |  80 ----
 .../template/ReflectionTemplateBuilder.java   | 114 ++++++
 .../org/msgpack/template/TemplateBuilder.java |  39 ++
 .../msgpack/template/TemplateRegistry.java    |  53 +--
 .../src/test/java/org/msgpack/TestArrays.java | 355 ++++++++++++++++++
 7 files changed, 578 insertions(+), 133 deletions(-)
 delete mode 100644 java/src/main/java/org/msgpack/template/ObjectArrayTemplate.java
 create mode 100644 java/src/test/java/org/msgpack/TestArrays.java

diff --git a/java/src/main/java/org/msgpack/template/BuiltInTemplateLoader.java b/java/src/main/java/org/msgpack/template/BuiltInTemplateLoader.java
index 37e3a0f6..9ac9a8ed 100644
--- a/java/src/main/java/org/msgpack/template/BuiltInTemplateLoader.java
+++ b/java/src/main/java/org/msgpack/template/BuiltInTemplateLoader.java
@@ -42,7 +42,6 @@ public class BuiltInTemplateLoader {
 		ListTemplate.load();
 		MapTemplate.load();
 		NullableTemplate.load();
-		ObjectArrayTemplate.load();
 	}
 }
 
diff --git a/java/src/main/java/org/msgpack/template/JavassistTemplateBuilder.java b/java/src/main/java/org/msgpack/template/JavassistTemplateBuilder.java
index dca54101..0f22b9ba 100644
--- a/java/src/main/java/org/msgpack/template/JavassistTemplateBuilder.java
+++ b/java/src/main/java/org/msgpack/template/JavassistTemplateBuilder.java
@@ -72,16 +72,6 @@ public class JavassistTemplateBuilder extends TemplateBuilder {
 		return seqId++;
 	}
 
-	public static abstract class JavassistTemplate extends AbstractTemplate {
-		public Class targetClass;
-		public Template[] templates;
-
-		public JavassistTemplate(Class targetClass, Template[] templates) {
-			this.targetClass = targetClass;
-			this.templates = templates;
-		}
-	}
-
 	private static abstract class BuildContextBase {
 		protected JavassistTemplateBuilder director;
 		protected Class<?> origClass;
@@ -101,9 +91,9 @@ public class JavassistTemplateBuilder extends TemplateBuilder {
 			this.director = director;
 		}
 
-		protected Template build(Class<?> targetClass) {
+		protected Template build(String className) {
 			try {
-				reset(targetClass);
+				reset(className);
 				buildClass();
 				buildConstructor();
 				buildMethodInit();
@@ -124,10 +114,8 @@ public class JavassistTemplateBuilder extends TemplateBuilder {
 			}
 		}
 
-		protected void reset(Class<?> targetClass) {
-			this.origClass = targetClass;
-			this.origName = this.origClass.getName();
-			this.tmplName = this.origName + "_$$_Template" + director.nextSeqId();
+		protected void reset(String className) {
+			this.tmplName = className + "_$$_Template" + director.nextSeqId();
 			this.tmplCtClass = director.makeCtClass(this.tmplName);
 		}
 
@@ -220,8 +208,20 @@ public class JavassistTemplateBuilder extends TemplateBuilder {
 		}
 	}
 
+	public static abstract class JavassistTemplate extends AbstractTemplate {
+		public Class targetClass;
+		public Template[] templates;
+
+		public JavassistTemplate(Class targetClass, Template[] templates) {
+			this.targetClass = targetClass;
+			this.templates = templates;
+		}
+	}
+
 	private static class BuildContext extends BuildContextBase {
 		protected FieldEntry[] entries;
+		protected Class<?> origClass;
+		protected String origName;
 		protected Template[] templates;
 		protected int minimumArrayLength;
 
@@ -232,7 +232,9 @@ public class JavassistTemplateBuilder extends TemplateBuilder {
 		public Template buildTemplate(Class<?> targetClass, FieldEntry[] entries, Template[] templates) {
 			this.entries = entries;
 			this.templates = templates;
-			return build(targetClass);
+			this.origClass = targetClass;
+			this.origName = this.origClass.getName();
+			return build(this.origName);
 		}
 
 		protected void setSuperClass() throws CannotCompileException, NotFoundException {
@@ -554,5 +556,38 @@ public class JavassistTemplateBuilder extends TemplateBuilder {
 	public Template buildOrdinalEnumTemplate(Class<?> targetClass, Enum<?>[] entries) {
 		return new JavassistOrdinalEnumTemplate(entries);
 	}
+
+	public Template buildArrayTemplate(Type arrayType, Type genericBaseType, Class<?> baseClass, int dim) {
+		if(dim == 1) {
+			if(baseClass == boolean.class) {
+				return BooleanArrayTemplate.getInstance();
+			} else if(baseClass == short.class) {
+				return ShortArrayTemplate.getInstance();
+			} else if(baseClass == int.class) {
+				return IntArrayTemplate.getInstance();
+			} else if(baseClass == long.class) {
+				return LongArrayTemplate.getInstance();
+			} else if(baseClass == float.class) {
+				return FloatArrayTemplate.getInstance();
+			} else if(baseClass == double.class) {
+				return DoubleArrayTemplate.getInstance();
+			} else {
+				// FIXME
+				Template baseTemplate = TemplateRegistry.lookup(genericBaseType);
+				return new ReflectionTemplateBuilder.ReflectionObjectArrayTemplate(baseClass, baseTemplate);
+			}
+		} else if(dim == 2) {
+			// FIXME
+			Class<?> componentClass = Array.newInstance(baseClass, 0).getClass();
+			Template componentTemplate = buildArrayTemplate(arrayType, genericBaseType, baseClass, dim-1);
+			return new ReflectionTemplateBuilder.ReflectionMultidimentionalArrayTemplate(componentClass, componentTemplate);
+		} else {
+			// FIXME
+			ReflectionTemplateBuilder.ReflectionMultidimentionalArrayTemplate componentTemplate = (ReflectionTemplateBuilder.ReflectionMultidimentionalArrayTemplate)
+				buildArrayTemplate(arrayType, genericBaseType, baseClass, dim-1);
+			Class<?> componentClass = Array.newInstance(componentTemplate.getComponentClass(), 0).getClass();
+			return new ReflectionTemplateBuilder.ReflectionMultidimentionalArrayTemplate(componentClass, componentTemplate);
+		}
+	}
 }
 
diff --git a/java/src/main/java/org/msgpack/template/ObjectArrayTemplate.java b/java/src/main/java/org/msgpack/template/ObjectArrayTemplate.java
deleted file mode 100644
index f77a5b05..00000000
--- a/java/src/main/java/org/msgpack/template/ObjectArrayTemplate.java
+++ /dev/null
@@ -1,80 +0,0 @@
-//
-// MessagePack for Java
-//
-// Copyright (C) 2009-2010 FURUHASHI Sadayuki
-//
-//    Licensed 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.msgpack.template;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.io.IOException;
-import org.msgpack.*;
-
-// FIXME
-public class ObjectArrayTemplate implements Template {
-	static void load() { }
-
-	private Template componentTemplate;
-
-	public ObjectArrayTemplate(Template componentTemplate) {
-		this.componentTemplate = componentTemplate;
-	}
-
-	public Template getcomponentTemplate() {
-		return componentTemplate;
-	}
-
-	@SuppressWarnings("unchecked")
-	public void pack(Packer pk, Object target) throws IOException {
-		if(!(target instanceof Object[])) {
-			throw new MessageTypeException();
-		}
-		Object[] array = (Object[])target;  // FIXME
-		pk.packArray(array.length);
-		for(Object a : array) {
-			componentTemplate.pack(pk, a);
-		}
-	}
-
-	public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException {
-		int length = pac.unpackArray();
-		Object[] array;  // FIXME
-		if(to != null && to instanceof Object[] && ((Object[])to).length == length) {
-			array = (Object[])to;
-		} else {
-			array = new Object[length];
-		}
-		for(int i=0; i < length; i++) {
-			array[i] = componentTemplate.unpack(pac, null);
-		}
-		return array;
-	}
-
-	public Object convert(MessagePackObject from, Object to) throws MessageTypeException {
-		MessagePackObject[] src = from.asArray();
-		Object[] array;  // FIXME
-		if(to != null && to instanceof Object[] && ((Object[])to).length == src.length) {
-			array = (Object[])to;
-		} else {
-			array = new Object[src.length];
-		}
-		for(int i=0; i < src.length; i++) {
-			MessagePackObject s = src[i];
-			array[i] = componentTemplate.convert(s, array[i]);
-		}
-		return array;
-	}
-}
-
diff --git a/java/src/main/java/org/msgpack/template/ReflectionTemplateBuilder.java b/java/src/main/java/org/msgpack/template/ReflectionTemplateBuilder.java
index ce820ba4..03ff2067 100644
--- a/java/src/main/java/org/msgpack/template/ReflectionTemplateBuilder.java
+++ b/java/src/main/java/org/msgpack/template/ReflectionTemplateBuilder.java
@@ -451,5 +451,119 @@ public class ReflectionTemplateBuilder extends TemplateBuilder {
 	public Template buildOrdinalEnumTemplate(Class<?> targetClass, Enum<?>[] entries) {
 		return new ReflectionOrdinalEnumTemplate(entries);
 	}
+
+
+	static class ReflectionObjectArrayTemplate extends AbstractTemplate {
+		private Class<?> componentClass;
+		private Template elementTemplate;
+
+		public ReflectionObjectArrayTemplate(Class<?> componentClass, Template elementTemplate) {
+			this.componentClass = componentClass;
+			this.elementTemplate = elementTemplate;
+		}
+
+		public void pack(Packer pk, Object target) throws IOException {
+			if(!(target instanceof Object[]) || !componentClass.isAssignableFrom(target.getClass().getComponentType())) {
+				throw new MessageTypeException();
+			}
+			Object[] array = (Object[])target;
+			int length = array.length;
+			pk.packArray(length);
+			for(int i=0; i < length; i++) {
+				elementTemplate.pack(pk, array[i]);
+			}
+		}
+
+		public Object unpack(Unpacker pac, Object to) throws IOException {
+			int length = pac.unpackArray();
+			Object[] array = (Object[])Array.newInstance(componentClass, length);
+			for(int i=0; i < length; i++) {
+				array[i] = elementTemplate.unpack(pac, null);
+			}
+			return array;
+		}
+
+		public Object convert(MessagePackObject from, Object to) throws MessageTypeException {
+			MessagePackObject[] src = from.asArray();
+			int length = src.length;
+			Object[] array = (Object[])Array.newInstance(componentClass, length);
+			for(int i=0; i < length; i++) {
+				array[i] = elementTemplate.convert(src[i], null);
+			}
+			return array;
+		}
+	}
+
+	static class ReflectionMultidimentionalArrayTemplate extends AbstractTemplate {
+		private Class<?> componentClass;
+		private Template componentTemplate;
+
+		public ReflectionMultidimentionalArrayTemplate(Class<?> componentClass, Template componentTemplate) {
+			this.componentClass = componentClass;
+			this.componentTemplate = componentTemplate;
+		}
+
+		Class<?> getComponentClass() {
+			return componentClass;
+		}
+
+		public void pack(Packer pk, Object target) throws IOException {
+			Object[] array = (Object[])target;
+			int length = array.length;
+			pk.packArray(length);
+			for(int i=0; i < length; i++) {
+				componentTemplate.pack(pk, array[i]);
+			}
+		}
+
+		public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException {
+			int length = pac.unpackArray();
+			Object[] array = (Object[])Array.newInstance(componentClass, length);
+			for(int i=0; i < length; i++) {
+				array[i] = componentTemplate.unpack(pac, null);
+			}
+			return array;
+		}
+
+		public Object convert(MessagePackObject from, Object to) throws MessageTypeException {
+			MessagePackObject[] src = from.asArray();
+			int length = src.length;
+			Object[] array = (Object[])Array.newInstance(componentClass, length);
+			for(int i=0; i < length; i++) {
+				array[i] = componentTemplate.convert(src[i], null);
+			}
+			return array;
+		}
+	}
+
+	public Template buildArrayTemplate(Type arrayType, Type genericBaseType, Class<?> baseClass, int dim) {
+		if(dim == 1) {
+			if(baseClass == boolean.class) {
+				return BooleanArrayTemplate.getInstance();
+			} else if(baseClass == short.class) {
+				return ShortArrayTemplate.getInstance();
+			} else if(baseClass == int.class) {
+				return IntArrayTemplate.getInstance();
+			} else if(baseClass == long.class) {
+				return LongArrayTemplate.getInstance();
+			} else if(baseClass == float.class) {
+				return FloatArrayTemplate.getInstance();
+			} else if(baseClass == double.class) {
+				return DoubleArrayTemplate.getInstance();
+			} else {
+				Template baseTemplate = TemplateRegistry.lookup(genericBaseType);
+				return new ReflectionObjectArrayTemplate(baseClass, baseTemplate);
+			}
+		} else if(dim == 2) {
+			Class<?> componentClass = Array.newInstance(baseClass, 0).getClass();
+			Template componentTemplate = buildArrayTemplate(arrayType, genericBaseType, baseClass, dim-1);
+			return new ReflectionMultidimentionalArrayTemplate(componentClass, componentTemplate);
+		} else {
+			ReflectionMultidimentionalArrayTemplate componentTemplate = (ReflectionMultidimentionalArrayTemplate)
+				buildArrayTemplate(arrayType, genericBaseType, baseClass, dim-1);
+			Class<?> componentClass = Array.newInstance(componentTemplate.getComponentClass(), 0).getClass();
+			return new ReflectionMultidimentionalArrayTemplate(componentClass, componentTemplate);
+		}
+	}
 }
 
diff --git a/java/src/main/java/org/msgpack/template/TemplateBuilder.java b/java/src/main/java/org/msgpack/template/TemplateBuilder.java
index b4f38810..c77a4338 100644
--- a/java/src/main/java/org/msgpack/template/TemplateBuilder.java
+++ b/java/src/main/java/org/msgpack/template/TemplateBuilder.java
@@ -113,6 +113,9 @@ public abstract class TemplateBuilder {
 	// Override this method
 	public abstract Template buildOrdinalEnumTemplate(Class<?> targetClass, Enum<?>[] entries);
 
+	// Override this method
+	public abstract Template buildArrayTemplate(Type arrayType, Type genericBaseType, Class<?> baseClass, int dim);
+
 
 	public Template buildTemplate(Class<?> targetClass, FieldList flist) throws NoSuchFieldException {
 		checkValidation(targetClass);
@@ -135,6 +138,38 @@ public abstract class TemplateBuilder {
 		return buildOrdinalEnumTemplate(targetClass, entries);
 	}
 
+	public Template buildArrayTemplate(Type arrayType) {
+		Type baseType;
+		Class<?> baseClass;
+		int dim = 1;
+		if(arrayType instanceof GenericArrayType) {
+			GenericArrayType type = (GenericArrayType)arrayType;
+			baseType = type.getGenericComponentType();
+			while(baseType instanceof GenericArrayType) {
+				baseType = ((GenericArrayType)baseType).getGenericComponentType();
+				dim += 1;
+			}
+			baseClass = (Class<?>)((ParameterizedType)baseType).getRawType();
+		} else {
+			Class<?> type = (Class<?>)arrayType;
+			baseClass = type.getComponentType();
+			while(baseClass.isArray()) {
+				baseClass = baseClass.getComponentType();
+				dim += 1;
+			}
+			baseType = baseClass;
+		}
+		return buildArrayTemplate(arrayType, baseType, baseClass, dim);
+	}
+
+	private static Type getComponentType(Type arrayType) {
+		if(arrayType instanceof GenericArrayType) {
+			return ((GenericArrayType)arrayType).getGenericComponentType();
+		} else {
+			return ((Class<?>)arrayType).getComponentType();
+		}
+	}
+
 
 	private static TemplateBuilder instance;
 	static {
@@ -172,6 +207,10 @@ public abstract class TemplateBuilder {
 		return instance.buildOrdinalEnumTemplate(targetClass);
 	}
 
+	public static Template buildArray(Type arrayType) {
+		return instance.buildArrayTemplate(arrayType);
+	}
+
 
 	private static void checkValidation(Class<?> targetClass) {
 		if(targetClass.isInterface()) {
diff --git a/java/src/main/java/org/msgpack/template/TemplateRegistry.java b/java/src/main/java/org/msgpack/template/TemplateRegistry.java
index 9c7b5485..3f98fb46 100644
--- a/java/src/main/java/org/msgpack/template/TemplateRegistry.java
+++ b/java/src/main/java/org/msgpack/template/TemplateRegistry.java
@@ -87,37 +87,36 @@ public class TemplateRegistry {
 
 	private static synchronized Template lookupImpl(Type targetType, boolean forceBuild, boolean fallbackDefault) {
 		Template tmpl;
-		Class<?> target;
-
-		// TODO
-		//if(targetType instanceof GenericArrayType) {
-		//	return lookupGenericArray((GenericArrayType)targetType);
-		//}
 
 		if(targetType instanceof ParameterizedType) {
+			// ParameterizedType is not a Class<?>?
 			tmpl = lookupGenericImpl((ParameterizedType)targetType);
 			if(tmpl != null) {
 				return tmpl;
 			}
-			target = (Class<?>)((ParameterizedType)targetType).getRawType();
-		} else {
-			target = (Class<?>)targetType;
+			targetType = ((ParameterizedType)targetType).getRawType();
 		}
 
-		tmpl = map.get(target);
+		tmpl = map.get(targetType);
 		if(tmpl != null) {
 			return tmpl;
 		}
 
-		// TODO
-		//if(target.isArray()) {
-		//	// FIXME can't distinguish type-erased Object[T<>]?
-		//	Type componentType = target.getComponentType();
-		//	Template componentTemplate = lookup(componentType);
-		//	tmpl = new ObjectArrayTemplate(componentTemplate);
-		//	register(target, tmpl);
-		//	return tmpl;
-		//}
+		if(targetType instanceof GenericArrayType) {
+			// GenericArrayType is not a Class<?>
+			tmpl = TemplateBuilder.buildArray(targetType);
+			register(targetType, tmpl);
+			return tmpl;
+		}
+
+		Class<?> target = (Class<?>)targetType;
+
+		if(target.isArray()) {
+			// FIXME can't distinguish type-erased T<>[]?
+			tmpl = TemplateBuilder.buildArray(target);
+			register(target, tmpl);
+			return tmpl;
+		}
 
 		if(isAnnotated(target, MessagePackMessage.class)) {
 			tmpl = TemplateBuilder.build(target);
@@ -166,22 +165,6 @@ public class TemplateRegistry {
 		}
 	}
 
-	private static synchronized Template lookupGenericArray(GenericArrayType arrayType) {
-		Template tmpl = map.get(arrayType);
-		if(tmpl != null) {
-			// TODO primitive types are included?
-			return tmpl;
-		}
-
-		Type componentType = arrayType.getGenericComponentType();
-		Template componentTemplate = lookup(componentType);
-		tmpl = new ObjectArrayTemplate(componentTemplate);
-
-		register(arrayType, tmpl);
-
-		return tmpl;
-	}
-
 	public static synchronized Template lookupGeneric(Type targetType) {
 		if(targetType instanceof ParameterizedType) {
 			ParameterizedType parameterizedType = (ParameterizedType)targetType;
diff --git a/java/src/test/java/org/msgpack/TestArrays.java b/java/src/test/java/org/msgpack/TestArrays.java
new file mode 100644
index 00000000..92bb44c2
--- /dev/null
+++ b/java/src/test/java/org/msgpack/TestArrays.java
@@ -0,0 +1,355 @@
+package org.msgpack;
+
+import org.msgpack.*;
+import org.msgpack.object.*;
+import org.msgpack.annotation.*;
+import static org.msgpack.Templates.*;
+
+import java.io.*;
+import java.util.*;
+import java.math.BigInteger;
+
+import org.junit.Test;
+import junit.framework.TestCase;
+
+public class TestArrays extends TestCase {
+	@MessagePackMessage
+	public static class PrimitiveTest {
+		public PrimitiveTest() { }
+		public boolean[] b = new boolean[0];
+		public short[] s = new short[0];
+		public int[] i = new int[0];
+		//public long[] l = new long[0];  // FIXME javassist?
+		public float[] f = new float[0];
+		//public double[] d = new double[0];  // FIXME javassist?
+	}
+
+	@Test
+	public void testPrimitive() {
+		PrimitiveTest t = new PrimitiveTest();
+		t.b = new boolean[] {true, false};
+		t.s = new short[] {0, 1};
+		t.i = new int[] {2, 3};
+		//t.l = new long[] {4, 5};
+		t.f = new float[] {2.0f, 4.0f};
+		//t.d = new double[] {8.0, 16.0};
+
+		byte[] raw = MessagePack.pack(t);
+
+		PrimitiveTest u = MessagePack.unpack(raw, PrimitiveTest.class);
+		assertEquals(t.b.length, u.b.length);
+		for(int i=0; i < t.b.length; i++) { assertEquals(t.b[i], u.b[i]); }
+		assertEquals(t.s.length, u.s.length);
+		for(int i=0; i < t.s.length; i++) { assertEquals(t.s[i], u.s[i]); }
+		assertEquals(t.i.length, u.i.length);
+		for(int i=0; i < t.i.length; i++) { assertEquals(t.i[i], u.i[i]); }
+		//assertEquals(t.l.length, u.l.length);
+		//for(int i=0; i < t.l.length; i++) { assertEquals(t.l[i], u.l[i]); }
+		assertEquals(t.f.length, u.f.length);
+		for(int i=0; i < t.f.length; i++) { assertEquals(t.f[i], u.f[i]); }
+		//assertEquals(t.d.length, u.d.length);
+		//for(int i=0; i < t.d.length; i++) { assertEquals(t.d[i], u.d[i]); }
+
+		PrimitiveTest c = MessagePack.unpack(raw).convert(PrimitiveTest.class);
+		assertEquals(t.b.length, c.b.length);
+		for(int i=0; i < t.b.length; i++) { assertEquals(t.b[i], c.b[i]); }
+		assertEquals(t.s.length, c.s.length);
+		for(int i=0; i < t.s.length; i++) { assertEquals(t.s[i], c.s[i]); }
+		assertEquals(t.i.length, c.i.length);
+		for(int i=0; i < t.i.length; i++) { assertEquals(t.i[i], c.i[i]); }
+		//assertEquals(t.l.length, c.l.length);
+		//for(int i=0; i < t.l.length; i++) { assertEquals(t.l[i], c.l[i]); }
+		assertEquals(t.f.length, c.f.length);
+		for(int i=0; i < t.f.length; i++) { assertEquals(t.f[i], c.f[i]); }
+		//assertEquals(t.d.length, c.d.length);
+		//for(int i=0; i < t.d.length; i++) { assertEquals(t.d[i], c.d[i]); }
+	}
+
+	@MessagePackMessage
+	public static class ReferenceTest {
+		public ReferenceTest() { }
+		public Boolean[] b;
+		public Short[] s;
+		public Integer[] i;
+		public Long[] l;
+		public Float[] f;
+		public Double[] d;
+		public String[] str;
+	}
+
+	@Test
+	public void testReference() {
+		ReferenceTest t = new ReferenceTest();
+		t.b = new Boolean[] {true, false};
+		t.s = new Short[] {0, 1};
+		t.i = new Integer[] {2, 3};
+		t.l = new Long[] {4l, 5l};
+		t.f = new Float[] {2.0f, 4.0f};
+		t.d = new Double[] {8.0, 16.0};
+		t.str = new String[] {"furuhashi", "java"};
+
+		byte[] raw = MessagePack.pack(t);
+
+		ReferenceTest u = MessagePack.unpack(raw, ReferenceTest.class);
+		assertEquals(t.b.length, u.b.length);
+		for(int i=0; i < t.b.length; i++) { assertEquals(t.b[i], u.b[i]); }
+		assertEquals(t.s.length, u.s.length);
+		for(int i=0; i < t.s.length; i++) { assertEquals(t.s[i], u.s[i]); }
+		assertEquals(t.i.length, u.i.length);
+		for(int i=0; i < t.i.length; i++) { assertEquals(t.i[i], u.i[i]); }
+		assertEquals(t.l.length, u.l.length);
+		for(int i=0; i < t.l.length; i++) { assertEquals(t.l[i], u.l[i]); }
+		assertEquals(t.f.length, u.f.length);
+		for(int i=0; i < t.f.length; i++) { assertEquals(t.f[i], u.f[i]); }
+		assertEquals(t.d.length, u.d.length);
+		for(int i=0; i < t.d.length; i++) { assertEquals(t.d[i], u.d[i]); }
+		assertEquals(t.str.length, u.str.length);
+		for(int i=0; i < t.str.length; i++) { assertEquals(t.str[i], u.str[i]); }
+
+		ReferenceTest c = MessagePack.unpack(raw).convert(ReferenceTest.class);
+		assertEquals(t.b.length, c.b.length);
+		for(int i=0; i < t.b.length; i++) { assertEquals(t.b[i], c.b[i]); }
+		assertEquals(t.s.length, c.s.length);
+		for(int i=0; i < t.s.length; i++) { assertEquals(t.s[i], c.s[i]); }
+		assertEquals(t.i.length, c.i.length);
+		for(int i=0; i < t.i.length; i++) { assertEquals(t.i[i], c.i[i]); }
+		assertEquals(t.l.length, c.l.length);
+		for(int i=0; i < t.l.length; i++) { assertEquals(t.l[i], c.l[i]); }
+		assertEquals(t.f.length, c.f.length);
+		for(int i=0; i < t.f.length; i++) { assertEquals(t.f[i], c.f[i]); }
+		assertEquals(t.d.length, c.d.length);
+		for(int i=0; i < t.d.length; i++) { assertEquals(t.d[i], c.d[i]); }
+		assertEquals(t.str.length, c.str.length);
+		for(int i=0; i < t.str.length; i++) { assertEquals(t.str[i], c.str[i]); }
+	}
+
+	@MessagePackMessage
+	public static class GenericsTest {
+		public GenericsTest() { }
+		public List<String>[] slist;
+		public Map<String, Integer>[] imap;
+	}
+
+	@Test
+	public void testGenerics() {
+		GenericsTest t = new GenericsTest();
+		t.slist = new List[2];
+		t.slist[0] = new ArrayList();
+		t.slist[0].add("aa");
+		t.slist[0].add("bb");
+		t.slist[1] = new ArrayList();
+		t.slist[1].add("cc");
+		t.imap = new Map[2];
+		t.imap[0] = new HashMap();
+		t.imap[0].put("aa", 1);
+		t.imap[0].put("bb", 2);
+		t.imap[1] = new HashMap();
+		t.imap[1].put("cc", 3);
+
+		byte[] raw = MessagePack.pack(t);
+
+		GenericsTest u = MessagePack.unpack(raw, GenericsTest.class);
+		assertEquals(t.slist.length, u.slist.length);
+		for(int i=0; i < t.slist.length; i++) {
+			assertEquals(t.slist[i].size(), u.slist[i].size());
+			for(int j=0; j < t.slist[i].size(); j++) {
+				assertEquals(t.slist[i].get(j), u.slist[i].get(j));
+			}
+		}
+		for(int i=0; i < t.imap.length; i++) {
+			assertEquals(t.imap[i].size(), u.imap[i].size());
+			for(String j : t.imap[i].keySet()) {
+				assertEquals(t.imap[i].get(j), u.imap[i].get(j));
+			}
+		}
+
+		GenericsTest c = MessagePack.unpack(raw).convert(GenericsTest.class);
+		assertEquals(t.slist.length, c.slist.length);
+		for(int i=0; i < t.slist.length; i++) {
+			assertEquals(t.slist[i].size(), c.slist[i].size());
+			for(int j=0; j < t.slist[i].size(); j++) {
+				assertEquals(t.slist[i].get(j), c.slist[i].get(j));
+			}
+		}
+		for(int i=0; i < t.imap.length; i++) {
+			assertEquals(t.imap[i].size(), c.imap[i].size());
+			for(String j : t.imap[i].keySet()) {
+				assertEquals(t.imap[i].get(j), c.imap[i].get(j));
+			}
+		}
+	}
+
+	@MessagePackMessage
+	public static class Dim2Test {
+		public Dim2Test() { }
+		public int[][] i;
+		public String[][] str;
+		public List<String>[][] slist;
+	}
+
+	@Test
+	public void testDim2() {
+		Dim2Test t = new Dim2Test();
+		t.i = new int[2][];
+		t.i[0] = new int[] {0, 1};
+		t.i[1] = new int[] {2, 3, 4};
+		t.str = new String[2][];
+		t.str[0] = new String[] {"aa", "bb"};
+		t.str[1] = new String[] {"cc", "dd", "ee"};
+		t.slist = new List[2][];
+		t.slist[0] = new List[1];
+		t.slist[0][0] = new ArrayList();
+		t.slist[0][0].add("ff");
+		t.slist[0][0].add("gg");
+		t.slist[1] = new List[2];
+		t.slist[1][0] = new ArrayList();
+		t.slist[1][0].add("hh");
+		t.slist[1][0].add("ii");
+		t.slist[1][1] = new ArrayList();
+		t.slist[1][1].add("jj");
+		t.slist[1][1].add("kk");
+
+		byte[] raw = MessagePack.pack(t);
+
+		Dim2Test u = MessagePack.unpack(raw, Dim2Test.class);
+		assertEquals(t.i.length, t.i.length);
+		for(int i=0; i < t.i.length; i++) {
+			assertEquals(t.i[i].length, u.i[i].length);
+			for(int j=0; j < t.i[i].length; j++) {
+				assertEquals(t.i[i][j], u.i[i][j]);
+			}
+		}
+		assertEquals(t.str.length, t.str.length);
+		for(int i=0; i < t.str.length; i++) {
+			assertEquals(t.str[i].length, u.str[i].length);
+			for(int j=0; j < t.str[i].length; j++) {
+				assertEquals(t.str[i][j], u.str[i][j]);
+			}
+		}
+		assertEquals(t.slist.length, t.slist.length);
+		for(int i=0; i < t.slist.length; i++) {
+			assertEquals(t.slist[i].length, u.slist[i].length);
+			for(int j=0; j < t.slist[i].length; j++) {
+				assertEquals(t.slist[i][j].size(), u.slist[i][j].size());
+				for(int k=0; k < t.slist[i][j].size(); k++) {
+					assertEquals(t.slist[i][j].get(k), u.slist[i][j].get(k));
+				}
+			}
+		}
+	}
+
+	@MessagePackMessage
+	public static class Dim3Test {
+		public Dim3Test() { }
+		public int[][][] i;
+		public String[][][] str;
+		public List<String>[][][] slist;
+	}
+
+	@Test
+	public void testDim3() {
+		Dim3Test t = new Dim3Test();
+		t.i = new int[2][][];
+		t.i[0] = new int[2][];
+		t.i[0][0] = new int[] {0, 1};
+		t.i[0][1] = new int[] {2, 3, 4};
+		t.i[1] = new int[1][];
+		t.i[1][0] = new int[] {5};
+		t.str = new String[2][][];
+		t.str[0] = new String[1][];
+		t.str[0][0] = new String[] {"aa", "bb"};
+		t.str[1] = new String[2][];
+		t.str[1][0] = new String[] {"cc", "dd", "ee"};
+		t.str[1][1] = new String[] {"ff"};
+		t.slist = new List[2][][];
+		t.slist[0] = new List[2][];
+		t.slist[0][0] = new List[1];
+		t.slist[0][0][0] = new ArrayList();
+		t.slist[0][0][0].add("ff");
+		t.slist[0][0][0].add("gg");
+		t.slist[0][1] = new List[2];
+		t.slist[0][1][0] = new ArrayList();
+		t.slist[0][1][0].add("hh");
+		t.slist[0][1][0].add("ii");
+		t.slist[0][1][1] = new ArrayList();
+		t.slist[0][1][1].add("jj");
+		t.slist[0][1][1].add("kk");
+		t.slist[1] = new List[1][];
+		t.slist[1][0] = new List[0];
+
+		byte[] raw = MessagePack.pack(t);
+
+		Dim3Test u = MessagePack.unpack(raw, Dim3Test.class);
+		assertEquals(t.i.length, t.i.length);
+		for(int i=0; i < t.i.length; i++) {
+			assertEquals(t.i[i].length, u.i[i].length);
+			for(int j=0; j < t.i[i].length; j++) {
+				for(int k=0; k < t.i[i].length; k++) {
+					assertEquals(t.i[i][j][k], u.i[i][j][k]);
+				}
+			}
+		}
+		assertEquals(t.str.length, t.str.length);
+		for(int i=0; i < t.str.length; i++) {
+			assertEquals(t.str[i].length, u.str[i].length);
+			for(int j=0; j < t.str[i].length; j++) {
+				assertEquals(t.str[i][j].length, u.str[i][j].length);
+				for(int k=0; k < t.str[i][j].length; k++) {
+					assertEquals(t.str[i][j][k], u.str[i][j][k]);
+				}
+			}
+		}
+		assertEquals(t.slist.length, t.slist.length);
+		for(int i=0; i < t.slist.length; i++) {
+			assertEquals(t.slist[i].length, u.slist[i].length);
+			for(int j=0; j < t.slist[i].length; j++) {
+				assertEquals(t.slist[i][j].length, u.slist[i][j].length);
+				for(int k=0; k < t.slist[i][j].length; k++) {
+					assertEquals(t.slist[i][j][k].size(), u.slist[i][j][k].size());
+					for(int l=0; l < t.slist[i][j][k].size(); l++) {
+						assertEquals(t.slist[i][j][k].get(l), u.slist[i][j][k].get(l));
+					}
+				}
+			}
+		}
+	}
+
+	@Test
+	public void testLocal() throws IOException {
+		int[][][] src = new int[10][20][30];
+		for (int i = 0; i < 10; ++i) {
+			for (int j = 0; j < 20; ++j) {
+				for (int k = 0; k < 30; ++k) {
+					src[i][j][k] = (int) (Math.random() * 100);
+				}
+			}
+		}
+
+		byte[] raw = MessagePack.pack(src);
+
+		int[][][] u = MessagePack.unpack(raw, int[][][].class);
+		assertEquals(src.length, u.length);
+		for(int i = 0; i < src.length; ++i) {
+			assertEquals(src[i].length, u[i].length);
+			for(int j = 0; j < src[i].length; ++j) {
+				assertEquals(src[i][j].length, u[i][j].length);
+				for(int k = 0; k < src[i][j].length; ++k) {
+					assertEquals(src[i][j][k], u[i][j][k]);
+				}
+			}
+		}
+
+		int[][][] c = MessagePack.unpack(raw).convert(int[][][].class);
+		assertEquals(src.length, c.length);
+		for(int i = 0; i < src.length; ++i) {
+			assertEquals(src[i].length, c[i].length);
+			for(int j = 0; j < src[i].length; ++j) {
+				assertEquals(src[i][j].length, c[i][j].length);
+				for(int k = 0; k < src[i][j].length; ++k) {
+					assertEquals(src[i][j][k], c[i][j][k]);
+				}
+			}
+		}
+	}
+}
+