From d5e583b09ea084c2e091f2eaf33bbf61866bdc3c Mon Sep 17 00:00:00 2001
From: takeshita <takeshita@geishatokyo.com>
Date: Mon, 4 Apr 2011 01:37:24 +0900
Subject: [PATCH 1/2] Implement Scala MessagePack. Change Java MessagePack to
 fit Scala's. Minor version up for Java's MessagePack.

---
 java/.settings/org.eclipse.jdt.core.prefs     |    6 +-
 java/msgpack.iml                              |   17 +
 java/pom.xml                                  |   18 +-
 .../main/java/org/msgpack/MessagePack.java    |    2 +-
 .../java/org/msgpack/annotation/Ignore.java   |    2 +-
 .../java/org/msgpack/annotation/Index.java    |    2 +-
 .../msgpack/annotation/MessagePackBeans.java  |   35 +
 .../java/org/msgpack/annotation/Nullable.java |    2 +-
 .../java/org/msgpack/annotation/Optional.java |    2 +-
 .../java/org/msgpack/annotation/Required.java |    2 +-
 .../org/msgpack/template/BeansFieldEntry.java |  149 ++
 .../template/BeansFieldEntryReader.java       |  174 ++
 .../BeansReflectionTemplateBuilder.java       |  321 +++
 .../java/org/msgpack/template/FieldEntry.java |  143 ++
 .../msgpack/template/FieldEntryReader.java    |  165 ++
 .../java/org/msgpack/template/FieldList.java  |   10 +-
 .../org/msgpack/template/IFieldEntry.java     |   25 +
 .../msgpack/template/IFieldEntryReader.java   |    8 +
 .../template/JavassistTemplateBuilder.java    |  513 +---
 .../template/ReflectionTemplateBuilder.java   |  178 +-
 .../msgpack/template/TemplateRegistry.java    |   47 +-
 .../builder/ArrayTemplateBuilder.java         |  173 ++
 .../builder/ArrayTemplateBuilderSelector.java |   35 +
 .../template/builder/BuilderSelector.java     |   27 +
 .../builder/BuilderSelectorRegistry.java      |  213 ++
 .../builder/CustomTemplateBuilder.java        |   53 +
 .../template/builder/EnumBuilderSelector.java |   26 +
 .../MessagePackBeansBuilderSelector.java      |   40 +
 .../MessagePackMessageBuilderSelector.java    |   38 +
 ...MessagePackOrdinalEnumBuilderSelector.java |   34 +
 .../builder/OrdinalEnumTemplateBuilder.java   |   68 +
 .../template/builder/TemplateBuilder.java     |  327 +++
 .../template/javassist/BeansBuildContext.java |  273 ++
 .../template/javassist/BuildContext.java      |  273 ++
 .../template/javassist/BuildContextBase.java  |  244 ++
 .../javassist/BuildContextFactory.java        |    9 +
 .../template/javassist/JavassistTemplate.java |   36 +
 .../src/test/java/org/msgpack/TestArrays.java |    4 +
 .../template/BeansEntryReaderTest.java        |  133 +
 .../org/msgpack/template/BeansEquals.java     |   83 +
 ...ionTemplateBuilderJavaBeansPackUnpack.java | 2207 ++++++++++++++++
 ...tReflectionTemplateBuilderPackConvert.java | 1417 +++++++++++
 ...stReflectionTemplateBuilderPackUnpack.java | 1422 +++++++++++
 ...estTemplateBuilderJavaBeansPackUnpack.java | 2234 +++++++++++++++++
 .../TestTemplateBuilderPackConvert.java       |   15 +-
 .../TestTemplateBuilderPackUnpack.java        |   16 +-
 scala/.gitignore                              |   40 +
 scala/pom.xml                                 |  158 ++
 scala/src/main/scala/org/msgpack/App.scala    |   15 +
 .../JavassistTypeScalaTemplateBuilder.scala   |  521 ++++
 .../scala/org/msgpack/ScalaMessagePack.scala  |   76 +
 .../org/msgpack/ScalaTemplateBuilder.scala    |   16 +
 .../ScalaTemplateBuilderSelector.scala        |   48 +
 .../template/ImmutableListTemplate.scala      |   49 +
 .../template/ImmutableMapTemplate.scala       |   54 +
 .../template/MutableListTemplate.scala        |   56 +
 .../msgpack/template/MutableMapTemplate.scala |   70 +
 .../scala/org/msgpack/ClassWithList.scala     |   21 +
 .../test/scala/org/msgpack/ClassWithMap.scala |   19 +
 .../org/msgpack/CollectionPackSpec.scala      |   71 +
 .../test/scala/org/msgpack/SampleClass.scala  |   77 +
 .../msgpack/ScalaFieldEntryReaderSpec.scala   |  120 +
 .../org/msgpack/ScalaMessagePackSpec.scala    |   90 +
 63 files changed, 12077 insertions(+), 645 deletions(-)
 create mode 100644 java/msgpack.iml
 create mode 100644 java/src/main/java/org/msgpack/annotation/MessagePackBeans.java
 create mode 100644 java/src/main/java/org/msgpack/template/BeansFieldEntry.java
 create mode 100644 java/src/main/java/org/msgpack/template/BeansFieldEntryReader.java
 create mode 100644 java/src/main/java/org/msgpack/template/BeansReflectionTemplateBuilder.java
 create mode 100644 java/src/main/java/org/msgpack/template/FieldEntry.java
 create mode 100644 java/src/main/java/org/msgpack/template/FieldEntryReader.java
 create mode 100644 java/src/main/java/org/msgpack/template/IFieldEntry.java
 create mode 100644 java/src/main/java/org/msgpack/template/IFieldEntryReader.java
 create mode 100644 java/src/main/java/org/msgpack/template/builder/ArrayTemplateBuilder.java
 create mode 100644 java/src/main/java/org/msgpack/template/builder/ArrayTemplateBuilderSelector.java
 create mode 100644 java/src/main/java/org/msgpack/template/builder/BuilderSelector.java
 create mode 100644 java/src/main/java/org/msgpack/template/builder/BuilderSelectorRegistry.java
 create mode 100644 java/src/main/java/org/msgpack/template/builder/CustomTemplateBuilder.java
 create mode 100644 java/src/main/java/org/msgpack/template/builder/EnumBuilderSelector.java
 create mode 100644 java/src/main/java/org/msgpack/template/builder/MessagePackBeansBuilderSelector.java
 create mode 100644 java/src/main/java/org/msgpack/template/builder/MessagePackMessageBuilderSelector.java
 create mode 100644 java/src/main/java/org/msgpack/template/builder/MessagePackOrdinalEnumBuilderSelector.java
 create mode 100644 java/src/main/java/org/msgpack/template/builder/OrdinalEnumTemplateBuilder.java
 create mode 100644 java/src/main/java/org/msgpack/template/builder/TemplateBuilder.java
 create mode 100644 java/src/main/java/org/msgpack/template/javassist/BeansBuildContext.java
 create mode 100644 java/src/main/java/org/msgpack/template/javassist/BuildContext.java
 create mode 100644 java/src/main/java/org/msgpack/template/javassist/BuildContextBase.java
 create mode 100644 java/src/main/java/org/msgpack/template/javassist/BuildContextFactory.java
 create mode 100644 java/src/main/java/org/msgpack/template/javassist/JavassistTemplate.java
 create mode 100644 java/src/test/java/org/msgpack/template/BeansEntryReaderTest.java
 create mode 100644 java/src/test/java/org/msgpack/template/BeansEquals.java
 create mode 100644 java/src/test/java/org/msgpack/template/TestReflectionTemplateBuilderJavaBeansPackUnpack.java
 create mode 100644 java/src/test/java/org/msgpack/template/TestReflectionTemplateBuilderPackConvert.java
 create mode 100644 java/src/test/java/org/msgpack/template/TestReflectionTemplateBuilderPackUnpack.java
 create mode 100644 java/src/test/java/org/msgpack/template/TestTemplateBuilderJavaBeansPackUnpack.java
 create mode 100644 scala/.gitignore
 create mode 100644 scala/pom.xml
 create mode 100644 scala/src/main/scala/org/msgpack/App.scala
 create mode 100644 scala/src/main/scala/org/msgpack/JavassistTypeScalaTemplateBuilder.scala
 create mode 100644 scala/src/main/scala/org/msgpack/ScalaMessagePack.scala
 create mode 100644 scala/src/main/scala/org/msgpack/ScalaTemplateBuilder.scala
 create mode 100644 scala/src/main/scala/org/msgpack/ScalaTemplateBuilderSelector.scala
 create mode 100644 scala/src/main/scala/org/msgpack/template/ImmutableListTemplate.scala
 create mode 100644 scala/src/main/scala/org/msgpack/template/ImmutableMapTemplate.scala
 create mode 100644 scala/src/main/scala/org/msgpack/template/MutableListTemplate.scala
 create mode 100644 scala/src/main/scala/org/msgpack/template/MutableMapTemplate.scala
 create mode 100644 scala/src/test/scala/org/msgpack/ClassWithList.scala
 create mode 100644 scala/src/test/scala/org/msgpack/ClassWithMap.scala
 create mode 100644 scala/src/test/scala/org/msgpack/CollectionPackSpec.scala
 create mode 100644 scala/src/test/scala/org/msgpack/SampleClass.scala
 create mode 100644 scala/src/test/scala/org/msgpack/ScalaFieldEntryReaderSpec.scala
 create mode 100644 scala/src/test/scala/org/msgpack/ScalaMessagePackSpec.scala

diff --git a/java/.settings/org.eclipse.jdt.core.prefs b/java/.settings/org.eclipse.jdt.core.prefs
index c75ad473..b20b311f 100755
--- a/java/.settings/org.eclipse.jdt.core.prefs
+++ b/java/.settings/org.eclipse.jdt.core.prefs
@@ -1,5 +1,5 @@
-#Mon Apr 19 22:18:48 JST 2010
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+#Sat Mar 26 02:44:20 JST 2011
 eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
 org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/java/msgpack.iml b/java/msgpack.iml
new file mode 100644
index 00000000..3a6d962d
--- /dev/null
+++ b/java/msgpack.iml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <output-test url="file://$MODULE_DIR$/target/test-classes" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+      <excludeFolder url="file://$MODULE_DIR$/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.8.1" level="project" />
+  </component>
+</module>
+
diff --git a/java/pom.xml b/java/pom.xml
index c8a19b3a..7663b30d 100755
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -3,7 +3,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.msgpack</groupId>
   <artifactId>msgpack</artifactId>
-  <version>0.5.1-devel</version>
+  <version>0.5.2-SNAPSHOT</version>
   <description>MessagePack for Java</description>
 
   <name>MessagePack for Java</name>
@@ -81,6 +81,20 @@
           <connectionUrl>scm:git://github.com/msgpack/msgpack.git</connectionUrl>
         </configuration>
       </plugin>
+      
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      
     </plugins>
   </build>
 
@@ -132,12 +146,14 @@
       <id>msgpack.org</id>
       <name>Repository at msgpack.org</name>
 	  <url>file://${project.build.directory}/website/maven2/</url>
+	  <!--<url>${deploy-release-url}</url>-->
     </repository>
     <snapshotRepository>
        <uniqueVersion>true</uniqueVersion>
        <id>msgpack.org</id>
        <name>Repository at msgpack.org</name>
 	   <url>file://${project.build.directory}/website/maven2/</url>
+	   <!--<url>${deploy-snapshot-url}</url>-->
     </snapshotRepository>
   </distributionManagement>
 
diff --git a/java/src/main/java/org/msgpack/MessagePack.java b/java/src/main/java/org/msgpack/MessagePack.java
index f3a73c06..c25023c4 100644
--- a/java/src/main/java/org/msgpack/MessagePack.java
+++ b/java/src/main/java/org/msgpack/MessagePack.java
@@ -22,8 +22,8 @@ import java.io.InputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import org.msgpack.template.TemplateRegistry;
-import org.msgpack.template.TemplateBuilder;
 import org.msgpack.template.FieldList;
+import org.msgpack.template.builder.TemplateBuilder;
 
 public class MessagePack {
 	public static byte[] pack(Object obj) {
diff --git a/java/src/main/java/org/msgpack/annotation/Ignore.java b/java/src/main/java/org/msgpack/annotation/Ignore.java
index 96a37bb6..e052e58a 100644
--- a/java/src/main/java/org/msgpack/annotation/Ignore.java
+++ b/java/src/main/java/org/msgpack/annotation/Ignore.java
@@ -22,7 +22,7 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-@Target({ElementType.FIELD, ElementType.PARAMETER})
+@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 public @interface Ignore {
 }
diff --git a/java/src/main/java/org/msgpack/annotation/Index.java b/java/src/main/java/org/msgpack/annotation/Index.java
index 7c1b6598..d3b2181f 100644
--- a/java/src/main/java/org/msgpack/annotation/Index.java
+++ b/java/src/main/java/org/msgpack/annotation/Index.java
@@ -22,7 +22,7 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-@Target({ElementType.FIELD, ElementType.PARAMETER})
+@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 public @interface Index {
 	int value();
diff --git a/java/src/main/java/org/msgpack/annotation/MessagePackBeans.java b/java/src/main/java/org/msgpack/annotation/MessagePackBeans.java
new file mode 100644
index 00000000..0c361360
--- /dev/null
+++ b/java/src/main/java/org/msgpack/annotation/MessagePackBeans.java
@@ -0,0 +1,35 @@
+//
+// 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.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.msgpack.template.FieldOption;
+
+/**
+ * Annotation for java beans class
+ * @author takeshita
+ *
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface MessagePackBeans {
+	FieldOption value() default FieldOption.DEFAULT;
+}
diff --git a/java/src/main/java/org/msgpack/annotation/Nullable.java b/java/src/main/java/org/msgpack/annotation/Nullable.java
index e6893e72..56aea89e 100644
--- a/java/src/main/java/org/msgpack/annotation/Nullable.java
+++ b/java/src/main/java/org/msgpack/annotation/Nullable.java
@@ -22,7 +22,7 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-@Target({ElementType.FIELD, ElementType.PARAMETER})
+@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 public @interface Nullable {
 }
diff --git a/java/src/main/java/org/msgpack/annotation/Optional.java b/java/src/main/java/org/msgpack/annotation/Optional.java
index 7894f881..a137ec86 100644
--- a/java/src/main/java/org/msgpack/annotation/Optional.java
+++ b/java/src/main/java/org/msgpack/annotation/Optional.java
@@ -22,7 +22,7 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-@Target({ElementType.FIELD, ElementType.PARAMETER})
+@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 public @interface Optional {
 }
diff --git a/java/src/main/java/org/msgpack/annotation/Required.java b/java/src/main/java/org/msgpack/annotation/Required.java
index 16311085..de1d816b 100644
--- a/java/src/main/java/org/msgpack/annotation/Required.java
+++ b/java/src/main/java/org/msgpack/annotation/Required.java
@@ -22,7 +22,7 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-@Target({ElementType.FIELD, ElementType.PARAMETER})
+@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 public @interface Required {
 }
diff --git a/java/src/main/java/org/msgpack/template/BeansFieldEntry.java b/java/src/main/java/org/msgpack/template/BeansFieldEntry.java
new file mode 100644
index 00000000..00e1bcad
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/BeansFieldEntry.java
@@ -0,0 +1,149 @@
+//
+// 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.beans.PropertyDescriptor;
+import java.io.IOException;
+import java.lang.reflect.*;
+import java.lang.annotation.*;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import org.msgpack.*;
+import org.msgpack.annotation.*;
+
+/**
+ * Field entry for Java beans property.
+ * @author takeshita
+ *
+ */
+public class BeansFieldEntry implements IFieldEntry {
+
+	PropertyDescriptor desc;
+	FieldOption option = FieldOption.DEFAULT;
+	
+	public BeansFieldEntry(PropertyDescriptor desc) {
+		this.desc = desc;
+	}
+
+	@Override
+	public String getName() {
+		return desc.getDisplayName();
+	}
+	public String getGetterName(){
+		return desc.getReadMethod().getName();
+	}
+	public String getSetterName(){
+		return desc.getWriteMethod().getName();
+	}
+
+	@Override
+	public Class<?> getType() {
+		return desc.getPropertyType();
+	}
+
+	@Override
+	public String getJavaTypeName() {
+		Class<?> type = getType();
+		if(type.isArray()) {
+			return arrayTypeToString(type);
+		} else {
+			return type.getName();
+		}
+	}
+	static String arrayTypeToString(Class<?> type) {
+		int dim = 1;
+		Class<?> baseType = type.getComponentType();
+		while(baseType.isArray()) {
+			baseType = baseType.getComponentType();
+			dim += 1;
+		}
+		StringBuilder sb = new StringBuilder();
+		sb.append(baseType.getName());
+		for (int i = 0; i < dim; ++i) {
+			sb.append("[]");
+		}
+		return sb.toString();
+	}
+
+	@Override
+	public Type getGenericType() {
+		return desc.getReadMethod().getGenericReturnType();
+	}
+
+	@Override
+	public FieldOption getOption() {
+		return option;
+	}
+	public void setOption(FieldOption option){
+		this.option = option;
+	}
+
+	@Override
+	public boolean isAvailable() {
+		return option != FieldOption.IGNORE;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.msgpack.template.IFieldEntry#isRequired()
+	 */
+	@Override
+	public boolean isRequired() {
+		return option == FieldOption.REQUIRED;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.msgpack.template.IFieldEntry#isOptional()
+	 */
+	@Override
+	public boolean isOptional() {
+		return option == FieldOption.OPTIONAL;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.msgpack.template.IFieldEntry#isNullable()
+	 */
+	@Override
+	public boolean isNullable() {
+		return option == FieldOption.NULLABLE;
+	}
+	
+	public Object get(Object target){
+		try {
+			return desc.getReadMethod().invoke(target);
+		} catch (IllegalArgumentException e) {
+			throw new MessageTypeException(e);
+		} catch (IllegalAccessException e) {
+			throw new MessageTypeException(e);
+		} catch (InvocationTargetException e) {
+			throw new MessageTypeException(e);
+		}
+	}
+	public void set(Object target , Object value){
+		try {
+			desc.getWriteMethod().invoke(target, value);
+		} catch (IllegalArgumentException e) {
+			throw new MessageTypeException(e);
+		} catch (IllegalAccessException e) {
+			throw new MessageTypeException(e);
+		} catch (InvocationTargetException e) {
+			throw new MessageTypeException(e);
+		}
+	}
+	
+}
\ No newline at end of file
diff --git a/java/src/main/java/org/msgpack/template/BeansFieldEntryReader.java b/java/src/main/java/org/msgpack/template/BeansFieldEntryReader.java
new file mode 100644
index 00000000..43536dcb
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/BeansFieldEntryReader.java
@@ -0,0 +1,174 @@
+package org.msgpack.template;
+
+import java.beans.BeanDescriptor;
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.msgpack.annotation.Ignore;
+import org.msgpack.annotation.Index;
+import org.msgpack.annotation.MessagePackMessage;
+import org.msgpack.annotation.Nullable;
+import org.msgpack.annotation.Optional;
+import org.msgpack.annotation.Required;
+
+/**
+ * List up Java beans property methods.
+ * @author takeshita
+ *
+ */
+public class BeansFieldEntryReader implements IFieldEntryReader{
+
+
+	public IFieldEntry[] convertFieldEntries(Class<?> targetClass, FieldList flist) throws NoSuchFieldException {
+		List<FieldList.Entry> src = flist.getList();
+		FieldEntry[] result = new FieldEntry[src.size()];
+		for(int i=0; i < src.size(); i++) {
+			FieldList.Entry s = src.get(i);
+			if(s.isAvailable()) {
+				result[i] = new FieldEntry(targetClass.getDeclaredField(s.getName()), s.getOption());
+			} else {
+				result[i] = new FieldEntry();
+			}
+		}
+		return result;
+	}
+	
+	@Override
+	public IFieldEntry[] readFieldEntries(Class<?> targetClass,
+			FieldOption implicitOption) {
+		BeanInfo desc;
+		try {
+			desc = Introspector.getBeanInfo(targetClass);
+		} catch (IntrospectionException e1) {
+			throw new TemplateBuildException("Class must be java beans class:" + targetClass.getName());
+		}
+		
+		PropertyDescriptor[] props = desc.getPropertyDescriptors();
+		ArrayList<PropertyDescriptor> list = new ArrayList<PropertyDescriptor>();
+		for(int i = 0;i < props.length;i++){
+			PropertyDescriptor pd = props[i];
+			if(!isIgnoreProp(pd)){
+				list.add(pd);
+			}
+		}
+		props = new PropertyDescriptor[list.size()];
+		list.toArray(props);
+		
+		BeansFieldEntry[] entries = new BeansFieldEntry[props.length];
+		for(int i = 0;i < props.length;i++){
+			PropertyDescriptor p = props[i];
+			int index = readPropIndex(p);
+			if(index >= 0){
+				if(entries[index] != null){
+					throw new TemplateBuildException("duplicated index: "+index);
+				}
+				if(index >= entries.length){
+					throw new TemplateBuildException("invalid index: "+index);
+				}
+				entries[index] = new BeansFieldEntry(p);
+				props[index] = null;
+			}
+		}
+		int insertIndex = 0;
+		for(int i = 0;i < props.length;i++){
+			PropertyDescriptor p = props[i];
+			if(p != null){
+				while(entries[insertIndex] != null){
+					insertIndex++;
+				}
+				entries[insertIndex] = new BeansFieldEntry(p);
+			}
+			
+		}
+	    for(int i = 0;i < entries.length;i++){
+	    	BeansFieldEntry e = entries[i];
+	    	FieldOption op = readPropOption(e.desc, implicitOption);
+	    	e.setOption(op);
+	    }
+		return entries;
+	}
+
+	public FieldOption readImplicitFieldOption(Class<?> targetClass) {
+		MessagePackMessage a = targetClass.getAnnotation(MessagePackMessage.class);
+		if(a == null) {
+			return FieldOption.DEFAULT;
+		}
+		return a.value();
+	}
+	
+
+	private FieldOption readPropOption(PropertyDescriptor desc, FieldOption implicitOption) {
+		
+		FieldOption forGetter = readMethodOption(desc.getReadMethod());
+		if(forGetter != FieldOption.DEFAULT){
+			return forGetter;
+		}
+		FieldOption forSetter = readMethodOption(desc.getWriteMethod());
+		if(forSetter != FieldOption.DEFAULT){
+			return forSetter;
+		}else{
+			return implicitOption;
+		}
+		
+	}
+	private FieldOption readMethodOption(Method method){
+
+		if(isAnnotated(method, Ignore.class)) {
+			return FieldOption.IGNORE;
+		} else if(isAnnotated(method, Required.class)) {
+			return FieldOption.REQUIRED;
+		} else if(isAnnotated(method, Optional.class)) {
+			return FieldOption.OPTIONAL;
+		} else if(isAnnotated(method, Nullable.class)) {
+			if(method.getDeclaringClass().isPrimitive()) {
+				return FieldOption.REQUIRED;
+			} else {
+				return FieldOption.NULLABLE;
+			}
+		}
+		return FieldOption.DEFAULT;
+	}
+
+	private int readPropIndex(PropertyDescriptor desc) {
+		
+		int forGetter = readMethodIndex(desc.getReadMethod());
+		if(forGetter >= 0){
+			return forGetter;
+		}
+		int forSetter = readMethodIndex(desc.getWriteMethod());
+		return forSetter;
+	}
+	private int readMethodIndex(Method method){
+		Index a = method.getAnnotation(Index.class);
+		if(a == null) {
+			return -1;
+		} else {
+			return a.value();
+		}
+	}
+
+	private boolean isAnnotated(AccessibleObject ao, Class<? extends Annotation> with) {
+		return ao.getAnnotation(with) != null;
+	}
+	boolean isIgnoreProp(PropertyDescriptor desc){
+		if(desc == null)return true;
+		Method getter = desc.getReadMethod();
+		Method setter = desc.getWriteMethod();
+		return getter == null ||
+		setter == null ||
+		!Modifier.isPublic(getter.getModifiers()) ||
+		!Modifier.isPublic(setter.getModifiers()) ||
+		isAnnotated(getter,Ignore.class) ||
+		isAnnotated(setter, Ignore.class);
+	}
+}
diff --git a/java/src/main/java/org/msgpack/template/BeansReflectionTemplateBuilder.java b/java/src/main/java/org/msgpack/template/BeansReflectionTemplateBuilder.java
new file mode 100644
index 00000000..8899399f
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/BeansReflectionTemplateBuilder.java
@@ -0,0 +1,321 @@
+package org.msgpack.template;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import org.msgpack.AbstractTemplate;
+import org.msgpack.MessagePackObject;
+import org.msgpack.MessageTypeException;
+import org.msgpack.Packer;
+import org.msgpack.Template;
+import org.msgpack.Unpacker;
+import org.msgpack.template.ReflectionTemplateBuilder.BooleanFieldEntry;
+import org.msgpack.template.ReflectionTemplateBuilder.ByteFieldEntry;
+import org.msgpack.template.ReflectionTemplateBuilder.DoubleFieldEntry;
+import org.msgpack.template.ReflectionTemplateBuilder.FloatFieldEntry;
+import org.msgpack.template.ReflectionTemplateBuilder.IntFieldEntry;
+import org.msgpack.template.ReflectionTemplateBuilder.LongFieldEntry;
+import org.msgpack.template.ReflectionTemplateBuilder.NullFieldEntry;
+import org.msgpack.template.ReflectionTemplateBuilder.ObjectFieldEntry;
+import org.msgpack.template.ReflectionTemplateBuilder.ShortFieldEntry;
+import org.msgpack.template.builder.CustomTemplateBuilder;
+
+/**
+ * Class for building java reflection template builder for java beans class.
+ * @author takeshita
+ *
+ */
+public class BeansReflectionTemplateBuilder extends CustomTemplateBuilder{
+
+	IFieldEntryReader reader = new BeansFieldEntryReader();
+	
+	public BeansReflectionTemplateBuilder(){}
+
+	@Override
+	public IFieldEntryReader getFieldEntryReader(){
+		return reader;
+	}
+	
+	static class ReflectionEntry{
+		BeansFieldEntry entry;
+		public ReflectionEntry(BeansFieldEntry entry){
+			this.entry = entry;
+		}
+		
+		public void pack(Object value , Packer packer) throws IOException{
+			packer.pack(value);
+		}
+		public void convert(Object target, MessagePackObject obj) throws MessageTypeException, IllegalAccessException {
+			entry.set(target, obj.convert(entry.getType()));
+		}
+		
+		public void unpack(Object target, Unpacker unpacker) throws IOException, MessageTypeException, IllegalAccessException {
+			entry.set(target, unpacker.unpack(entry.getType()));
+		}
+		
+		public void setNull(Object target){
+			entry.set(target, null);
+		}
+		
+		public boolean isRequired(){
+			return entry.isRequired();
+		}
+		public boolean isNullable(){
+			return entry.isNullable();
+		}
+		public boolean isAvailable(){
+			return entry.isAvailable();
+		}
+		public boolean isOptional(){
+			return entry.isOptional();
+		}
+		public Object get(Object target){
+			return entry.get(target);
+		}
+		
+	}
+	
+	static class ObjectFieldEntry extends ReflectionEntry{
+		Template template;
+		public ObjectFieldEntry(BeansFieldEntry entry,Template template){
+			super(entry);
+			this.template = template;
+		}
+		public void pack(Object value , Packer packer) throws IOException{
+			template.pack(packer,value);
+		}
+		public void convert(Object target, MessagePackObject obj) throws MessageTypeException, IllegalAccessException {
+			Class<Object> type = (Class<Object>)entry.getType();
+			Object fieldReference = entry.get(target);
+			Object valueReference = template.convert(obj, fieldReference);
+			if(valueReference != fieldReference) {
+				entry.set(target, valueReference);
+			}
+		}
+		
+		public void unpack(Object target, Unpacker unpacker) throws IOException, MessageTypeException, IllegalAccessException {
+
+			Class<Object> type = (Class<Object>)entry.getType();
+			Object fieldReference = entry.get(target);
+			Object valueReference = template.unpack(unpacker, fieldReference);
+			if(valueReference != fieldReference) {
+				entry.set(target, valueReference);
+			}
+		}
+	}
+	
+	static class BeansReflectionTemplate extends AbstractTemplate{
+		
+		Class<?> targetClass;
+		ReflectionEntry[] entries = null;
+		protected int minimumArrayLength;
+		
+		public BeansReflectionTemplate(
+				Class<?> targetClass,
+				ReflectionEntry[] entries){
+			this.targetClass = targetClass;
+			this.entries = entries;
+			this.minimumArrayLength = 0;
+			for(int i=0; i < entries.length; i++) {
+				ReflectionEntry e = entries[i];
+				if(e.isRequired() || e.isNullable()) {
+					this.minimumArrayLength = i+1;
+				}
+			}
+		}
+		
+
+		@Override
+		public void pack(Packer pk, Object target) throws IOException {
+			
+			pk.packArray(entries.length);
+			for(ReflectionEntry e : entries){
+				if(!e.isAvailable()){
+					pk.packNil();
+					continue;
+				}
+				Object obj = e.get(target);
+				if(obj == null) {
+					if(!e.isNullable() && !e.isOptional()) {
+						throw new MessageTypeException();
+					}
+					pk.packNil();
+				} else {
+					pk.pack(obj);
+				}
+			}
+			
+		}
+		@Override
+		public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException {
+			try {
+				if(to == null) {
+					to = targetClass.newInstance();
+				}
+
+				int length = pac.unpackArray();
+				if(length < minimumArrayLength) {
+					throw new MessageTypeException();
+				}
+
+				int i;
+				for(i=0; i < minimumArrayLength; i++) {
+					ReflectionEntry e = entries[i];
+					if(!e.isAvailable()) {
+						pac.unpackObject();
+						continue;
+					}
+
+					if(pac.tryUnpackNull()) {
+						if(e.isRequired()) {
+							// Required + nil => exception
+							throw new MessageTypeException();
+						} else if(e.isOptional()) {
+							// Optional + nil => keep default value
+						} else {  // Nullable
+							// Nullable + nil => set null
+							e.setNull(to);
+						}
+					} else {
+						e.unpack(to,pac);
+						//e.set(to, pac.unpack(e.getType()));
+					}
+				}
+
+				int max = length < entries.length ? length : entries.length;
+				for(; i < max; i++) {
+					ReflectionEntry e = entries[i];
+					if(!e.isAvailable()) {
+						pac.unpackObject();
+						continue;
+					}
+
+					if(pac.tryUnpackNull()) {
+						// this is Optional field becaue i >= minimumArrayLength
+						// Optional + nil => keep default value
+					} else {
+						e.unpack(to, pac);
+						//e.set(to, pac.unpack(e.getType()));
+					}
+				}
+
+				// latter entries are all Optional + nil => keep default value
+
+				for(; i < length; i++) {
+					pac.unpackObject();
+				}
+
+				return to;
+
+			} catch (MessageTypeException e) {
+				throw e;
+			} catch (IOException e) {
+				throw e;
+			} catch (Exception e) {
+				throw new MessageTypeException(e);
+			}
+		}
+
+		@Override
+		public Object convert(MessagePackObject from, Object to)
+				throws MessageTypeException {
+			try {
+				if(to == null) {
+					to = targetClass.newInstance();
+				}
+
+				MessagePackObject[] array = from.asArray();
+				int length = array.length;
+				if(length < minimumArrayLength) {
+					throw new MessageTypeException();
+				}
+
+				int i;
+				for(i=0; i < minimumArrayLength; i++) {
+					ReflectionEntry e = entries[i];
+					if(!e.isAvailable()) {
+						continue;
+					}
+
+					MessagePackObject obj = array[i];
+					if(obj.isNil()) {
+						if(e.isRequired()) {
+							// Required + nil => exception
+							throw new MessageTypeException();
+						} else if(e.isOptional()) {
+							// Optional + nil => keep default value
+						} else {  // Nullable
+							// Nullable + nil => set null
+							e.setNull(to);
+							//e.set(to,null);
+						}
+					} else {
+						e.convert(to, obj);
+						//e.set(to, from.convert(e.getType()));
+					}
+				}
+
+				int max = length < entries.length ? length : entries.length;
+				for(; i < max; i++) {
+					ReflectionEntry e = entries[i];
+					if(!e.isAvailable()) {
+						continue;
+					}
+
+					MessagePackObject obj = array[i];
+					if(obj.isNil()) {
+						// this is Optional field becaue i >= minimumArrayLength
+						// Optional + nil => keep default value
+					} else {
+						e.convert(to, obj);
+						//e.set(to, obj.convert(e.getType()));
+					}
+				}
+
+				// latter entries are all Optional + nil => keep default value
+
+				return to;
+
+			} catch (MessageTypeException e) {
+				throw e;
+			} catch (Exception e) {
+				throw new MessageTypeException(e);
+			}
+		}
+		
+	}
+
+
+	@Override
+	public Template buildTemplate(Class<?> targetClass, IFieldEntry[] entries) {
+		
+		ReflectionEntry[] refEntries = new ReflectionEntry[entries.length];
+		for(int i = 0;i < entries.length;i++){
+			BeansFieldEntry e = (BeansFieldEntry)entries[i];
+			Class<?> type = e.getType();
+			if(type.equals(boolean.class)) {
+				refEntries[i] = new ReflectionEntry(e);
+			} else if(type.equals(byte.class)) {
+				refEntries[i] = new ReflectionEntry(e);
+			} else if(type.equals(short.class)) {
+				refEntries[i] = new ReflectionEntry(e);
+			} else if(type.equals(int.class)) {
+				refEntries[i] = new ReflectionEntry(e);
+			} else if(type.equals(long.class)) {
+				refEntries[i] = new ReflectionEntry(e);
+			} else if(type.equals(float.class)) {
+				refEntries[i] = new ReflectionEntry(e);
+			} else if(type.equals(double.class)) {
+				refEntries[i] = new ReflectionEntry(e);
+			} else {
+				Template tmpl = TemplateRegistry.lookup(e.getGenericType(), true);
+				refEntries[i] = new ObjectFieldEntry(e, tmpl);
+			}
+		}
+		
+		
+		return new BeansReflectionTemplate(targetClass,refEntries);
+	}
+	
+	
+}
diff --git a/java/src/main/java/org/msgpack/template/FieldEntry.java b/java/src/main/java/org/msgpack/template/FieldEntry.java
new file mode 100644
index 00000000..2c474ec3
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/FieldEntry.java
@@ -0,0 +1,143 @@
+//
+// 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.io.IOException;
+import java.lang.reflect.*;
+import java.lang.annotation.*;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import org.msgpack.*;
+import org.msgpack.annotation.*;
+
+public class FieldEntry implements IFieldEntry {
+	private Field field;
+	private FieldOption option;
+
+	public FieldEntry() {
+		this.field = null;
+		this.option = FieldOption.IGNORE;
+	}
+
+	public FieldEntry(FieldEntry e) {
+		this.field = e.field;
+		this.option = e.option;
+	}
+
+	public FieldEntry(Field field, FieldOption option) {
+		this.field = field;
+		this.option = option;
+	}
+
+	public Field getField() {
+		return field;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.msgpack.template.IFieldEntry#getName()
+	 */
+	@Override
+	public String getName() {
+		return field.getName();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.msgpack.template.IFieldEntry#getType()
+	 */
+	@Override
+	public Class<?> getType() {
+		return field.getType();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.msgpack.template.IFieldEntry#getJavaTypeName()
+	 */
+	@Override
+	public String getJavaTypeName() {
+		Class<?> type = field.getType();
+		if(type.isArray()) {
+			return arrayTypeToString(type);
+		} else {
+			return type.getName();
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.msgpack.template.IFieldEntry#getGenericType()
+	 */
+	@Override
+	public Type getGenericType() {
+		return field.getGenericType();
+	}
+
+	/* (non-Javadoc)
+	 * @see org.msgpack.template.IFieldEntry#getOption()
+	 */
+	@Override
+	public FieldOption getOption() {
+		return option;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.msgpack.template.IFieldEntry#isAvailable()
+	 */
+	@Override
+	public boolean isAvailable() {
+		return option != FieldOption.IGNORE;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.msgpack.template.IFieldEntry#isRequired()
+	 */
+	@Override
+	public boolean isRequired() {
+		return option == FieldOption.REQUIRED;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.msgpack.template.IFieldEntry#isOptional()
+	 */
+	@Override
+	public boolean isOptional() {
+		return option == FieldOption.OPTIONAL;
+	}
+
+	/* (non-Javadoc)
+	 * @see org.msgpack.template.IFieldEntry#isNullable()
+	 */
+	@Override
+	public boolean isNullable() {
+		return option == FieldOption.NULLABLE;
+	}
+
+	static String arrayTypeToString(Class<?> type) {
+		int dim = 1;
+		Class<?> baseType = type.getComponentType();
+		while(baseType.isArray()) {
+			baseType = baseType.getComponentType();
+			dim += 1;
+		}
+		StringBuilder sb = new StringBuilder();
+		sb.append(baseType.getName());
+		for (int i = 0; i < dim; ++i) {
+			sb.append("[]");
+		}
+		return sb.toString();
+	}
+}
\ No newline at end of file
diff --git a/java/src/main/java/org/msgpack/template/FieldEntryReader.java b/java/src/main/java/org/msgpack/template/FieldEntryReader.java
new file mode 100644
index 00000000..9d477465
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/FieldEntryReader.java
@@ -0,0 +1,165 @@
+package org.msgpack.template;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.msgpack.annotation.Ignore;
+import org.msgpack.annotation.Index;
+import org.msgpack.annotation.MessagePackMessage;
+import org.msgpack.annotation.Nullable;
+import org.msgpack.annotation.Optional;
+import org.msgpack.annotation.Required;
+
+public class FieldEntryReader implements IFieldEntryReader{
+
+
+	public IFieldEntry[] convertFieldEntries(Class<?> targetClass, FieldList flist) throws NoSuchFieldException {
+		List<FieldList.Entry> src = flist.getList();
+		FieldEntry[] result = new FieldEntry[src.size()];
+		for(int i=0; i < src.size(); i++) {
+			FieldList.Entry s = src.get(i);
+			if(s.isAvailable()) {
+				result[i] = new FieldEntry(targetClass.getDeclaredField(s.getName()), s.getOption());
+			} else {
+				result[i] = new FieldEntry();
+			}
+		}
+		return result;
+	}
+	
+	@Override
+	public IFieldEntry[] readFieldEntries(Class<?> targetClass,
+			FieldOption implicitOption) {
+		Field[] allFields = readAllFields(targetClass);
+
+		/* index:
+		 *   @Index(0) int field_a;   // 0
+		 *             int field_b;   // 1
+		 *   @Index(3) int field_c;   // 3
+		 *             int field_d;   // 4
+		 *   @Index(2) int field_e;   // 2
+		 *             int field_f;   // 5
+		 */
+		List<FieldEntry> indexed = new ArrayList<FieldEntry>();
+		int maxIndex = -1;
+		for(Field f : allFields) {
+			FieldOption opt = readFieldOption(f, implicitOption);
+			if(opt == FieldOption.IGNORE) {
+				// skip
+				continue;
+			}
+
+			int index = readFieldIndex(f, maxIndex);
+
+			if(indexed.size() > index && indexed.get(index) != null) {
+				throw new TemplateBuildException("duplicated index: "+index);
+			}
+			if(index < 0) {
+				throw new TemplateBuildException("invalid index: "+index);
+			}
+
+			while(indexed.size() <= index) {
+				indexed.add(null);
+			}
+			indexed.set(index, new FieldEntry(f, opt));
+
+			if(maxIndex < index) {
+				maxIndex = index;
+			}
+		}
+
+		FieldEntry[] result = new FieldEntry[maxIndex+1];
+		for(int i=0; i < indexed.size(); i++) {
+			FieldEntry e = indexed.get(i);
+			if(e == null) {
+				result[i] = new FieldEntry();
+			} else {
+				result[i] = e;
+			}
+		}
+
+		return result;
+	}
+
+	public FieldOption readImplicitFieldOption(Class<?> targetClass) {
+		MessagePackMessage a = targetClass.getAnnotation(MessagePackMessage.class);
+		if(a == null) {
+			return FieldOption.DEFAULT;
+		}
+		return a.value();
+	}
+	
+	private Field[] readAllFields(Class<?> targetClass) {
+		// order: [fields of super class, ..., fields of this class]
+		List<Field[]> succ = new ArrayList<Field[]>();
+		int total = 0;
+		for(Class<?> c = targetClass; c != Object.class; c = c.getSuperclass()) {
+			Field[] fields = c.getDeclaredFields();
+			total += fields.length;
+			succ.add(fields);
+		}
+		Field[] result = new Field[total];
+		int off = 0;
+		for(int i=succ.size()-1; i >= 0; i--) {
+			Field[] fields = succ.get(i);
+			System.arraycopy(fields, 0, result, off, fields.length);
+			off += fields.length;
+		}
+		return result;
+	}
+
+	private static FieldOption readFieldOption(Field field, FieldOption implicitOption) {
+		int mod = field.getModifiers();
+		if(Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
+			return FieldOption.IGNORE;
+		}
+
+		if(isAnnotated(field, Ignore.class)) {
+			return FieldOption.IGNORE;
+		} else if(isAnnotated(field, Required.class)) {
+			return FieldOption.REQUIRED;
+		} else if(isAnnotated(field, Optional.class)) {
+			return FieldOption.OPTIONAL;
+		} else if(isAnnotated(field, Nullable.class)) {
+			if(field.getDeclaringClass().isPrimitive()) {
+				return FieldOption.REQUIRED;
+			} else {
+				return FieldOption.NULLABLE;
+			}
+		}
+
+		if(implicitOption != FieldOption.DEFAULT) {
+			return implicitOption;
+		}
+
+		// default mode:
+		//   transient : Ignore
+		//   public    : Required
+		//   others    : Ignore
+		if(Modifier.isTransient(mod)) {
+			return FieldOption.IGNORE;
+		} else if(Modifier.isPublic(mod)) {
+			return FieldOption.REQUIRED;
+		} else {
+			return FieldOption.IGNORE;
+		}
+	}
+
+	private static int readFieldIndex(Field field, int maxIndex) {
+		Index a = field.getAnnotation(Index.class);
+		if(a == null) {
+			return maxIndex + 1;
+		} else {
+			return a.value();
+		}
+	}
+
+	private static boolean isAnnotated(AccessibleObject ao, Class<? extends Annotation> with) {
+		return ao.getAnnotation(with) != null;
+	}
+	
+}
diff --git a/java/src/main/java/org/msgpack/template/FieldList.java b/java/src/main/java/org/msgpack/template/FieldList.java
index daf59f5b..a5b141d9 100644
--- a/java/src/main/java/org/msgpack/template/FieldList.java
+++ b/java/src/main/java/org/msgpack/template/FieldList.java
@@ -43,19 +43,19 @@ public class FieldList {
 			return option;
 		}
 
-		boolean isAvailable() {
+		public boolean isAvailable() {
 			return this.option != FieldOption.IGNORE;
 		}
 
-		boolean isRequired() {
+		public boolean isRequired() {
 			return this.option == FieldOption.REQUIRED;
 		}
 
-		boolean isOptional() {
+		public boolean isOptional() {
 			return this.option == FieldOption.OPTIONAL;
 		}
 
-		boolean isNullable() {
+		public boolean isNullable() {
 			return this.option == FieldOption.NULLABLE;
 		}
 	}
@@ -89,7 +89,7 @@ public class FieldList {
 		}
 	}
 
-	List<Entry> getList() {
+	public List<Entry> getList() {
 		return list;
 	}
 }
diff --git a/java/src/main/java/org/msgpack/template/IFieldEntry.java b/java/src/main/java/org/msgpack/template/IFieldEntry.java
new file mode 100644
index 00000000..d12f7a8d
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/IFieldEntry.java
@@ -0,0 +1,25 @@
+package org.msgpack.template;
+
+import java.lang.reflect.Type;
+
+public interface IFieldEntry {
+
+	public abstract String getName();
+
+	public abstract Class<?> getType();
+
+	public abstract String getJavaTypeName();
+
+	public abstract Type getGenericType();
+
+	public abstract FieldOption getOption();
+
+	public abstract boolean isAvailable();
+
+	public abstract boolean isRequired();
+
+	public abstract boolean isOptional();
+
+	public abstract boolean isNullable();
+
+}
\ No newline at end of file
diff --git a/java/src/main/java/org/msgpack/template/IFieldEntryReader.java b/java/src/main/java/org/msgpack/template/IFieldEntryReader.java
new file mode 100644
index 00000000..07a8f877
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/IFieldEntryReader.java
@@ -0,0 +1,8 @@
+package org.msgpack.template;
+
+public interface IFieldEntryReader {
+	
+	public IFieldEntry[] convertFieldEntries(Class<?> targetClass, FieldList flist) throws NoSuchFieldException;
+	public IFieldEntry[] readFieldEntries(Class<?> targetClass, FieldOption implicitOption);
+	public FieldOption readImplicitFieldOption(Class<?> targetClass) ;
+}
diff --git a/java/src/main/java/org/msgpack/template/JavassistTemplateBuilder.java b/java/src/main/java/org/msgpack/template/JavassistTemplateBuilder.java
index a4d2e4db..03d7f15d 100644
--- a/java/src/main/java/org/msgpack/template/JavassistTemplateBuilder.java
+++ b/java/src/main/java/org/msgpack/template/JavassistTemplateBuilder.java
@@ -22,6 +22,7 @@ import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Type;
+import java.lang.Thread;
 
 import org.msgpack.*;
 
@@ -34,11 +35,14 @@ import javassist.CtNewConstructor;
 import javassist.CtNewMethod;
 import javassist.LoaderClassPath;
 import javassist.NotFoundException;
+import javassist.ClassClassPath;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.msgpack.template.builder.CustomTemplateBuilder;
+import org.msgpack.template.javassist.*;
 
-public class JavassistTemplateBuilder extends TemplateBuilder {
+public class JavassistTemplateBuilder extends CustomTemplateBuilder {
 	private static Logger LOG = LoggerFactory.getLogger(JavassistTemplateBuilder.class);
 
 	private static JavassistTemplateBuilder instance;
@@ -54,12 +58,33 @@ public class JavassistTemplateBuilder extends TemplateBuilder {
 		getInstance().pool.appendClassPath(new LoaderClassPath(cl));
 	}
 
-	private JavassistTemplateBuilder() {
+	
+	IFieldEntryReader reader = new FieldEntryReader();
+	
+	public void setFieldEntryReader(IFieldEntryReader reader){
+		this.reader = reader;
+	}
+	
+	BuildContextFactory buildContextFactory = new BuildContextFactory() {
+		
+		@Override
+		public BuildContextBase createBuildContext(JavassistTemplateBuilder builder) {
+			
+			return new BuildContext(builder);
+		}
+	};
+	public void setBuildContextFactory(BuildContextFactory factory){
+		this.buildContextFactory = factory;
+	}
+	
+	
+	
+	public JavassistTemplateBuilder() {
 		pool = new ClassPool();
 		boolean appended = false;
 		ClassLoader cl = null;
 		try {
-			Thread.currentThread().getContextClassLoader();
+			cl = Thread.currentThread().getContextClassLoader();
 			if (cl != null) {
 				pool.appendClassPath(new LoaderClassPath(cl));
 				appended = true;
@@ -80,471 +105,40 @@ public class JavassistTemplateBuilder extends TemplateBuilder {
 			pool.appendSystemPath();
 		}
 	}
+	/**
+	 * Replace FieldEntryReader and BuilderContextFactory.
+	 * you can replace field entry rules and generated codes easily.
+	 * @param reader
+	 * @param buildContextFactory
+	 */
+	public JavassistTemplateBuilder(IFieldEntryReader reader,BuildContextFactory buildContextFactory ){
+		this();
+		this.reader = reader;
+		this.buildContextFactory = buildContextFactory;
+	}
+
 
 	protected ClassPool pool;
 
 	private int seqId = 0;
 
-	CtClass makeCtClass(String className) {
+	public CtClass makeCtClass(String className) {
 		return pool.makeClass(className);
 	}
 
-	CtClass getCtClass(String className) throws NotFoundException {
+	public CtClass getCtClass(String className) throws NotFoundException {
 		return pool.get(className);
 	}
 
-	int nextSeqId() {
+	public int nextSeqId() {
 		return seqId++;
 	}
 
-	private static abstract class BuildContextBase {
-		protected JavassistTemplateBuilder director;
 
-		protected String tmplName;
 
-		protected CtClass tmplCtClass;
 
-		protected abstract void setSuperClass() throws CannotCompileException, NotFoundException;
-
-		protected abstract void buildConstructor() throws CannotCompileException, NotFoundException;
-
-		protected void buildMethodInit() { }
-
-		protected abstract String buildPackMethodBody();
-
-		protected abstract String buildUnpackMethodBody();
-
-		protected abstract String buildConvertMethodBody();
-
-		protected abstract Template buildInstance(Class<?> c) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException;
-
-		public BuildContextBase(JavassistTemplateBuilder director) {
-			this.director = director;
-		}
-
-		protected Template build(final String className) {
-			try {
-				reset(className);
-				buildClass();
-				buildConstructor();
-				buildMethodInit();
-				buildPackMethod();
-				buildUnpackMethod();
-				buildConvertMethod();
-				return buildInstance(createClass());
-			} catch (Exception e) {
-				String code = getBuiltString();
-				if(code != null) {
-					LOG.error("builder: " + code, e);
-					throw new TemplateBuildException("cannot compile: " + code, e);
-				} else {
-					throw new TemplateBuildException(e);
-				}
-			}
-		}
-
-		protected void reset(String className) {
-			tmplName = className + "_$$_Template" + director.nextSeqId();
-			tmplCtClass = director.makeCtClass(tmplName);
-		}
-
-		protected void buildClass() throws CannotCompileException, NotFoundException {
-			setSuperClass();
-			tmplCtClass.addInterface(director.getCtClass(Template.class.getName()));
-		}
-
-		protected void buildPackMethod() throws CannotCompileException, NotFoundException {
-			String mbody = buildPackMethodBody();
-			int mod = javassist.Modifier.PUBLIC;
-			CtClass returnType = CtClass.voidType;
-			String mname = "pack";
-			CtClass[] paramTypes = new CtClass[] {
-					director.getCtClass(Packer.class.getName()),
-					director.getCtClass(Object.class.getName())
-			};
-			CtClass[] exceptTypes = new CtClass[] {
-					director.getCtClass(IOException.class.getName())
-			};
-			CtMethod newCtMethod = CtNewMethod.make(
-					mod, returnType, mname,
-					paramTypes, exceptTypes, mbody, tmplCtClass);
-			tmplCtClass.addMethod(newCtMethod);
-		}
-
-		protected void buildUnpackMethod() throws CannotCompileException, NotFoundException {
-			String mbody = buildUnpackMethodBody();
-			int mod = javassist.Modifier.PUBLIC;
-			CtClass returnType = director.getCtClass(Object.class.getName());
-			String mname = "unpack";
-			CtClass[] paramTypes = new CtClass[] {
-					director.getCtClass(Unpacker.class.getName()),
-					director.getCtClass(Object.class.getName())
-			};
-			CtClass[] exceptTypes = new CtClass[] {
-					director.getCtClass(MessageTypeException.class.getName())
-			};
-			CtMethod newCtMethod = CtNewMethod.make(
-					mod, returnType, mname,
-					paramTypes, exceptTypes, mbody, tmplCtClass);
-			tmplCtClass.addMethod(newCtMethod);
-		}
-
-		protected void buildConvertMethod() throws CannotCompileException, NotFoundException {
-			String mbody = buildConvertMethodBody();
-			int mod = javassist.Modifier.PUBLIC;
-			CtClass returnType = director.getCtClass(Object.class.getName());
-			String mname = "convert";
-			CtClass[] paramTypes = new CtClass[] {
-					director.getCtClass(MessagePackObject.class.getName()),
-					director.getCtClass(Object.class.getName())
-			};
-			CtClass[] exceptTypes = new CtClass[] {
-					director.getCtClass(MessageTypeException.class.getName())
-			};
-			CtMethod newCtMethod = CtNewMethod.make(
-					mod, returnType, mname,
-					paramTypes, exceptTypes, mbody, tmplCtClass);
-			tmplCtClass.addMethod(newCtMethod);
-		}
-
-		protected Class<?> createClass() throws CannotCompileException {
-			return (Class<?>) tmplCtClass.toClass(null, null);
-		}
-
-		protected StringBuilder stringBuilder = null;
-
-		protected void resetStringBuilder() {
-			stringBuilder = new StringBuilder();
-		}
-
-		protected void buildString(String str) {
-			stringBuilder.append(str);
-		}
-
-		protected void buildString(String format, Object... args) {
-			stringBuilder.append(String.format(format, args));
-		}
-
-		protected String getBuiltString() {
-			if(stringBuilder == null) {
-				return null;
-			}
-			return stringBuilder.toString();
-		}
-	}
-
-	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;
-
-		public BuildContext(JavassistTemplateBuilder director) {
-			super(director);
-		}
-
-		public Template buildTemplate(Class<?> targetClass, FieldEntry[] entries, Template[] templates) {
-			this.entries = entries;
-			this.templates = templates;
-			this.origClass = targetClass;
-			this.origName = this.origClass.getName();
-			return build(this.origName);
-		}
-
-		protected void setSuperClass() throws CannotCompileException, NotFoundException {
-			this.tmplCtClass.setSuperclass(
-					director.getCtClass(JavassistTemplate.class.getName()));
-		}
-
-		protected void buildConstructor() throws CannotCompileException, NotFoundException {
-			// Constructor(Class targetClass, Template[] templates)
-			CtConstructor newCtCons = CtNewConstructor.make(
-				new CtClass[] {
-					director.getCtClass(Class.class.getName()),
-					director.getCtClass(Template.class.getName()+"[]")
-				},
-				new CtClass[0],
-				this.tmplCtClass);
-			this.tmplCtClass.addConstructor(newCtCons);
-		}
-
-		protected Template buildInstance(Class<?> c) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
-			Constructor<?> cons = c.getConstructor(new Class[] {
-					Class.class,
-					Template[].class
-				});
-			Object tmpl = cons.newInstance(new Object[] {
-					this.origClass,
-					this.templates
-				});
-			return (Template)tmpl;
-		}
-
-		protected void buildMethodInit() {
-			this.minimumArrayLength = 0;
-			for(int i=0; i < entries.length; i++) {
-				FieldEntry e = entries[i];
-				if(e.isRequired() || e.isNullable()) {
-					this.minimumArrayLength = i+1;
-				}
-			}
-		}
-
-		protected String buildPackMethodBody() {
-			resetStringBuilder();
-			buildString("{");
-			buildString("%s _$$_t = (%s)$2;", this.origName, this.origName);
-			buildString("$1.packArray(%d);", entries.length);
-			for(int i=0; i < entries.length; i++) {
-				FieldEntry e = entries[i];
-				if(!e.isAvailable()) {
-					buildString("$1.packNil();");
-					continue;
-				}
-				Class<?> type = e.getType();
-				if(type.isPrimitive()) {
-					buildString("$1.%s(_$$_t.%s);", primitivePackName(type), e.getName());
-				} else {
-					buildString("if(_$$_t.%s == null) {", e.getName());
-					if(!e.isNullable() && !e.isOptional()) {
-						buildString("throw new %s();", MessageTypeException.class.getName());
-					} else {
-						buildString("$1.packNil();");
-					}
-					buildString("} else {");
-					buildString("  this.templates[%d].pack($1, _$$_t.%s);", i, e.getName());
-					buildString("}");
-				}
-			}
-			buildString("}");
-			return getBuiltString();
-		}
-
-		protected String buildUnpackMethodBody() {
-			resetStringBuilder();
-			buildString("{ ");
-
-			buildString("%s _$$_t;", this.origName);
-			buildString("if($2 == null) {");
-			buildString("  _$$_t = new %s();", this.origName);
-			buildString("} else {");
-			buildString("  _$$_t = (%s)$2;", this.origName);
-			buildString("}");
-
-			buildString("int length = $1.unpackArray();");
-			buildString("if(length < %d) {", this.minimumArrayLength);
-			buildString("  throw new %s();", MessageTypeException.class.getName());
-			buildString("}");
-
-			int i;
-			for(i=0; i < this.minimumArrayLength; i++) {
-				FieldEntry e = entries[i];
-				if(!e.isAvailable()) {
-					buildString("$1.unpackObject();");
-					continue;
-				}
-
-				buildString("if($1.tryUnpackNull()) {");
-					if(e.isRequired()) {
-						// Required + nil => exception
-						buildString("throw new %s();", MessageTypeException.class.getName());
-					} else if(e.isOptional()) {
-						// Optional + nil => keep default value
-					} else {  // Nullable
-						// Nullable + nil => set null
-						buildString("_$$_t.%s = null;", e.getName());
-					}
-				buildString("} else {");
-					Class<?> type = e.getType();
-					if(type.isPrimitive()) {
-						buildString("_$$_t.%s = $1.%s();", e.getName(), primitiveUnpackName(type));
-					} else {
-						buildString("_$$_t.%s = (%s)this.templates[%d].unpack($1, _$$_t.%s);", e.getName(), e.getJavaTypeName(), i, e.getName());
-					}
-				buildString("}");
-			}
-
-			for(; i < entries.length; i++) {
-				buildString("if(length <= %d) { return _$$_t; }", i);
-
-				FieldEntry e = entries[i];
-				if(!e.isAvailable()) {
-					buildString("$1.unpackObject();");
-					continue;
-				}
-
-				buildString("if($1.tryUnpackNull()) {");
-					// this is Optional field becaue i >= minimumArrayLength
-					// Optional + nil => keep default value
-				buildString("} else {");
-					Class<?> type = e.getType();
-					if(type.isPrimitive()) {
-						buildString("_$$_t.%s = $1.%s();", e.getName(), primitiveUnpackName(type));
-					} else {
-						buildString("_$$_t.%s = (%s)this.templates[%d].unpack($1, _$$_t.%s);", e.getName(), e.getJavaTypeName(), i, e.getName());
-					}
-				buildString("}");
-			}
-
-			// latter entries are all Optional + nil => keep default value
-
-			buildString("for(int i=%d; i < length; i++) {", i);
-			buildString("  $1.unpackObject();");
-			buildString("}");
-
-			buildString("return _$$_t;");
-
-			buildString("}");
-			return getBuiltString();
-		}
-
-		protected String buildConvertMethodBody() {
-			resetStringBuilder();
-			buildString("{ ");
-
-			buildString("%s _$$_t;", this.origName);
-			buildString("if($2 == null) {");
-			buildString("  _$$_t = new %s();", this.origName);
-			buildString("} else {");
-			buildString("  _$$_t = (%s)$2;", this.origName);
-			buildString("}");
-
-			buildString("%s[] array = $1.asArray();", MessagePackObject.class.getName());
-			buildString("int length = array.length;");
-			buildString("if(length < %d) {", this.minimumArrayLength);
-			buildString("  throw new %s();", MessageTypeException.class.getName());
-			buildString("}");
-
-			buildString("%s obj;", MessagePackObject.class.getName());
-
-			int i;
-			for(i=0; i < this.minimumArrayLength; i++) {
-				FieldEntry e = entries[i];
-				if(!e.isAvailable()) {
-					continue;
-				}
-
-				buildString("obj = array[%d];", i);
-				buildString("if(obj.isNil()) {");
-					if(e.isRequired()) {
-						// Required + nil => exception
-						buildString("throw new %s();", MessageTypeException.class.getName());
-					} else if(e.isOptional()) {
-						// Optional + nil => keep default value
-					} else {  // Nullable
-						// Nullable + nil => set null
-						buildString("_$$_t.%s = null;", e.getName());
-					}
-				buildString("} else {");
-					Class<?> type = e.getType();
-					if(type.isPrimitive()) {
-						buildString("_$$_t.%s = obj.%s();", e.getName(), primitiveConvertName(type));
-					} else {
-						buildString("_$$_t.%s = (%s)this.templates[%d].convert(obj, _$$_t.%s);", e.getName(), e.getJavaTypeName(), i, e.getName());
-					}
-				buildString("}");
-			}
-
-			for(; i < entries.length; i++) {
-				buildString("if(length <= %d) { return _$$_t; }", i);
-
-				FieldEntry e = entries[i];
-				if(!e.isAvailable()) {
-					continue;
-				}
-
-				buildString("obj = array[%d];", i);
-				buildString("if(obj.isNil()) {");
-					// this is Optional field becaue i >= minimumArrayLength
-					// Optional + nil => keep default value
-				buildString("} else {");
-					Class<?> type = e.getType();
-					if(type.isPrimitive()) {
-						buildString("_$$_t.%s = obj.%s();", e.getName(), primitiveConvertName(type));
-					} else {
-						buildString("_$$_t.%s = (%s)this.templates[%d].convert(obj, _$$_t.%s);", e.getName(), e.getJavaTypeName(), i, e.getName());
-					}
-				buildString("}");
-			}
-
-			// latter entries are all Optional + nil => keep default value
-
-			buildString("return _$$_t;");
-
-			buildString("}");
-			return getBuiltString();
-		}
-
-		protected String primitivePackName(Class<?> type) {
-			if(type == boolean.class) {
-				return "packBoolean";
-			} else if(type == byte.class) {
-				return "packByte";
-			} else if(type == short.class) {
-				return "packShort";
-			} else if(type == int.class) {
-				return "packInt";
-			} else if(type == long.class) {
-				return "packLong";
-			} else if(type == float.class) {
-				return "packFloat";
-			} else if(type == double.class) {
-				return "packDouble";
-			}
-			return null;
-		}
-
-		protected String primitiveUnpackName(Class<?> type) {
-			if(type == boolean.class) {
-				return "unpackBoolean";
-			} else if(type == byte.class) {
-				return "unpackByte";
-			} else if(type == short.class) {
-				return "unpackShort";
-			} else if(type == int.class) {
-				return "unpackInt";
-			} else if(type == long.class) {
-				return "unpackLong";
-			} else if(type == float.class) {
-				return "unpackFloat";
-			} else if(type == double.class) {
-				return "unpackDouble";
-			}
-			return null;
-		}
-
-		protected String primitiveConvertName(Class<?> type) {
-			if(type == boolean.class) {
-				return "asBoolean";
-			} else if(type == byte.class) {
-				return "asByte";
-			} else if(type == short.class) {
-				return "asShort";
-			} else if(type == int.class) {
-				return "asInt";
-			} else if(type == long.class) {
-				return "asLong";
-			} else if(type == float.class) {
-				return "asFloat";
-			} else if(type == double.class) {
-				return "asDouble";
-			}
-			return null;
-		}
-	}
-
-	public Template buildTemplate(Class<?> targetClass, FieldEntry[] entries) {
+	@Override
+	public Template buildTemplate(Class<?> targetClass, IFieldEntry[] entries) {
 		// FIXME private / packagefields
 		//for(FieldEntry e : entries) {
 		//	Field f = e.getField();
@@ -556,7 +150,7 @@ public class JavassistTemplateBuilder extends TemplateBuilder {
 
 		Template[] tmpls = new Template[entries.length];
 		for(int i=0; i < entries.length; i++) {
-			FieldEntry e = entries[i];
+			IFieldEntry e = entries[i];
 			if(!e.isAvailable()) {
 				tmpls[i] = null;
 			} else {
@@ -565,10 +159,21 @@ public class JavassistTemplateBuilder extends TemplateBuilder {
 			}
 		}
 
-		BuildContext bc = new BuildContext(this);
+		BuildContextBase bc = getBuildContextFacotry().createBuildContext(this);
 		return bc.buildTemplate(targetClass, entries, tmpls);
 	}
 
+	@Override
+	public IFieldEntryReader getFieldEntryReader() {
+		return reader;
+	}
+
+	public BuildContextFactory getBuildContextFacotry() {
+		return buildContextFactory;
+	}
+
+	
+    /*
 	static class JavassistOrdinalEnumTemplate extends ReflectionTemplateBuilder.ReflectionOrdinalEnumTemplate {
 		JavassistOrdinalEnumTemplate(Enum<?>[] entries) {
 			super(entries);
@@ -610,6 +215,6 @@ public class JavassistTemplateBuilder extends TemplateBuilder {
 			Class<?> componentClass = Array.newInstance(componentTemplate.getComponentClass(), 0).getClass();
 			return new ReflectionTemplateBuilder.ReflectionMultidimentionalArrayTemplate(componentClass, componentTemplate);
 		}
-	}
+	}*/
 }
 
diff --git a/java/src/main/java/org/msgpack/template/ReflectionTemplateBuilder.java b/java/src/main/java/org/msgpack/template/ReflectionTemplateBuilder.java
index 03ff2067..c85b69f9 100644
--- a/java/src/main/java/org/msgpack/template/ReflectionTemplateBuilder.java
+++ b/java/src/main/java/org/msgpack/template/ReflectionTemplateBuilder.java
@@ -22,17 +22,26 @@ import java.lang.reflect.*;
 import java.util.Map;
 import java.util.HashMap;
 import org.msgpack.*;
+import org.msgpack.template.builder.CustomTemplateBuilder;
+import org.msgpack.template.builder.TemplateBuilder;
 
-public class ReflectionTemplateBuilder extends TemplateBuilder {
-	private static ReflectionTemplateBuilder instance;
+public class ReflectionTemplateBuilder extends CustomTemplateBuilder {
+	/*private static ReflectionTemplateBuilder instance;
 	public synchronized static ReflectionTemplateBuilder getInstance() {
 		if(instance == null) {
 			instance = new ReflectionTemplateBuilder();
 		}
 		return instance;
+	}*/
+
+	IFieldEntryReader reader = new FieldEntryReader();
+	
+	@Override
+	public IFieldEntryReader getFieldEntryReader(){
+		return reader;
 	}
 
-	private ReflectionTemplateBuilder() {
+	public ReflectionTemplateBuilder() {
 	}
 
 	static abstract class ReflectionFieldEntry extends FieldEntry {
@@ -373,9 +382,10 @@ public class ReflectionTemplateBuilder extends TemplateBuilder {
 		}
 	}
 
-	public Template buildTemplate(Class<?> targetClass, FieldEntry[] entries) {
-		for(FieldEntry e : entries) {
-			Field f = e.getField();
+	public Template buildTemplate(Class<?> targetClass, IFieldEntry[] entries) {
+		// TODO Now it is simply cast.
+		for(IFieldEntry e : entries) {
+			Field f = ((FieldEntry)e).getField();
 			int mod = f.getModifiers();
 			if(!Modifier.isPublic(mod)) {
 				f.setAccessible(true);
@@ -384,7 +394,7 @@ public class ReflectionTemplateBuilder extends TemplateBuilder {
 
 		ReflectionFieldEntry[] res = new ReflectionFieldEntry[entries.length];
 		for(int i=0; i < entries.length; i++) {
-			FieldEntry e = entries[i];
+			FieldEntry e = (FieldEntry)entries[i];
 			Class<?> type = e.getType();
 			if(!e.isAvailable()) {
 				res[i] = new NullFieldEntry(e);
@@ -411,159 +421,5 @@ public class ReflectionTemplateBuilder extends TemplateBuilder {
 		return new ReflectionTemplate(targetClass, res);
 	}
 
-	static class ReflectionOrdinalEnumTemplate extends AbstractTemplate {
-		protected Enum<?>[] entries;
-		protected Map<Enum<?>, Integer> reverse;
-
-		ReflectionOrdinalEnumTemplate(Enum<?>[] entries) {
-			this.entries = entries;
-			this.reverse = new HashMap<Enum<?>, Integer>();
-			for(int i=0; i < entries.length; i++) {
-				this.reverse.put(entries[i], i);
-			}
-		}
-
-		public void pack(Packer pk, Object target) throws IOException {
-			Integer ord = reverse.get(target);
-			if(ord == null) {
-				throw new MessageTypeException();
-			}
-			pk.pack((int)ord);
-		}
-
-		public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException {
-			int ord = pac.unpackInt();
-			if(entries.length <= ord) {
-				throw new MessageTypeException();
-			}
-			return entries[ord];
-		}
-
-		public Object convert(MessagePackObject from, Object to) throws MessageTypeException {
-			int ord = from.asInt();
-			if(entries.length <= ord) {
-				throw new MessageTypeException();
-			}
-			return entries[ord];
-		}
-	}
-
-	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/TemplateRegistry.java b/java/src/main/java/org/msgpack/template/TemplateRegistry.java
index 3f98fb46..9c447cc2 100644
--- a/java/src/main/java/org/msgpack/template/TemplateRegistry.java
+++ b/java/src/main/java/org/msgpack/template/TemplateRegistry.java
@@ -26,33 +26,55 @@ import java.lang.annotation.Annotation;
 import org.msgpack.annotation.MessagePackMessage;
 import org.msgpack.annotation.MessagePackDelegate;
 import org.msgpack.annotation.MessagePackOrdinalEnum;
+import org.msgpack.template.builder.BuilderSelectorRegistry;
+import org.msgpack.template.builder.CustomTemplateBuilder;
+import org.msgpack.template.builder.TemplateBuilder;
 import org.msgpack.Template;
 import org.msgpack.Templates;
 
 public class TemplateRegistry {
 	private static Map<Type, Template> map;
 	private static Map<Type, GenericTemplate> genericMap;
+	
+	private static BuilderSelectorRegistry builderSelectorRegistry;
 
 	static {
 		map = new HashMap<Type, Template>();
 		genericMap = new HashMap<Type, GenericTemplate>();
 		BuiltInTemplateLoader.load();
+		builderSelectorRegistry = BuilderSelectorRegistry.getInstance();
 	}
 
 	public static void register(Class<?> target) { // auto-detect
-		if(target.isEnum()) {
+		TemplateBuilder builder = builderSelectorRegistry.select(target);
+		if(builder != null){
+			register(target,builder.buildTemplate(target));
+		}else{
+			register(target,builderSelectorRegistry.getForceBuilder().buildTemplate(target));
+		}
+		/*if(target.isEnum()) {
 			register(target, TemplateBuilder.buildOrdinalEnum(target));
 		} else {
 			register(target, TemplateBuilder.build(target));
-		}
+		}*/
 	}
 
 	public static void register(Class<?> target, FieldOption implicitOption) {
-		register(target, TemplateBuilder.build(target, implicitOption));
+		TemplateBuilder builder = builderSelectorRegistry.select(target);
+		if(builder != null && builder instanceof CustomTemplateBuilder){
+			register(target, ((CustomTemplateBuilder)builder).buildTemplate(target, implicitOption));
+		}else{
+			throw new TemplateBuildException("cannot build template with filed option");
+		}
 	}
 
 	public static void register(Class<?> target, FieldList flist) throws NoSuchFieldException {
-		register(target, TemplateBuilder.build(target, flist));
+		TemplateBuilder builder = builderSelectorRegistry.select(target);
+		if(builder != null && builder instanceof CustomTemplateBuilder){
+			register(target, ((CustomTemplateBuilder)builder).buildTemplate(target, flist));
+		}else{
+			throw new TemplateBuildException("cannot build template with filed list");
+		}
 	}
 
 	public static synchronized void register(Type rawType, Template tmpl) {
@@ -102,7 +124,7 @@ public class TemplateRegistry {
 			return tmpl;
 		}
 
-		if(targetType instanceof GenericArrayType) {
+		/*if(targetType instanceof GenericArrayType) {
 			// GenericArrayType is not a Class<?>
 			tmpl = TemplateBuilder.buildArray(targetType);
 			register(targetType, tmpl);
@@ -129,7 +151,17 @@ public class TemplateRegistry {
 			tmpl = TemplateBuilder.buildOrdinalEnum(target);
 			register(target, tmpl);
 			return tmpl;
+		}*/
+		// find match TemplateBuilder
+		TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(targetType);
+		if(builder != null){
+			tmpl = builder.buildTemplate(targetType);
+			register(targetType,tmpl);
+			return tmpl;
 		}
+		
+		
+		Class<?> target = (Class<?>)targetType;
 
 		for(Class<?> i : target.getInterfaces()) {
 			tmpl = map.get(i);
@@ -150,7 +182,7 @@ public class TemplateRegistry {
 			}
 
 			if(forceBuild) {
-				tmpl = TemplateBuilder.build(target);
+				tmpl = builderSelectorRegistry.getForceBuilder().buildTemplate(target);
 				register(target, tmpl);
 				return tmpl;
 			}
@@ -198,8 +230,5 @@ public class TemplateRegistry {
 		return ao.getAnnotation(with) != null;
 	}
 
-	public static void setTemplateBuilder(TemplateBuilder builder) {
-		TemplateBuilder.setInstance(builder);
-	}
 }
 
diff --git a/java/src/main/java/org/msgpack/template/builder/ArrayTemplateBuilder.java b/java/src/main/java/org/msgpack/template/builder/ArrayTemplateBuilder.java
new file mode 100644
index 00000000..93a8aba8
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/builder/ArrayTemplateBuilder.java
@@ -0,0 +1,173 @@
+package org.msgpack.template.builder;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.msgpack.AbstractTemplate;
+import org.msgpack.MessagePackObject;
+import org.msgpack.MessageTypeException;
+import org.msgpack.Packer;
+import org.msgpack.Template;
+import org.msgpack.Unpacker;
+import org.msgpack.template.BooleanArrayTemplate;
+import org.msgpack.template.DoubleArrayTemplate;
+import org.msgpack.template.FloatArrayTemplate;
+import org.msgpack.template.IFieldEntry;
+import org.msgpack.template.IFieldEntryReader;
+import org.msgpack.template.IntArrayTemplate;
+import org.msgpack.template.LongArrayTemplate;
+import org.msgpack.template.ShortArrayTemplate;
+import org.msgpack.template.TemplateRegistry;
+
+public class ArrayTemplateBuilder extends TemplateBuilder {
+
+
+
+
+	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;
+		}
+	}
+	@Override
+	public Template buildTemplate(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;
+			}
+			if(baseType instanceof ParameterizedType) {
+				baseClass = (Class<?>)((ParameterizedType)baseType).getRawType();
+			} else {
+				baseClass = (Class<?>)baseType;
+			}
+		} else {
+			Class<?> type = (Class<?>)arrayType;
+			baseClass = type.getComponentType();
+			while(baseClass.isArray()) {
+				baseClass = baseClass.getComponentType();
+				dim += 1;
+			}
+			baseType = baseClass;
+		}
+		return toTemplate(arrayType, baseType, baseClass, dim);
+	
+	}
+	private Template toTemplate(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 = toTemplate(arrayType, genericBaseType, baseClass, dim-1);
+			return new ReflectionMultidimentionalArrayTemplate(componentClass, componentTemplate);
+		} else {
+			ReflectionMultidimentionalArrayTemplate componentTemplate = (ReflectionMultidimentionalArrayTemplate)
+				toTemplate(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/builder/ArrayTemplateBuilderSelector.java b/java/src/main/java/org/msgpack/template/builder/ArrayTemplateBuilderSelector.java
new file mode 100644
index 00000000..30417dd0
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/builder/ArrayTemplateBuilderSelector.java
@@ -0,0 +1,35 @@
+package org.msgpack.template.builder;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Type;
+
+import org.msgpack.Template;
+
+public class ArrayTemplateBuilderSelector implements BuilderSelector {
+
+	public static final String NAME = "ArrayTemplateBuilder";
+	
+	@Override
+	public String getName(){
+		return NAME;
+	}
+	
+	
+	@Override
+	public boolean matchType(Type targetType) {
+		if(targetType instanceof GenericArrayType){
+			return true;
+		}
+		Class<?> targetClass = (Class<?>)targetType;
+		return targetClass.isArray();
+	}
+	
+	ArrayTemplateBuilder templateBuilder = new ArrayTemplateBuilder();
+
+	@Override
+	public TemplateBuilder getTemplateBuilder(Type target) {
+		return templateBuilder;
+	}
+
+
+}
diff --git a/java/src/main/java/org/msgpack/template/builder/BuilderSelector.java b/java/src/main/java/org/msgpack/template/builder/BuilderSelector.java
new file mode 100644
index 00000000..d30aa90b
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/builder/BuilderSelector.java
@@ -0,0 +1,27 @@
+package org.msgpack.template.builder;
+
+import java.lang.reflect.Type;
+
+/**
+ * Match condition for TemplateBuilder.
+ * @author takeshita
+ *
+ */
+public interface BuilderSelector {
+	
+	
+	/**
+	 * Name of this.
+	 * @return
+	 */
+	public String getName();
+	
+	
+	public abstract boolean matchType(Type targetType);
+	
+	
+	public abstract TemplateBuilder getTemplateBuilder(Type targetType);
+	
+	
+
+}
diff --git a/java/src/main/java/org/msgpack/template/builder/BuilderSelectorRegistry.java b/java/src/main/java/org/msgpack/template/builder/BuilderSelectorRegistry.java
new file mode 100644
index 00000000..329f882b
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/builder/BuilderSelectorRegistry.java
@@ -0,0 +1,213 @@
+package org.msgpack.template.builder;
+
+import java.lang.reflect.Type;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.msgpack.template.BeansFieldEntryReader;
+import org.msgpack.template.BeansReflectionTemplateBuilder;
+import org.msgpack.template.JavassistTemplateBuilder;
+import org.msgpack.template.ReflectionTemplateBuilder;
+import org.msgpack.template.javassist.BeansBuildContext;
+import org.msgpack.template.javassist.BuildContext;
+import org.msgpack.template.javassist.BuildContextBase;
+import org.msgpack.template.javassist.BuildContextFactory;
+
+/**
+ * Registry for BuilderSelectors.
+ * You can modify BuilderSelector chain throw this class.
+ * 
+ * @author takeshita
+ *
+ */
+public class BuilderSelectorRegistry {
+	
+	private static BuilderSelectorRegistry instance = new BuilderSelectorRegistry();
+	static{
+		initForJava();
+	}
+	
+	public static BuilderSelectorRegistry getInstance(){
+		return instance;
+	}
+	
+	TemplateBuilder forceBuilder;
+	
+	
+    List<BuilderSelector> builderSelectors = new LinkedList<BuilderSelector>();
+
+	private BuilderSelectorRegistry(){
+	}
+	/**
+	 * initialize BuilderSelectors for basic java enviroment.
+	 */
+	private static void initForJava(){
+
+		instance.append(new ArrayTemplateBuilderSelector());
+		
+		if(isSupportJavassist()){
+			instance.append(
+					new MessagePackMessageBuilderSelector(
+							new JavassistTemplateBuilder()));
+			instance.forceBuilder = new JavassistTemplateBuilder();
+
+			//Java beans
+			instance.append(new MessagePackBeansBuilderSelector(
+					new JavassistTemplateBuilder(
+							new BeansFieldEntryReader(),
+							new BuildContextFactory() {
+								@Override
+								public BuildContextBase createBuildContext(JavassistTemplateBuilder builder) {
+									return new BeansBuildContext(builder);
+								}
+							}
+					)));
+		}else{
+			instance.append(
+					new MessagePackMessageBuilderSelector(
+							new ReflectionTemplateBuilder()));
+			instance.forceBuilder = new ReflectionTemplateBuilder();
+			
+			//Java beans
+			instance.append(new MessagePackBeansBuilderSelector(
+					new BeansReflectionTemplateBuilder()));
+		}
+		
+		instance.append(new MessagePackOrdinalEnumBuilderSelector());
+		instance.append(new EnumBuilderSelector());
+	}
+	public static boolean isSupportJavassist(){
+		try {
+			return System.getProperty("java.vm.name").equals("Dalvik");
+		} catch (Exception e) {
+			return true;
+		}
+	}
+	
+    /**
+     * Check whether same name BuilderSelector is registered.
+     * @param builderSelectorName
+     * @return
+     */
+    public boolean contains(String builderSelectorName){
+    	for(BuilderSelector bs : builderSelectors){
+    		if(bs.getName().equals(builderSelectorName)){
+    			return true;
+    		}
+    	}
+    	return false;
+    }
+    /**
+     * Append BuilderSelector to tail
+     * @param builderSelector
+     */
+    public void append(BuilderSelector builderSelector){
+    	
+    	if(contains(builderSelector.getName())){
+			throw new RuntimeException("Duplicate BuilderSelector name:" + builderSelector.getName());
+    	}
+    	this.builderSelectors.add(builderSelector);
+    }
+    /**
+     * Insert BuiderSelector to head
+     * @param builderSelector
+     */
+    public void prepend(BuilderSelector builderSelector){
+    	if(contains(builderSelector.getName())){
+			throw new RuntimeException("Duplicate BuilderSelector name:" + builderSelector.getName());
+    	}
+    	if(builderSelectors.size() > 0){
+    		this.builderSelectors.add(0, builderSelector);
+    	}else{
+    		this.builderSelectors.add(builderSelector);
+    	}
+    }
+    
+    /**
+     * Insert BuilderSelector
+     * @param index
+     * @param builderSelector
+     */
+    public void insert(int index,BuilderSelector builderSelector){
+    	if(contains(builderSelector.getName())){
+			throw new RuntimeException("Duplicate BuilderSelector name:" + builderSelector.getName());
+    	}
+    	if(builderSelectors.size() > 0){
+    		this.builderSelectors.add(index, builderSelector);
+    		
+    	}else{
+    		this.builderSelectors.add(builderSelector);
+    	}
+    }
+    /**
+     * Replace same name BuilderSelector
+     * @param builderSelector
+     */
+    public void replace(BuilderSelector builderSelector){
+    	String name = builderSelector.getName();
+    	int index = getIndex(name);
+    	builderSelectors.add(index, builderSelector);
+    	builderSelectors.remove(index + 1);
+    }
+    
+    /**
+     * Insert the BuilderSelector before BuilderSelector named "builderSelectorName".
+     * @param builderSelectorName
+     * @param builderSelector
+     */
+    public void insertBefore(String builderSelectorName,BuilderSelector builderSelector){
+    	int index = getIndex(builderSelectorName);
+    	
+    	builderSelectors.add(index,builderSelector);
+    }
+    /**
+     * Insert the BuilderSelector after BuilderSelector named "builderSelectorName".
+     * @param builderSelectorName
+     * @param builderSelector
+     */
+    public void insertAfter(String builderSelectorName,BuilderSelector builderSelector){
+    	int index = getIndex(builderSelectorName);
+    	if(index + 1 == builderSelectors.size()){
+    		builderSelectors.add(builderSelector);
+    	}else{
+    		builderSelectors.add(index + 1 , builderSelector);
+    	}
+    }
+    private int getIndex(String builderSelectorName){
+    	int index = 0;
+    	for(BuilderSelector bs : builderSelectors){
+    		if(bs.getName().equals(builderSelectorName)){
+    			break;
+    		}
+    		index++;
+    	}
+    	if(index >= builderSelectors.size()){
+    		throw new RuntimeException(
+    				String.format("BuilderSelector named %s does not exist",builderSelectorName));
+    	}
+    	return index;
+    }
+    
+    
+    public TemplateBuilder select(Type target){
+    	for(BuilderSelector selector : builderSelectors){
+    		if(selector.matchType(target)){
+    			return selector.getTemplateBuilder(target);
+    		}
+    	}
+    	return null;
+    }
+
+	public TemplateBuilder getForceBuilder() {
+		return forceBuilder;
+	}
+
+
+	public void setForceBuilder(TemplateBuilder forceBuilder) {
+		this.forceBuilder = forceBuilder;
+	}
+	
+	
+	
+
+}
diff --git a/java/src/main/java/org/msgpack/template/builder/CustomTemplateBuilder.java b/java/src/main/java/org/msgpack/template/builder/CustomTemplateBuilder.java
new file mode 100644
index 00000000..6f7f45cb
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/builder/CustomTemplateBuilder.java
@@ -0,0 +1,53 @@
+package org.msgpack.template.builder;
+
+import java.lang.reflect.Type;
+
+import org.msgpack.Template;
+import org.msgpack.template.FieldList;
+import org.msgpack.template.FieldOption;
+import org.msgpack.template.IFieldEntry;
+import org.msgpack.template.IFieldEntryReader;
+import org.msgpack.template.TemplateBuildException;
+import org.msgpack.template.javassist.BuildContextFactory;
+
+public abstract class CustomTemplateBuilder extends TemplateBuilder {
+
+
+	public abstract IFieldEntryReader getFieldEntryReader();
+
+	
+	public abstract Template buildTemplate(Class<?> targetClass , IFieldEntry[] entries);
+	
+	public Template buildTemplate(Class<?> targetClass ,FieldOption implicitOption ){
+		checkValidation(targetClass);
+		return buildTemplate(targetClass,
+				getFieldEntryReader().readFieldEntries(targetClass, implicitOption));
+	}
+	public Template buildTemplate(Class<?> targetClass, FieldList flist) throws NoSuchFieldException {
+		checkValidation(targetClass);
+		return buildTemplate(targetClass, getFieldEntryReader().convertFieldEntries(targetClass, flist));
+	}
+
+	@Override
+	public Template buildTemplate(Type targetType) {
+		Class<?> targetClass = (Class<?>)targetType;
+		IFieldEntryReader reader = getFieldEntryReader();
+		FieldOption implicitOption = reader.readImplicitFieldOption(targetClass);
+		checkValidation(targetClass);
+		
+		IFieldEntry[] entries = reader.readFieldEntries(targetClass, implicitOption);
+		
+		return buildTemplate(targetClass,entries);
+	}
+	private void checkValidation(Class<?> targetClass) {
+		if(targetClass.isInterface()) {
+			throw new TemplateBuildException("cannot build template of interface");
+		}
+		if(targetClass.isArray()) {
+			throw new TemplateBuildException("cannot build template of array class");
+		}
+		if(targetClass.isPrimitive()) {
+			throw new TemplateBuildException("cannot build template of primitive type");
+		}
+	}
+}
\ No newline at end of file
diff --git a/java/src/main/java/org/msgpack/template/builder/EnumBuilderSelector.java b/java/src/main/java/org/msgpack/template/builder/EnumBuilderSelector.java
new file mode 100644
index 00000000..b8a2701c
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/builder/EnumBuilderSelector.java
@@ -0,0 +1,26 @@
+package org.msgpack.template.builder;
+
+import java.lang.reflect.Type;
+
+public class EnumBuilderSelector implements BuilderSelector {
+
+	public static final String NAME = "EnumTemplateBuilder";
+	
+	public String getName(){
+		return NAME;
+	}
+	
+	@Override
+	public boolean matchType(Type targetType) {
+		return ((Class<?>)targetType).isEnum();
+	}
+
+
+	OrdinalEnumTemplateBuilder builder = new OrdinalEnumTemplateBuilder();
+
+	@Override
+	public TemplateBuilder getTemplateBuilder(Type targetType) {
+		return builder;
+	}
+
+}
diff --git a/java/src/main/java/org/msgpack/template/builder/MessagePackBeansBuilderSelector.java b/java/src/main/java/org/msgpack/template/builder/MessagePackBeansBuilderSelector.java
new file mode 100644
index 00000000..3b4a5903
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/builder/MessagePackBeansBuilderSelector.java
@@ -0,0 +1,40 @@
+package org.msgpack.template.builder;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import org.msgpack.annotation.MessagePackBeans;
+import org.msgpack.annotation.MessagePackMessage;
+
+public class MessagePackBeansBuilderSelector implements BuilderSelector{
+	
+	public static final String NAME = "MessagePackBeansTemplateBuilder";
+	
+	
+    TemplateBuilder builder;
+	public MessagePackBeansBuilderSelector(TemplateBuilder builder){
+		this.builder = builder;
+	}
+	
+	
+	public String getName(){
+		return NAME;
+	}
+	
+	@Override
+	public boolean matchType(Type targetType) {
+		Class<?> target = (Class<?>)targetType;
+		return isAnnotated(target, MessagePackBeans.class);
+	}
+
+	@Override
+	public TemplateBuilder getTemplateBuilder(Type targetType) {
+		return builder;
+	}
+	
+
+	private boolean isAnnotated(Class<?> ao, Class<? extends Annotation> with) {
+		return ao.getAnnotation(with) != null;
+	}
+
+}
diff --git a/java/src/main/java/org/msgpack/template/builder/MessagePackMessageBuilderSelector.java b/java/src/main/java/org/msgpack/template/builder/MessagePackMessageBuilderSelector.java
new file mode 100644
index 00000000..008b8f06
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/builder/MessagePackMessageBuilderSelector.java
@@ -0,0 +1,38 @@
+package org.msgpack.template.builder;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import org.msgpack.annotation.MessagePackMessage;
+
+public class MessagePackMessageBuilderSelector implements BuilderSelector{
+	
+	public static final String NAME = "MessagePackMessageTemplateBuilder";
+	
+	
+    TemplateBuilder builder;
+	public MessagePackMessageBuilderSelector(TemplateBuilder builder){
+		this.builder = builder;
+	}
+	
+	public String getName(){
+		return NAME;
+	}
+	
+	@Override
+	public boolean matchType(Type targetType) {
+		Class<?> target = (Class<?>)targetType;
+		return isAnnotated(target, MessagePackMessage.class);
+	}
+
+	@Override
+	public TemplateBuilder getTemplateBuilder(Type targetType) {
+		return builder;
+	}
+	
+
+	private boolean isAnnotated(Class<?> ao, Class<? extends Annotation> with) {
+		return ao.getAnnotation(with) != null;
+	}
+
+}
diff --git a/java/src/main/java/org/msgpack/template/builder/MessagePackOrdinalEnumBuilderSelector.java b/java/src/main/java/org/msgpack/template/builder/MessagePackOrdinalEnumBuilderSelector.java
new file mode 100644
index 00000000..2d32cb47
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/builder/MessagePackOrdinalEnumBuilderSelector.java
@@ -0,0 +1,34 @@
+package org.msgpack.template.builder;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import org.msgpack.annotation.MessagePackOrdinalEnum;
+
+public class MessagePackOrdinalEnumBuilderSelector implements BuilderSelector {
+
+	public static final String NAME = "MessagePackOrdinalEnumBuilderTemplate";
+	
+	public String getName(){
+		return NAME;
+	}
+	
+	@Override
+	public boolean matchType(Type targetType) {
+		Class<?> target = (Class<?>)targetType;
+		return isAnnotated(target, MessagePackOrdinalEnum.class);
+	}
+	
+	OrdinalEnumTemplateBuilder builder = new OrdinalEnumTemplateBuilder();
+
+	@Override
+	public TemplateBuilder getTemplateBuilder(Type targetType) {
+		return builder;
+	}
+	
+
+	private boolean isAnnotated(Class<?> ao, Class<? extends Annotation> with) {
+		return ao.getAnnotation(with) != null;
+	}
+
+}
diff --git a/java/src/main/java/org/msgpack/template/builder/OrdinalEnumTemplateBuilder.java b/java/src/main/java/org/msgpack/template/builder/OrdinalEnumTemplateBuilder.java
new file mode 100644
index 00000000..1d4752fc
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/builder/OrdinalEnumTemplateBuilder.java
@@ -0,0 +1,68 @@
+package org.msgpack.template.builder;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.msgpack.AbstractTemplate;
+import org.msgpack.MessagePackObject;
+import org.msgpack.MessageTypeException;
+import org.msgpack.Packer;
+import org.msgpack.Template;
+import org.msgpack.Unpacker;
+import org.msgpack.template.TemplateBuildException;
+
+public class OrdinalEnumTemplateBuilder extends TemplateBuilder{
+
+	static class ReflectionOrdinalEnumTemplate extends AbstractTemplate {
+		protected Enum<?>[] entries;
+		protected Map<Enum<?>, Integer> reverse;
+
+		ReflectionOrdinalEnumTemplate(Enum<?>[] entries) {
+			this.entries = entries;
+			this.reverse = new HashMap<Enum<?>, Integer>();
+			for(int i=0; i < entries.length; i++) {
+				this.reverse.put(entries[i], i);
+			}
+		}
+
+		public void pack(Packer pk, Object target) throws IOException {
+			Integer ord = reverse.get(target);
+			if(ord == null) {
+				throw new MessageTypeException();
+			}
+			pk.pack((int)ord);
+		}
+
+		public Object unpack(Unpacker pac, Object to) throws IOException, MessageTypeException {
+			int ord = pac.unpackInt();
+			if(entries.length <= ord) {
+				throw new MessageTypeException();
+			}
+			return entries[ord];
+		}
+
+		public Object convert(MessagePackObject from, Object to) throws MessageTypeException {
+			int ord = from.asInt();
+			if(entries.length <= ord) {
+				throw new MessageTypeException();
+			}
+			return entries[ord];
+		}
+	}
+	@Override
+	public Template buildTemplate(Type targetType) {
+		Class<?> targetClass = (Class<?>)targetType;
+		checkOrdinalEnumValidation(targetClass);
+		Enum<?>[] entries = (Enum<?>[])targetClass.getEnumConstants();
+		
+		return new ReflectionOrdinalEnumTemplate(entries);
+	}
+	private void checkOrdinalEnumValidation(Class<?> targetClass) {
+		if(!targetClass.isEnum()) {
+			throw new TemplateBuildException("tried to build ordinal enum template of non-enum class");
+		}
+	}
+
+}
diff --git a/java/src/main/java/org/msgpack/template/builder/TemplateBuilder.java b/java/src/main/java/org/msgpack/template/builder/TemplateBuilder.java
new file mode 100644
index 00000000..0dd36ffc
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/builder/TemplateBuilder.java
@@ -0,0 +1,327 @@
+//
+// 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.builder;
+
+import java.io.IOException;
+import java.lang.reflect.*;
+import java.lang.annotation.*;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import org.msgpack.*;
+import org.msgpack.annotation.*;
+import org.msgpack.template.FieldList;
+import org.msgpack.template.FieldOption;
+import org.msgpack.template.IFieldEntry;
+import org.msgpack.template.IFieldEntryReader;
+import org.msgpack.template.JavassistTemplateBuilder;
+import org.msgpack.template.ReflectionTemplateBuilder;
+
+public abstract class TemplateBuilder {
+
+	public abstract Template buildTemplate(Type targetType);
+	/*
+	// Override this method
+	public abstract Template buildTemplate(Class<?> targetClass, IFieldEntry[] entries);
+
+	// 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 abstract IFieldEntryReader getFieldEntryReader();
+
+	public Template buildTemplate(Class<?> targetClass, FieldList flist) throws NoSuchFieldException {
+		checkValidation(targetClass);
+		return buildTemplate(targetClass, getFieldEntryReader().convertFieldEntries(targetClass, flist));
+	}
+
+	public Template buildTemplate(Class<?> targetClass, FieldOption implicitOption) {
+		checkValidation(targetClass);
+		return buildTemplate(targetClass, getFieldEntryReader().readFieldEntries(targetClass, implicitOption));
+	}
+
+	public Template buildTemplate(Class<?> targetClass) {
+		FieldOption implicitOption = getFieldEntryReader().readImplicitFieldOption(targetClass);
+		return buildTemplate(targetClass, implicitOption);
+	}
+
+	public Template buildOrdinalEnumTemplate(Class<?> targetClass) {
+		checkOrdinalEnumValidation(targetClass);
+		Enum<?>[] entries = (Enum<?>[])targetClass.getEnumConstants();
+		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;
+			}
+			if(baseType instanceof ParameterizedType) {
+				baseClass = (Class<?>)((ParameterizedType)baseType).getRawType();
+			} else {
+				baseClass = (Class<?>)baseType;
+			}
+		} 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 void checkValidation(Class<?> targetClass) {
+		if(targetClass.isInterface()) {
+			throw new TemplateBuildException("cannot build template of interface");
+		}
+		if(targetClass.isArray()) {
+			throw new TemplateBuildException("cannot build template of array class");
+		}
+		if(targetClass.isPrimitive()) {
+			throw new TemplateBuildException("cannot build template of primitive type");
+		}
+	}
+	private void checkOrdinalEnumValidation(Class<?> targetClass) {
+		if(!targetClass.isEnum()) {
+			throw new TemplateBuildException("tried to build ordinal enum template of non-enum class");
+		}
+	}
+
+    
+	private static TemplateBuilder instance;
+	static {
+		instance = selectDefaultTemplateBuilder();
+	}
+
+	private static TemplateBuilder selectDefaultTemplateBuilder() {
+		try {
+			// FIXME JavassistTemplateBuilder doesn't work on DalvikVM
+			if(System.getProperty("java.vm.name").equals("Dalvik")) {
+				return ReflectionTemplateBuilder.getInstance();
+			}
+		} catch (Exception e) {
+		}
+		return JavassistTemplateBuilder.getInstance();
+	}
+
+	public synchronized static void setInstance(TemplateBuilder builder) {
+		instance = builder;
+	}
+
+	public static Template build(Class<?> targetClass) {
+		return instance.buildTemplate(targetClass);
+	}
+
+	public static Template build(Class<?> targetClass, FieldOption implicitOption) {
+		return instance.buildTemplate(targetClass, implicitOption);
+	}
+
+	public static Template build(Class<?> targetClass, FieldList flist) throws NoSuchFieldException {
+		return instance.buildTemplate(targetClass, flist);
+	}
+
+	public static Template buildOrdinalEnum(Class<?> targetClass) {
+		return instance.buildOrdinalEnumTemplate(targetClass);
+	}
+
+	public static Template buildArray(Type arrayType) {
+		return instance.buildArrayTemplate(arrayType);
+	}*/
+
+    /*
+	private static void checkValidation(Class<?> targetClass) {
+		if(targetClass.isInterface()) {
+			throw new TemplateBuildException("cannot build template of interface");
+		}
+		if(targetClass.isArray()) {
+			throw new TemplateBuildException("cannot build template of array class");
+		}
+		if(targetClass.isPrimitive()) {
+			throw new TemplateBuildException("cannot build template of primitive type");
+		}
+	}
+
+	private static void checkOrdinalEnumValidation(Class<?> targetClass) {
+		if(!targetClass.isEnum()) {
+			throw new TemplateBuildException("tried to build ordinal enum template of non-enum class");
+		}
+	}*/
+
+    /*
+	static IFieldEntry[] convertFieldEntries(Class<?> targetClass, FieldList flist) throws NoSuchFieldException {
+		List<FieldList.Entry> src = flist.getList();
+		FieldEntry[] result = new FieldEntry[src.size()];
+		for(int i=0; i < src.size(); i++) {
+			FieldList.Entry s = src.get(i);
+			if(s.isAvailable()) {
+				result[i] = new FieldEntry(targetClass.getDeclaredField(s.getName()), s.getOption());
+			} else {
+				result[i] = new FieldEntry();
+			}
+		}
+		return result;
+	}*/
+    
+	/*static IFieldEntry[] readFieldEntries(Class<?> targetClass, FieldOption implicitOption) {
+		Field[] allFields = readAllFields(targetClass);
+
+		/* index:
+		 *   @Index(0) int field_a;   // 0
+		 *             int field_b;   // 1
+		 *   @Index(3) int field_c;   // 3
+		 *             int field_d;   // 4
+		 *   @Index(2) int field_e;   // 2
+		 *             int field_f;   // 5
+		 *//*
+		List<FieldEntry> indexed = new ArrayList<FieldEntry>();
+		int maxIndex = -1;
+		for(Field f : allFields) {
+			FieldOption opt = readFieldOption(f, implicitOption);
+			if(opt == FieldOption.IGNORE) {
+				// skip
+				continue;
+			}
+
+			int index = readFieldIndex(f, maxIndex);
+
+			if(indexed.size() > index && indexed.get(index) != null) {
+				throw new TemplateBuildException("duplicated index: "+index);
+			}
+			if(index < 0) {
+				throw new TemplateBuildException("invalid index: "+index);
+			}
+
+			while(indexed.size() <= index) {
+				indexed.add(null);
+			}
+			indexed.set(index, new FieldEntry(f, opt));
+
+			if(maxIndex < index) {
+				maxIndex = index;
+			}
+		}
+
+		FieldEntry[] result = new FieldEntry[maxIndex+1];
+		for(int i=0; i < indexed.size(); i++) {
+			FieldEntry e = indexed.get(i);
+			if(e == null) {
+				result[i] = new FieldEntry();
+			} else {
+				result[i] = e;
+			}
+		}
+
+		return result;
+	}*/
+    /* 
+	private static Field[] readAllFields(Class<?> targetClass) {
+		// order: [fields of super class, ..., fields of this class]
+		List<Field[]> succ = new ArrayList<Field[]>();
+		int total = 0;
+		for(Class<?> c = targetClass; c != Object.class; c = c.getSuperclass()) {
+			Field[] fields = c.getDeclaredFields();
+			total += fields.length;
+			succ.add(fields);
+		}
+		Field[] result = new Field[total];
+		int off = 0;
+		for(int i=succ.size()-1; i >= 0; i--) {
+			Field[] fields = succ.get(i);
+			System.arraycopy(fields, 0, result, off, fields.length);
+			off += fields.length;
+		}
+		return result;
+	}
+
+	private static FieldOption readImplicitFieldOption(Class<?> targetClass) {
+		MessagePackMessage a = targetClass.getAnnotation(MessagePackMessage.class);
+		if(a == null) {
+			return FieldOption.DEFAULT;
+		}
+		return a.value();
+	}
+
+	private static FieldOption readFieldOption(Field field, FieldOption implicitOption) {
+		int mod = field.getModifiers();
+		if(Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
+			return FieldOption.IGNORE;
+		}
+
+		if(isAnnotated(field, Ignore.class)) {
+			return FieldOption.IGNORE;
+		} else if(isAnnotated(field, Required.class)) {
+			return FieldOption.REQUIRED;
+		} else if(isAnnotated(field, Optional.class)) {
+			return FieldOption.OPTIONAL;
+		} else if(isAnnotated(field, Nullable.class)) {
+			if(field.getDeclaringClass().isPrimitive()) {
+				return FieldOption.REQUIRED;
+			} else {
+				return FieldOption.NULLABLE;
+			}
+		}
+
+		if(implicitOption != FieldOption.DEFAULT) {
+			return implicitOption;
+		}
+
+		// default mode:
+		//   transient : Ignore
+		//   public    : Required
+		//   others    : Ignore
+		if(Modifier.isTransient(mod)) {
+			return FieldOption.IGNORE;
+		} else if(Modifier.isPublic(mod)) {
+			return FieldOption.REQUIRED;
+		} else {
+			return FieldOption.IGNORE;
+		}
+	}
+
+	private static int readFieldIndex(Field field, int maxIndex) {
+		Index a = field.getAnnotation(Index.class);
+		if(a == null) {
+			return maxIndex + 1;
+		} else {
+			return a.value();
+		}
+	}
+
+	private static boolean isAnnotated(AccessibleObject ao, Class<? extends Annotation> with) {
+		return ao.getAnnotation(with) != null;
+	}*/
+}
+
diff --git a/java/src/main/java/org/msgpack/template/javassist/BeansBuildContext.java b/java/src/main/java/org/msgpack/template/javassist/BeansBuildContext.java
new file mode 100644
index 00000000..74d477fd
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/javassist/BeansBuildContext.java
@@ -0,0 +1,273 @@
+
+package org.msgpack.template.javassist;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Type;
+import java.lang.Thread;
+
+import org.msgpack.*;
+import org.msgpack.template.*;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.CtMethod;
+import javassist.CtNewConstructor;
+import javassist.CtNewMethod;
+import javassist.LoaderClassPath;
+import javassist.NotFoundException;
+import javassist.ClassClassPath;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+
+public class BeansBuildContext extends BuildContextBase<BeansFieldEntry> {
+	protected BeansFieldEntry[] entries;
+	protected Class<?> origClass;
+	protected String origName;
+	protected Template[] templates;
+	protected int minimumArrayLength;
+
+	public BeansBuildContext(JavassistTemplateBuilder director) {
+		super(director);
+	}
+	
+	public Template buildTemplate(Class<?> targetClass, BeansFieldEntry[] entries, Template[] templates) {
+		this.entries = entries;
+		this.templates = templates;
+		this.origClass = targetClass;
+		this.origName = this.origClass.getName();
+		return build(this.origName);
+	}
+
+	protected void setSuperClass() throws CannotCompileException, NotFoundException {
+		this.tmplCtClass.setSuperclass(
+				director.getCtClass(JavassistTemplate.class.getName()));
+	}
+
+	protected void buildConstructor() throws CannotCompileException, NotFoundException {
+		// Constructor(Class targetClass, Template[] templates)
+		CtConstructor newCtCons = CtNewConstructor.make(
+			new CtClass[] {
+				director.getCtClass(Class.class.getName()),
+				director.getCtClass(Template.class.getName()+"[]")
+			},
+			new CtClass[0],
+			this.tmplCtClass);
+		this.tmplCtClass.addConstructor(newCtCons);
+	}
+
+	protected Template buildInstance(Class<?> c) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
+		Constructor<?> cons = c.getConstructor(new Class[] {
+				Class.class,
+				Template[].class
+			});
+		Object tmpl = cons.newInstance(new Object[] {
+				this.origClass,
+				this.templates
+			});
+		return (Template)tmpl;
+	}
+
+	protected void buildMethodInit() {
+		this.minimumArrayLength = 0;
+		for(int i=0; i < entries.length; i++) {
+			IFieldEntry e = entries[i];
+			if(e.isRequired() || e.isNullable()) {
+				this.minimumArrayLength = i+1;
+			}
+		}
+	}
+
+	protected String buildPackMethodBody() {
+		resetStringBuilder();
+		buildString("{");
+		buildString("%s _$$_t = (%s)$2;", this.origName, this.origName);
+		buildString("$1.packArray(%d);", entries.length);
+		for(int i=0; i < entries.length; i++) {
+			BeansFieldEntry e = entries[i];
+			if(!e.isAvailable()) {
+				buildString("$1.packNil();");
+				continue;
+			}
+			Class<?> type = e.getType();
+			if(type.isPrimitive()) {
+				buildString("$1.%s(_$$_t.%s());", primitivePackName(type), e.getGetterName());
+			} else {
+				buildString("if(_$$_t.%s() == null) {", e.getGetterName());
+				if(!e.isNullable() && !e.isOptional()) {
+					buildString("throw new %s();", MessageTypeException.class.getName());
+				} else {
+					buildString("$1.packNil();");
+				}
+				buildString("} else {");
+				buildString("  this.templates[%d].pack($1, _$$_t.%s());", i, e.getGetterName());
+				buildString("}");
+			}
+		}
+		buildString("}");
+		return getBuiltString();
+	}
+
+	protected String buildUnpackMethodBody() {
+		resetStringBuilder();
+		buildString("{ ");
+
+		buildString("%s _$$_t;", this.origName);
+		buildString("if($2 == null) {");
+		buildString("  _$$_t = new %s();", this.origName);
+		buildString("} else {");
+		buildString("  _$$_t = (%s)$2;", this.origName);
+		buildString("}");
+
+		buildString("int length = $1.unpackArray();");
+		buildString("if(length < %d) {", this.minimumArrayLength);
+		buildString("  throw new %s();", MessageTypeException.class.getName());
+		buildString("}");
+
+		int i;
+		for(i=0; i < this.minimumArrayLength; i++) {
+			BeansFieldEntry e = entries[i];
+			if(!e.isAvailable()) {
+				buildString("$1.unpackObject();");
+				continue;
+			}
+
+			buildString("if($1.tryUnpackNull()) {");
+				if(e.isRequired()) {
+					// Required + nil => exception
+					buildString("throw new %s();", MessageTypeException.class.getName());
+				} else if(e.isOptional()) {
+					// Optional + nil => keep default value
+				} else {  // Nullable
+					// Nullable + nil => set null
+					buildString("_$$_t.%s(null);", e.getSetterName());
+				}
+			buildString("} else {");
+				Class<?> type = e.getType();
+				if(type.isPrimitive()) {
+					buildString("_$$_t.set%s( $1.%s() );", e.getName(), primitiveUnpackName(type));
+				} else {
+					buildString("_$$_t.set%s( (%s)this.templates[%d].unpack($1, _$$_t.get%s()) );", e.getName(), e.getJavaTypeName(), i, e.getName());
+				}
+			buildString("}");
+		}
+
+		for(; i < entries.length; i++) {
+			buildString("if(length <= %d) { return _$$_t; }", i);
+
+			BeansFieldEntry e = entries[i];
+			if(!e.isAvailable()) {
+				buildString("$1.unpackObject();");
+				continue;
+			}
+
+			buildString("if($1.tryUnpackNull()) {");
+				// this is Optional field becaue i >= minimumArrayLength
+				// Optional + nil => keep default value
+			buildString("} else {");
+				Class<?> type = e.getType();
+				if(type.isPrimitive()) {
+					buildString("_$$_t.%s( $1.%s() );", e.getSetterName(), primitiveUnpackName(type));
+				} else {
+					buildString("_$$_t.%s( (%s)this.templates[%d].unpack($1, _$$_t.%s()) );", e.getSetterName(), e.getJavaTypeName(), i, e.getGetterName());
+				}
+			buildString("}");
+		}
+
+		// latter entries are all Optional + nil => keep default value
+
+		buildString("for(int i=%d; i < length; i++) {", i);
+		buildString("  $1.unpackObject();");
+		buildString("}");
+
+		buildString("return _$$_t;");
+
+		buildString("}");
+		return getBuiltString();
+	}
+
+	protected String buildConvertMethodBody() {
+		resetStringBuilder();
+		buildString("{ ");
+
+		buildString("%s _$$_t;", this.origName);
+		buildString("if($2 == null) {");
+		buildString("  _$$_t = new %s();", this.origName);
+		buildString("} else {");
+		buildString("  _$$_t = (%s)$2;", this.origName);
+		buildString("}");
+
+		buildString("%s[] array = $1.asArray();", MessagePackObject.class.getName());
+		buildString("int length = array.length;");
+		buildString("if(length < %d) {", this.minimumArrayLength);
+		buildString("  throw new %s();", MessageTypeException.class.getName());
+		buildString("}");
+
+		buildString("%s obj;", MessagePackObject.class.getName());
+
+		int i;
+		for(i=0; i < this.minimumArrayLength; i++) {
+			BeansFieldEntry e = entries[i];
+			if(!e.isAvailable()) {
+				continue;
+			}
+
+			buildString("obj = array[%d];", i);
+			buildString("if(obj.isNil()) {");
+				if(e.isRequired()) {
+					// Required + nil => exception
+					buildString("throw new %s();", MessageTypeException.class.getName());
+				} else if(e.isOptional()) {
+					// Optional + nil => keep default value
+				} else {  // Nullable
+					// Nullable + nil => set null
+					buildString("_$$_t.%s( null );", e.getSetterName());
+				}
+			buildString("} else {");
+				Class<?> type = e.getType();
+				if(type.isPrimitive()) {
+					buildString("_$$_t.%s( obj.%s() );", e.getSetterName(), primitiveConvertName(type));
+				} else {
+					buildString("_$$_t.%s( (%s)this.templates[%d].convert(obj, _$$_t.%s()) );", e.getSetterName(), e.getJavaTypeName(), i, e.getGetterName());
+				}
+			buildString("}");
+		}
+
+		for(; i < entries.length; i++) {
+			buildString("if(length <= %d) { return _$$_t; }", i);
+
+			BeansFieldEntry e = entries[i];
+			if(!e.isAvailable()) {
+				continue;
+			}
+
+			buildString("obj = array[%d];", i);
+			buildString("if(obj.isNil()) {");
+				// this is Optional field becaue i >= minimumArrayLength
+				// Optional + nil => keep default value
+			buildString("} else {");
+				Class<?> type = e.getType();
+				if(type.isPrimitive()) {
+					buildString("_$$_t.%s( obj.%s() );", e.getSetterName(), primitiveConvertName(type));
+				} else {
+					buildString("_$$_t.%s( (%s)this.templates[%d].convert(obj, _$$_t.%s()) );", e.getSetterName(), e.getJavaTypeName(), i, e.getGetterName());
+				}
+			buildString("}");
+		}
+
+		// latter entries are all Optional + nil => keep default value
+
+		buildString("return _$$_t;");
+
+		buildString("}");
+		return getBuiltString();
+	}
+
+}
\ No newline at end of file
diff --git a/java/src/main/java/org/msgpack/template/javassist/BuildContext.java b/java/src/main/java/org/msgpack/template/javassist/BuildContext.java
new file mode 100644
index 00000000..ff1c2fc2
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/javassist/BuildContext.java
@@ -0,0 +1,273 @@
+
+package org.msgpack.template.javassist;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Type;
+import java.lang.Thread;
+
+import org.msgpack.*;
+import org.msgpack.template.*;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.CtMethod;
+import javassist.CtNewConstructor;
+import javassist.CtNewMethod;
+import javassist.LoaderClassPath;
+import javassist.NotFoundException;
+import javassist.ClassClassPath;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+
+public class BuildContext extends BuildContextBase<FieldEntry> {
+	protected IFieldEntry[] entries;
+	protected Class<?> origClass;
+	protected String origName;
+	protected Template[] templates;
+	protected int minimumArrayLength;
+
+	public BuildContext(JavassistTemplateBuilder director) {
+		super(director);
+	}
+	
+	public Template buildTemplate(Class<?> targetClass, FieldEntry[] entries, Template[] templates) {
+		this.entries = entries;
+		this.templates = templates;
+		this.origClass = targetClass;
+		this.origName = this.origClass.getName();
+		return build(this.origName);
+	}
+
+	protected void setSuperClass() throws CannotCompileException, NotFoundException {
+		this.tmplCtClass.setSuperclass(
+				director.getCtClass(JavassistTemplate.class.getName()));
+	}
+
+	protected void buildConstructor() throws CannotCompileException, NotFoundException {
+		// Constructor(Class targetClass, Template[] templates)
+		CtConstructor newCtCons = CtNewConstructor.make(
+			new CtClass[] {
+				director.getCtClass(Class.class.getName()),
+				director.getCtClass(Template.class.getName()+"[]")
+			},
+			new CtClass[0],
+			this.tmplCtClass);
+		this.tmplCtClass.addConstructor(newCtCons);
+	}
+
+	protected Template buildInstance(Class<?> c) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
+		Constructor<?> cons = c.getConstructor(new Class[] {
+				Class.class,
+				Template[].class
+			});
+		Object tmpl = cons.newInstance(new Object[] {
+				this.origClass,
+				this.templates
+			});
+		return (Template)tmpl;
+	}
+
+	protected void buildMethodInit() {
+		this.minimumArrayLength = 0;
+		for(int i=0; i < entries.length; i++) {
+			IFieldEntry e = entries[i];
+			if(e.isRequired() || e.isNullable()) {
+				this.minimumArrayLength = i+1;
+			}
+		}
+	}
+
+	protected String buildPackMethodBody() {
+		resetStringBuilder();
+		buildString("{");
+		buildString("%s _$$_t = (%s)$2;", this.origName, this.origName);
+		buildString("$1.packArray(%d);", entries.length);
+		for(int i=0; i < entries.length; i++) {
+			IFieldEntry e = entries[i];
+			if(!e.isAvailable()) {
+				buildString("$1.packNil();");
+				continue;
+			}
+			Class<?> type = e.getType();
+			if(type.isPrimitive()) {
+				buildString("$1.%s(_$$_t.%s);", primitivePackName(type), e.getName());
+			} else {
+				buildString("if(_$$_t.%s == null) {", e.getName());
+				if(!e.isNullable() && !e.isOptional()) {
+					buildString("throw new %s();", MessageTypeException.class.getName());
+				} else {
+					buildString("$1.packNil();");
+				}
+				buildString("} else {");
+				buildString("  this.templates[%d].pack($1, _$$_t.%s);", i, e.getName());
+				buildString("}");
+			}
+		}
+		buildString("}");
+		return getBuiltString();
+	}
+
+	protected String buildUnpackMethodBody() {
+		resetStringBuilder();
+		buildString("{ ");
+
+		buildString("%s _$$_t;", this.origName);
+		buildString("if($2 == null) {");
+		buildString("  _$$_t = new %s();", this.origName);
+		buildString("} else {");
+		buildString("  _$$_t = (%s)$2;", this.origName);
+		buildString("}");
+
+		buildString("int length = $1.unpackArray();");
+		buildString("if(length < %d) {", this.minimumArrayLength);
+		buildString("  throw new %s();", MessageTypeException.class.getName());
+		buildString("}");
+
+		int i;
+		for(i=0; i < this.minimumArrayLength; i++) {
+			IFieldEntry e = entries[i];
+			if(!e.isAvailable()) {
+				buildString("$1.unpackObject();");
+				continue;
+			}
+
+			buildString("if($1.tryUnpackNull()) {");
+				if(e.isRequired()) {
+					// Required + nil => exception
+					buildString("throw new %s();", MessageTypeException.class.getName());
+				} else if(e.isOptional()) {
+					// Optional + nil => keep default value
+				} else {  // Nullable
+					// Nullable + nil => set null
+					buildString("_$$_t.%s = null;", e.getName());
+				}
+			buildString("} else {");
+				Class<?> type = e.getType();
+				if(type.isPrimitive()) {
+					buildString("_$$_t.%s = $1.%s();", e.getName(), primitiveUnpackName(type));
+				} else {
+					buildString("_$$_t.%s = (%s)this.templates[%d].unpack($1, _$$_t.%s);", e.getName(), e.getJavaTypeName(), i, e.getName());
+				}
+			buildString("}");
+		}
+
+		for(; i < entries.length; i++) {
+			buildString("if(length <= %d) { return _$$_t; }", i);
+
+			IFieldEntry e = entries[i];
+			if(!e.isAvailable()) {
+				buildString("$1.unpackObject();");
+				continue;
+			}
+
+			buildString("if($1.tryUnpackNull()) {");
+				// this is Optional field becaue i >= minimumArrayLength
+				// Optional + nil => keep default value
+			buildString("} else {");
+				Class<?> type = e.getType();
+				if(type.isPrimitive()) {
+					buildString("_$$_t.%s = $1.%s();", e.getName(), primitiveUnpackName(type));
+				} else {
+					buildString("_$$_t.%s = (%s)this.templates[%d].unpack($1, _$$_t.%s);", e.getName(), e.getJavaTypeName(), i, e.getName());
+				}
+			buildString("}");
+		}
+
+		// latter entries are all Optional + nil => keep default value
+
+		buildString("for(int i=%d; i < length; i++) {", i);
+		buildString("  $1.unpackObject();");
+		buildString("}");
+
+		buildString("return _$$_t;");
+
+		buildString("}");
+		return getBuiltString();
+	}
+
+	protected String buildConvertMethodBody() {
+		resetStringBuilder();
+		buildString("{ ");
+
+		buildString("%s _$$_t;", this.origName);
+		buildString("if($2 == null) {");
+		buildString("  _$$_t = new %s();", this.origName);
+		buildString("} else {");
+		buildString("  _$$_t = (%s)$2;", this.origName);
+		buildString("}");
+
+		buildString("%s[] array = $1.asArray();", MessagePackObject.class.getName());
+		buildString("int length = array.length;");
+		buildString("if(length < %d) {", this.minimumArrayLength);
+		buildString("  throw new %s();", MessageTypeException.class.getName());
+		buildString("}");
+
+		buildString("%s obj;", MessagePackObject.class.getName());
+
+		int i;
+		for(i=0; i < this.minimumArrayLength; i++) {
+			IFieldEntry e = entries[i];
+			if(!e.isAvailable()) {
+				continue;
+			}
+
+			buildString("obj = array[%d];", i);
+			buildString("if(obj.isNil()) {");
+				if(e.isRequired()) {
+					// Required + nil => exception
+					buildString("throw new %s();", MessageTypeException.class.getName());
+				} else if(e.isOptional()) {
+					// Optional + nil => keep default value
+				} else {  // Nullable
+					// Nullable + nil => set null
+					buildString("_$$_t.%s = null;", e.getName());
+				}
+			buildString("} else {");
+				Class<?> type = e.getType();
+				if(type.isPrimitive()) {
+					buildString("_$$_t.%s = obj.%s();", e.getName(), primitiveConvertName(type));
+				} else {
+					buildString("_$$_t.%s = (%s)this.templates[%d].convert(obj, _$$_t.%s);", e.getName(), e.getJavaTypeName(), i, e.getName());
+				}
+			buildString("}");
+		}
+
+		for(; i < entries.length; i++) {
+			buildString("if(length <= %d) { return _$$_t; }", i);
+
+			IFieldEntry e = entries[i];
+			if(!e.isAvailable()) {
+				continue;
+			}
+
+			buildString("obj = array[%d];", i);
+			buildString("if(obj.isNil()) {");
+				// this is Optional field becaue i >= minimumArrayLength
+				// Optional + nil => keep default value
+			buildString("} else {");
+				Class<?> type = e.getType();
+				if(type.isPrimitive()) {
+					buildString("_$$_t.%s = obj.%s();", e.getName(), primitiveConvertName(type));
+				} else {
+					buildString("_$$_t.%s = (%s)this.templates[%d].convert(obj, _$$_t.%s);", e.getName(), e.getJavaTypeName(), i, e.getName());
+				}
+			buildString("}");
+		}
+
+		// latter entries are all Optional + nil => keep default value
+
+		buildString("return _$$_t;");
+
+		buildString("}");
+		return getBuiltString();
+	}
+
+}
\ No newline at end of file
diff --git a/java/src/main/java/org/msgpack/template/javassist/BuildContextBase.java b/java/src/main/java/org/msgpack/template/javassist/BuildContextBase.java
new file mode 100644
index 00000000..635dc47c
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/javassist/BuildContextBase.java
@@ -0,0 +1,244 @@
+//
+// 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.javassist;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Type;
+import java.lang.Thread;
+
+import org.msgpack.*;
+import org.msgpack.template.*;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.CtMethod;
+import javassist.CtNewConstructor;
+import javassist.CtNewMethod;
+import javassist.LoaderClassPath;
+import javassist.NotFoundException;
+import javassist.ClassClassPath;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public abstract class BuildContextBase<T extends IFieldEntry> {
+	
+	private static Logger LOG = LoggerFactory.getLogger(JavassistTemplateBuilder.class);
+
+	
+	protected JavassistTemplateBuilder director;
+
+	protected String tmplName;
+
+	protected CtClass tmplCtClass;
+
+	protected abstract void setSuperClass() throws CannotCompileException, NotFoundException;
+
+	protected abstract void buildConstructor() throws CannotCompileException, NotFoundException;
+
+	protected void buildMethodInit() { }
+
+	protected abstract String buildPackMethodBody();
+
+	protected abstract String buildUnpackMethodBody();
+
+	protected abstract String buildConvertMethodBody();
+
+	protected abstract Template buildInstance(Class<?> c) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException;
+
+	public BuildContextBase(JavassistTemplateBuilder director) {
+		this.director = director;
+	}
+	
+	
+	public abstract Template buildTemplate(Class<?> targetClass, T[] entries, Template[] templates);
+
+
+	protected Template build(final String className) {
+		try {
+			reset(className);
+			buildClass();
+			buildConstructor();
+			buildMethodInit();
+			buildPackMethod();
+			buildUnpackMethod();
+			buildConvertMethod();
+			return buildInstance(createClass());
+		} catch (Exception e) {
+			String code = getBuiltString();
+			if(code != null) {
+				LOG.error("builder: " + code, e);
+				throw new TemplateBuildException("cannot compile: " + code, e);
+			} else {
+				throw new TemplateBuildException(e);
+			}
+		}
+	}
+
+	protected void reset(String className) {
+		tmplName = className + "_$$_Template" + director.nextSeqId();
+		tmplCtClass = director.makeCtClass(tmplName);
+	}
+
+	protected void buildClass() throws CannotCompileException, NotFoundException {
+		setSuperClass();
+		tmplCtClass.addInterface(director.getCtClass(Template.class.getName()));
+	}
+
+	protected void buildPackMethod() throws CannotCompileException, NotFoundException {
+		String mbody = buildPackMethodBody();
+		int mod = javassist.Modifier.PUBLIC;
+		CtClass returnType = CtClass.voidType;
+		String mname = "pack";
+		CtClass[] paramTypes = new CtClass[] {
+				director.getCtClass(Packer.class.getName()),
+				director.getCtClass(Object.class.getName())
+		};
+		CtClass[] exceptTypes = new CtClass[] {
+				director.getCtClass(IOException.class.getName())
+		};
+		CtMethod newCtMethod = CtNewMethod.make(
+				mod, returnType, mname,
+				paramTypes, exceptTypes, mbody, tmplCtClass);
+		tmplCtClass.addMethod(newCtMethod);
+	}
+
+	protected void buildUnpackMethod() throws CannotCompileException, NotFoundException {
+		String mbody = buildUnpackMethodBody();
+		int mod = javassist.Modifier.PUBLIC;
+		CtClass returnType = director.getCtClass(Object.class.getName());
+		String mname = "unpack";
+		CtClass[] paramTypes = new CtClass[] {
+				director.getCtClass(Unpacker.class.getName()),
+				director.getCtClass(Object.class.getName())
+		};
+		CtClass[] exceptTypes = new CtClass[] {
+				director.getCtClass(MessageTypeException.class.getName())
+		};
+		CtMethod newCtMethod = CtNewMethod.make(
+				mod, returnType, mname,
+				paramTypes, exceptTypes, mbody, tmplCtClass);
+		tmplCtClass.addMethod(newCtMethod);
+	}
+
+	protected void buildConvertMethod() throws CannotCompileException, NotFoundException {
+		String mbody = buildConvertMethodBody();
+		int mod = javassist.Modifier.PUBLIC;
+		CtClass returnType = director.getCtClass(Object.class.getName());
+		String mname = "convert";
+		CtClass[] paramTypes = new CtClass[] {
+				director.getCtClass(MessagePackObject.class.getName()),
+				director.getCtClass(Object.class.getName())
+		};
+		CtClass[] exceptTypes = new CtClass[] {
+				director.getCtClass(MessageTypeException.class.getName())
+		};
+		CtMethod newCtMethod = CtNewMethod.make(
+				mod, returnType, mname,
+				paramTypes, exceptTypes, mbody, tmplCtClass);
+		tmplCtClass.addMethod(newCtMethod);
+	}
+
+	protected Class<?> createClass() throws CannotCompileException {
+		return (Class<?>) tmplCtClass.toClass(null, null);
+	}
+
+	protected StringBuilder stringBuilder = null;
+
+	protected void resetStringBuilder() {
+		stringBuilder = new StringBuilder();
+	}
+
+	protected void buildString(String str) {
+		stringBuilder.append(str);
+	}
+
+	protected void buildString(String format, Object... args) {
+		stringBuilder.append(String.format(format, args));
+	}
+
+	protected String getBuiltString() {
+		if(stringBuilder == null) {
+			return null;
+		}
+		return stringBuilder.toString();
+	}
+	
+	protected String primitivePackName(Class<?> type) {
+		if(type == boolean.class) {
+			return "packBoolean";
+		} else if(type == byte.class) {
+			return "packByte";
+		} else if(type == short.class) {
+			return "packShort";
+		} else if(type == int.class) {
+			return "packInt";
+		} else if(type == long.class) {
+			return "packLong";
+		} else if(type == float.class) {
+			return "packFloat";
+		} else if(type == double.class) {
+			return "packDouble";
+		}
+		return null;
+	}
+
+	protected String primitiveUnpackName(Class<?> type) {
+		if(type == boolean.class) {
+			return "unpackBoolean";
+		} else if(type == byte.class) {
+			return "unpackByte";
+		} else if(type == short.class) {
+			return "unpackShort";
+		} else if(type == int.class) {
+			return "unpackInt";
+		} else if(type == long.class) {
+			return "unpackLong";
+		} else if(type == float.class) {
+			return "unpackFloat";
+		} else if(type == double.class) {
+			return "unpackDouble";
+		}
+		return null;
+	}
+
+	protected String primitiveConvertName(Class<?> type) {
+		if(type == boolean.class) {
+			return "asBoolean";
+		} else if(type == byte.class) {
+			return "asByte";
+		} else if(type == short.class) {
+			return "asShort";
+		} else if(type == int.class) {
+			return "asInt";
+		} else if(type == long.class) {
+			return "asLong";
+		} else if(type == float.class) {
+			return "asFloat";
+		} else if(type == double.class) {
+			return "asDouble";
+		}
+		return null;
+	}
+}
\ No newline at end of file
diff --git a/java/src/main/java/org/msgpack/template/javassist/BuildContextFactory.java b/java/src/main/java/org/msgpack/template/javassist/BuildContextFactory.java
new file mode 100644
index 00000000..d44ed090
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/javassist/BuildContextFactory.java
@@ -0,0 +1,9 @@
+package org.msgpack.template.javassist;
+
+import org.msgpack.template.JavassistTemplateBuilder;
+
+public interface BuildContextFactory {
+	
+	public BuildContextBase createBuildContext(JavassistTemplateBuilder builder);
+
+}
diff --git a/java/src/main/java/org/msgpack/template/javassist/JavassistTemplate.java b/java/src/main/java/org/msgpack/template/javassist/JavassistTemplate.java
new file mode 100644
index 00000000..24e7082d
--- /dev/null
+++ b/java/src/main/java/org/msgpack/template/javassist/JavassistTemplate.java
@@ -0,0 +1,36 @@
+
+package org.msgpack.template.javassist;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Type;
+import java.lang.Thread;
+
+import org.msgpack.*;
+import org.msgpack.template.*;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.CtMethod;
+import javassist.CtNewConstructor;
+import javassist.CtNewMethod;
+import javassist.LoaderClassPath;
+import javassist.NotFoundException;
+import javassist.ClassClassPath;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class JavassistTemplate extends AbstractTemplate {
+	public Class<?> targetClass;
+	public Template[] templates;
+
+	public JavassistTemplate(Class<?> targetClass, Template[] templates) {
+		this.targetClass = targetClass;
+		this.templates = templates;
+	}
+}
\ No newline at end of file
diff --git a/java/src/test/java/org/msgpack/TestArrays.java b/java/src/test/java/org/msgpack/TestArrays.java
index 92bb44c2..40fa5d69 100644
--- a/java/src/test/java/org/msgpack/TestArrays.java
+++ b/java/src/test/java/org/msgpack/TestArrays.java
@@ -13,6 +13,10 @@ import org.junit.Test;
 import junit.framework.TestCase;
 
 public class TestArrays extends TestCase {
+	
+	
+	
+	
 	@MessagePackMessage
 	public static class PrimitiveTest {
 		public PrimitiveTest() { }
diff --git a/java/src/test/java/org/msgpack/template/BeansEntryReaderTest.java b/java/src/test/java/org/msgpack/template/BeansEntryReaderTest.java
new file mode 100644
index 00000000..78880582
--- /dev/null
+++ b/java/src/test/java/org/msgpack/template/BeansEntryReaderTest.java
@@ -0,0 +1,133 @@
+package org.msgpack.template;
+
+import java.beans.BeanDescriptor;
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.msgpack.template.BeansFieldEntryReader;
+
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.CoreMatchers.*;
+
+/**
+ * 
+ * @author takeshita
+ *
+ */
+public class BeansEntryReaderTest {
+	
+	public static class VariableProps{
+		
+		public int getCollect(){
+			return 0;
+		}
+		public void setCollect(int v){}
+		
+		public int getOnlyGetter(){
+			return 0;
+		}
+		
+		public void setOnlySetter(int v){}
+		
+		public boolean isBoolean(){
+			return true;
+		}
+		public void setBoolean(boolean b){}
+		
+		
+		private int getPrivateBoth(){return 1;}
+		private void setPrivateBoth(int v){}
+		
+		private int getPrivateGetter(){return 1;}
+		public void setPrivateGetter(int v){}
+		
+		public int getPrivateSetter(){return 1;}
+		private void setPrivateSetter(int v){}
+		
+		protected int getProtected(){return 1;}
+		protected void setProtected(int v){}
+
+		int getInternal(){return 1;}
+		void setInternal(int v){}
+		
+		public int getWrongGetter(int v){return 1;}
+		public void setWrongGetter(int v){}
+		
+		public void getWrongGetter2(){}
+		public void setWrongGetter2(int v){}
+		
+		public int isWrongGetter3(){return 1;}
+		public void setWrongGetter3(int v){}
+		
+		public int getWrongSetter(){return 1;}
+		public int setWrongSetter(int v){return 1;}
+		
+		public int getWrongSetter2(){return 1;}
+		public void setWrongSetter2(){}
+	}
+	
+	@Before
+	public void before(){
+		reader = new BeansFieldEntryReader();
+		
+		try {
+			info = Introspector.getBeanInfo(VariableProps.class);
+		} catch (IntrospectionException e) {
+			e.printStackTrace();
+			Assert.fail();
+		}
+	}
+	BeansFieldEntryReader reader;
+	BeanInfo info;
+	@Test
+	public void testIgnorePropertyDesc(){
+		BeanDescriptor desc = info.getBeanDescriptor();
+		
+		assertThat(reader.isIgnoreProp(getProp(info,"collect")),is(false));
+		assertThat(reader.isIgnoreProp(getProp(info,"boolean")),is(false));
+		
+
+		assertThat(reader.isIgnoreProp(getProp(info,"onlyGetter")),is(true));
+		assertThat(reader.isIgnoreProp(getProp(info,"onlySetter")),is(true));
+		assertThat(reader.isIgnoreProp(getProp(info,"privateBoth")),is(true));
+		assertThat(reader.isIgnoreProp(getProp(info,"privateGetter")),is(true));
+		assertThat(reader.isIgnoreProp(getProp(info,"privateSetter")),is(true));
+		assertThat(reader.isIgnoreProp(getProp(info,"protected")),is(true));
+		assertThat(reader.isIgnoreProp(getProp(info,"internal")),is(true));
+		assertThat(reader.isIgnoreProp(getProp(info,"wrongGetter")),is(true));
+		assertThat(reader.isIgnoreProp(getProp(info,"wrongGetter2")),is(true));
+		assertThat(reader.isIgnoreProp(getProp(info,"wrongGetter3")),is(true));
+		assertThat(reader.isIgnoreProp(getProp(info,"wrongSetter")),is(true));
+		assertThat(reader.isIgnoreProp(getProp(info,"wrongSetter2")),is(true));
+		
+	}
+	@Test
+	public void testReadEntries(){
+		
+		IFieldEntry[] entries = reader.readFieldEntries(VariableProps.class, FieldOption.DEFAULT);
+		
+		assertThat(entries.length, is(2));
+		
+		
+	}
+	
+	
+	public PropertyDescriptor getProp(BeanInfo info , String name){
+		PropertyDescriptor[] props = info.getPropertyDescriptors();
+		for(int i = 0;i < props.length;i++){
+			PropertyDescriptor d = props[i];
+			if(d.getDisplayName().equalsIgnoreCase(name)){
+				return d;
+			}
+		}
+		return null;
+	}
+	
+	
+
+}
diff --git a/java/src/test/java/org/msgpack/template/BeansEquals.java b/java/src/test/java/org/msgpack/template/BeansEquals.java
new file mode 100644
index 00000000..e07a224d
--- /dev/null
+++ b/java/src/test/java/org/msgpack/template/BeansEquals.java
@@ -0,0 +1,83 @@
+package org.msgpack.template;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.Description;
+import org.junit.Assert;
+
+/**
+ * This matcher compares all get***() methods(except getClass)
+ * @author takeshita
+ *
+ */
+public class BeansEquals extends BaseMatcher<Object>{
+	
+	Object expected;
+	
+	HashSet<String> ignoreNames = new HashSet<String>();
+	
+	public BeansEquals(Object expected){
+		this.expected = expected;
+	}
+	public BeansEquals(Object expected,String[] ignoreNames){
+		this.expected = expected;
+		for(int i = 0;i < ignoreNames.length;i++){
+			this.ignoreNames.add(ignoreNames[i]);
+		}
+	}
+	
+	static String errorMessage = "hoge";
+
+	@Override
+	public boolean matches(Object actual) {
+		if(expected == actual){
+			return true;
+		}
+		if(!actual.getClass().equals(expected.getClass())){
+			errorMessage = String.format("Expected class is %s but actual %s", 
+					expected.getClass().getName(),
+					actual.getClass().getName());
+			return false;
+		}
+		
+		for(Method m : expected.getClass().getMethods()){
+			String n = m.getName();
+			if(n.startsWith("get") &&
+					!n.equals("getClass") &&
+					!ignoreNames.contains(n)){
+				
+				if(m.getParameterTypes().length == 0 &&
+						!m.getReturnType().equals(void.class)){
+					try {
+						Object exp = m.invoke(expected);
+						Object act = m.invoke(actual);
+						
+						Assert.assertThat("@" + n,act, CoreMatchers.is(exp));
+						
+					} catch (Exception e) {
+						throw new RuntimeException(String.format(
+								"Exception occured while comparing %s",n), e);
+					} 
+					
+				}
+				
+			}
+			
+		}
+
+		return true;
+	}
+
+	
+	@Override
+	public void describeTo(Description desc) {
+		
+		desc.appendText(errorMessage);
+	}
+	
+
+}
diff --git a/java/src/test/java/org/msgpack/template/TestReflectionTemplateBuilderJavaBeansPackUnpack.java b/java/src/test/java/org/msgpack/template/TestReflectionTemplateBuilderJavaBeansPackUnpack.java
new file mode 100644
index 00000000..951b401c
--- /dev/null
+++ b/java/src/test/java/org/msgpack/template/TestReflectionTemplateBuilderJavaBeansPackUnpack.java
@@ -0,0 +1,2207 @@
+package org.msgpack.template;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.Matcher;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import org.msgpack.MessagePack;
+import org.msgpack.MessagePackable;
+import org.msgpack.MessagePacker;
+import org.msgpack.MessageTypeException;
+import org.msgpack.MessageUnpackable;
+import org.msgpack.Packer;
+import org.msgpack.Template;
+import org.msgpack.Unpacker;
+import org.msgpack.annotation.MessagePackBeans;
+import org.msgpack.annotation.MessagePackMessage;
+import org.msgpack.annotation.MessagePackOrdinalEnum;
+import org.msgpack.annotation.Optional;
+import org.msgpack.template.TestTemplateBuilderPackConvert.SampleInterface;
+import org.msgpack.template.builder.BuilderSelectorRegistry;
+import org.msgpack.template.builder.MessagePackBeansBuilderSelector;
+import org.msgpack.template.builder.MessagePackMessageBuilderSelector;
+import org.msgpack.template.builder.TemplateBuilder;
+
+import org.junit.Assert;
+import junit.framework.TestCase;
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.assertThat;
+
+public class TestReflectionTemplateBuilderJavaBeansPackUnpack extends TestCase {
+	static {
+		//Replace template selectors from javassist to reflection.
+		BuilderSelectorRegistry instance = BuilderSelectorRegistry.getInstance();
+
+		instance.replace(
+				new MessagePackMessageBuilderSelector(
+						new ReflectionTemplateBuilder()));
+		instance.setForceBuilder( new ReflectionTemplateBuilder());
+		instance.replace(new MessagePackBeansBuilderSelector(
+				new BeansReflectionTemplateBuilder()));
+		
+		MessagePack.register(PrimitiveTypeFieldsClass.class);
+		MessagePack.register(OptionalPrimitiveTypeFieldsClass.class);
+		MessagePack.register(GeneralReferenceTypeFieldsClass.class);
+		MessagePack.register(GeneralOptionalReferenceTypeFieldsClass.class);
+		MessagePack.register(SampleListTypes.class);
+		MessagePack.register(SampleOptionalListTypes.class);
+		MessagePack.register(SampleMapTypes.class);
+		MessagePack.register(SampleOptionalMapTypes.class);
+		MessagePack.register(SampleEnumFieldClass.class);
+		MessagePack.register(SampleOptionalEnumFieldClass.class);
+		MessagePack.register(FieldModifiersClass.class);
+		MessagePack.register(OptionalFieldModifiersClass.class);
+		MessagePack.register(BaseClass.class);
+		MessagePack.register(NestedClass.class);
+		MessagePack.register(BaseClass2.class);
+		MessagePack.register(OptionalBaseClass.class);
+		MessagePack.register(OptionalNestedClass.class);
+		MessagePack.register(OptionalBaseClass2.class);
+		MessagePack.register(SampleSubClass.class);
+		MessagePack.register(SampleSuperClass.class);
+		MessagePack.register(SampleOptionalSubClass.class);
+		MessagePack.register(SampleOptionalSuperClass.class);
+		MessagePack.register(BaseMessagePackableUnpackableClass.class);
+		MessagePack.register(MessagePackableUnpackableClass.class);
+		MessagePack.register(OptionalBaseMessagePackableUnpackableClass.class);
+		MessagePack.register(OptionalMessagePackableUnpackableClass.class);
+	}
+	
+	Matcher<Object> beansEquals(Object actual){
+		return new BeansEquals(actual); 
+	}
+	Matcher<Object> beansEquals(Object actual,String[] ignoreNames){
+		return new BeansEquals(actual,ignoreNames); 
+	}
+	String[] ignoring(String ... strings){
+		return strings;
+	}
+
+	@Test
+	public void testPrimitiveTypeFields00() throws Exception {
+		PrimitiveTypeFieldsClass src = new PrimitiveTypeFieldsClass();
+		src.f0 = (byte) 0;
+		src.f1 = 1;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f6 = false;
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, PrimitiveTypeFieldsClass.class);
+		
+		
+		assertThat(dst,beansEquals(src));
+	}
+
+	@Test
+	public void testPrimitiveTypeFields01() throws Exception {
+		PrimitiveTypeFieldsClass src = new PrimitiveTypeFieldsClass();
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, PrimitiveTypeFieldsClass.class);
+
+		assertThat(dst,beansEquals(src));
+	}
+
+	@Test
+	public void testPrimitiveTypeFields02() throws Exception {
+		PrimitiveTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, PrimitiveTypeFieldsClass.class);
+		
+		assertThat(dst,beansEquals(src));
+	}
+
+	@MessagePackBeans
+	public static class PrimitiveTypeFieldsClass {
+		byte f0;
+		short f1;
+		int f2;
+		long f3;
+		float f4;
+		double f5;
+		boolean f6;
+
+		public byte getF0() {
+			return f0;
+		}
+
+		public void setF0(byte f0) {
+			this.f0 = f0;
+		}
+
+		public short getF1() {
+			return f1;
+		}
+
+		public void setF1(short f1) {
+			this.f1 = f1;
+		}
+
+		public int getF2() {
+			return f2;
+		}
+
+		public void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		public long getF3() {
+			return f3;
+		}
+
+		public void setF3(long f3) {
+			this.f3 = f3;
+		}
+
+		public float getF4() {
+			return f4;
+		}
+
+		public void setF4(float f4) {
+			this.f4 = f4;
+		}
+
+		public double getF5() {
+			return f5;
+		}
+
+		public void setF5(double f5) {
+			this.f5 = f5;
+		}
+
+		public boolean isF6() {
+			return f6;
+		}
+
+		public void setF6(boolean f6) {
+			this.f6 = f6;
+		}
+
+		public PrimitiveTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testOptionalPrimitiveTypeFields00() throws Exception {
+		OptionalPrimitiveTypeFieldsClass src = new OptionalPrimitiveTypeFieldsClass();
+		src.f0 = (byte) 0;
+		src.f1 = 1;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f6 = false;
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, PrimitiveTypeFieldsClass.class);
+		
+
+		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);
+	}
+
+	@Test
+	public void testOptionalPrimitiveTypeFields01() throws Exception {
+		OptionalPrimitiveTypeFieldsClass src = new OptionalPrimitiveTypeFieldsClass();
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalPrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, OptionalPrimitiveTypeFieldsClass.class);
+		
+
+		assertThat(dst,beansEquals(src));
+	}
+
+	@Test
+	public void testOptionalPrimitiveTypeFields02() throws Exception {
+		OptionalPrimitiveTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalPrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, OptionalPrimitiveTypeFieldsClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class OptionalPrimitiveTypeFieldsClass {
+		@Optional
+		public byte f0;
+		@Optional
+		public short f1;
+		@Optional
+		public int f2;
+		@Optional
+		public long f3;
+		@Optional
+		public float f4;
+		@Optional
+		public double f5;
+		@Optional
+		public boolean f6;
+
+		@Optional
+		public byte getF0() {
+			return f0;
+		}
+
+		@Optional
+		public void setF0(byte f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public short getF1() {
+			return f1;
+		}
+
+		public void setF1(short f1) {
+			this.f1 = f1;
+		}
+
+		public int getF2() {
+			return f2;
+		}
+
+		@Optional
+		public void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		@Optional
+		public long getF3() {
+			return f3;
+		}
+
+		public void setF3(long f3) {
+			this.f3 = f3;
+		}
+
+		@Optional
+		public float getF4() {
+			return f4;
+		}
+
+		public void setF4(float f4) {
+			this.f4 = f4;
+		}
+
+		@Optional
+		public double getF5() {
+			return f5;
+		}
+
+		public void setF5(double f5) {
+			this.f5 = f5;
+		}
+
+		@Optional
+		public boolean isF6() {
+			return f6;
+		}
+
+		public void setF6(boolean f6) {
+			this.f6 = f6;
+		}
+
+		public OptionalPrimitiveTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testGeneralReferenceTypeFieldsClass00() throws Exception {
+		GeneralReferenceTypeFieldsClass src = new GeneralReferenceTypeFieldsClass();
+		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";
+		src.f9 = new byte[] { 0x01, 0x02 };
+		src.f10 = ByteBuffer.wrap("muga".getBytes());
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw, GeneralReferenceTypeFieldsClass.class);
+		
+
+		assertThat(dst,beansEquals(src));
+		
+	}
+
+	@Test
+	public void testGeneralReferenceTypeFieldsClass01() throws Exception {
+		GeneralReferenceTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw, GeneralReferenceTypeFieldsClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	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 byte[] f9;
+		public ByteBuffer f10;
+
+		public Byte getF0() {
+			return f0;
+		}
+
+		public void setF0(Byte f0) {
+			this.f0 = f0;
+		}
+
+		public Short getF1() {
+			return f1;
+		}
+
+		public void setF1(Short f1) {
+			this.f1 = f1;
+		}
+
+		public Integer getF2() {
+			return f2;
+		}
+
+		public void setF2(Integer f2) {
+			this.f2 = f2;
+		}
+
+		public Long getF3() {
+			return f3;
+		}
+
+		public void setF3(Long f3) {
+			this.f3 = f3;
+		}
+
+		public Float getF4() {
+			return f4;
+		}
+
+		public void setF4(Float f4) {
+			this.f4 = f4;
+		}
+
+		public Double getF5() {
+			return f5;
+		}
+
+		public void setF5(Double f5) {
+			this.f5 = f5;
+		}
+
+		public Boolean getF6() {
+			return f6;
+		}
+
+		public void setF6(Boolean f6) {
+			this.f6 = f6;
+		}
+
+		public BigInteger getF7() {
+			return f7;
+		}
+
+		public void setF7(BigInteger f7) {
+			this.f7 = f7;
+		}
+
+		public String getF8() {
+			return f8;
+		}
+
+		public void setF8(String f8) {
+			this.f8 = f8;
+		}
+
+		public byte[] getF9() {
+			return f9;
+		}
+
+		public void setF9(byte[] f9) {
+			this.f9 = f9;
+		}
+
+		public ByteBuffer getF10() {
+			return f10;
+		}
+
+		public void setF10(ByteBuffer f10) {
+			this.f10 = f10;
+		}
+
+		public GeneralReferenceTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testGeneralOptionalReferenceTypeFieldsClass00()
+			throws Exception {
+		GeneralOptionalReferenceTypeFieldsClass src = new GeneralOptionalReferenceTypeFieldsClass();
+		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";
+		src.f9 = new byte[] { 0x01, 0x02 };
+		src.f10 = ByteBuffer.wrap("muga".getBytes());
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralOptionalReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw, GeneralOptionalReferenceTypeFieldsClass.class);
+
+
+		assertThat(dst,beansEquals(src));
+	}
+
+	@Test
+	public void testGeneralOptionalReferenceTypeFieldsClass01()
+			throws Exception {
+		GeneralOptionalReferenceTypeFieldsClass src = new GeneralOptionalReferenceTypeFieldsClass();
+		src.f0 = null;
+		src.f1 = null;
+		src.f2 = null;
+		src.f3 = null;
+		src.f4 = null;
+		src.f5 = null;
+		src.f6 = null;
+		src.f7 = null;
+		src.f8 = null;
+		src.f9 = null;
+		src.f10 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralOptionalReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw, GeneralOptionalReferenceTypeFieldsClass.class);
+		
+
+		assertThat(dst,beansEquals(src));
+	}
+
+	@Test
+	public void testGeneralOptionalReferenceTypeFieldsClass02()
+			throws Exception {
+		GeneralOptionalReferenceTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralOptionalReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw, GeneralOptionalReferenceTypeFieldsClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class GeneralOptionalReferenceTypeFieldsClass {
+		@Optional
+		public Byte f0;
+		@Optional
+		public Short f1;
+		@Optional
+		public Integer f2;
+		@Optional
+		public Long f3;
+		@Optional
+		public Float f4;
+		@Optional
+		public Double f5;
+		@Optional
+		public Boolean f6;
+		@Optional
+		public BigInteger f7;
+		@Optional
+		public String f8;
+		@Optional
+		public byte[] f9;
+		@Optional
+		public ByteBuffer f10;
+
+		@Optional
+		public Byte getF0() {
+			return f0;
+		}
+
+		@Optional
+		public void setF0(Byte f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public Short getF1() {
+			return f1;
+		}
+
+		public void setF1(Short f1) {
+			this.f1 = f1;
+		}
+
+		public Integer getF2() {
+			return f2;
+		}
+
+		@Optional
+		public void setF2(Integer f2) {
+			this.f2 = f2;
+		}
+
+		@Optional
+		public Long getF3() {
+			return f3;
+		}
+
+		public void setF3(Long f3) {
+			this.f3 = f3;
+		}
+
+		@Optional
+		public Float getF4() {
+			return f4;
+		}
+
+		public void setF4(Float f4) {
+			this.f4 = f4;
+		}
+
+		@Optional
+		public Double getF5() {
+			return f5;
+		}
+
+		public void setF5(Double f5) {
+			this.f5 = f5;
+		}
+
+		@Optional
+		public Boolean getF6() {
+			return f6;
+		}
+
+		public void setF6(Boolean f6) {
+			this.f6 = f6;
+		}
+
+		@Optional
+		public BigInteger getF7() {
+			return f7;
+		}
+
+		public void setF7(BigInteger f7) {
+			this.f7 = f7;
+		}
+
+		@Optional
+		public String getF8() {
+			return f8;
+		}
+
+		public void setF8(String f8) {
+			this.f8 = f8;
+		}
+
+		@Optional
+		public byte[] getF9() {
+			return f9;
+		}
+
+		public void setF9(byte[] f9) {
+			this.f9 = f9;
+		}
+
+		@Optional
+		public ByteBuffer getF10() {
+			return f10;
+		}
+
+		public void setF10(ByteBuffer f10) {
+			this.f10 = f10;
+		}
+
+		public GeneralOptionalReferenceTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testListTypes00() throws Exception {
+		SampleListTypes src = new SampleListTypes();
+		src.integerListSize0 = new ArrayList<Integer>();
+		src.integerList = new ArrayList<Integer>();
+		src.integerList.add(1);
+		src.integerList.add(2);
+		src.integerList.add(3);
+		src.stringList = new ArrayList<String>();
+		src.stringList.add("e1");
+		src.stringList.add("e2");
+		src.stringList.add("e3");
+		src.stringListList = new ArrayList<List<String>>();
+		src.stringListList.add(src.stringList);
+		src.sampleListNestedTypeList = new ArrayList<SampleListNestedType>();
+		SampleListNestedType slnt = new SampleListNestedType();
+		slnt.f0 = new byte[] { 0x01, 0x02 };
+		slnt.f1 = "muga";
+		src.sampleListNestedTypeList.add(slnt);
+		src.byteBufferList = new ArrayList<ByteBuffer>();
+		src.byteBufferList.add(ByteBuffer.wrap("e1".getBytes()));
+		src.byteBufferList.add(ByteBuffer.wrap("e2".getBytes()));
+		src.byteBufferList.add(ByteBuffer.wrap("e3".getBytes()));
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleListTypes dst =
+			MessagePack.unpack(raw, SampleListTypes.class);
+
+		//ignore sampleListNestedTypeList,
+		//because SampleListNestedType is not implemented equals() correctly.
+		assertThat(dst,beansEquals(src, ignoring("getF4")));
+		
+		assertEquals(src.sampleListNestedTypeList.size(), dst.sampleListNestedTypeList.size());
+		for (int i = 0; i < src.sampleListNestedTypeList.size(); ++i) {
+			SampleListNestedType s = src.sampleListNestedTypeList.get(i);
+			SampleListNestedType d = dst.sampleListNestedTypeList.get(i);
+			assertEquals(s.f0[0], d.f0[0]);
+			assertEquals(s.f0[1], d.f0[1]);
+			assertEquals(s.f1, d.f1);
+		}
+	}
+
+	@Test
+	public void testListTypes01() throws Exception {
+		SampleListTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleListTypes dst =
+			MessagePack.unpack(raw, SampleListTypes.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class SampleListTypes {
+		public List<Integer> integerListSize0;
+		public List<Integer> integerList;
+		public List<String> stringList;
+		public List<List<String>> stringListList;
+		public List<SampleListNestedType> sampleListNestedTypeList;
+		public List<ByteBuffer> byteBufferList;
+
+		public List<Integer> getF0() {
+			return integerListSize0;
+		}
+
+		public void setF0(List<Integer> f0) {
+			this.integerListSize0 = f0;
+		}
+
+		public List<Integer> getF1() {
+			return integerList;
+		}
+
+		public void setF1(List<Integer> f1) {
+			this.integerList = f1;
+		}
+
+		public List<String> getF2() {
+			return stringList;
+		}
+
+		public void setF2(List<String> f2) {
+			this.stringList = f2;
+		}
+
+		public List<List<String>> getF3() {
+			return stringListList;
+		}
+
+		public void setF3(List<List<String>> f3) {
+			this.stringListList = f3;
+		}
+
+		public List<SampleListNestedType> getF4() {
+			return sampleListNestedTypeList;
+		}
+
+		public void setF4(List<SampleListNestedType> f4) {
+			this.sampleListNestedTypeList = f4;
+		}
+
+		public List<ByteBuffer> getF5() {
+			return byteBufferList;
+		}
+
+		public void setF5(List<ByteBuffer> f5) {
+			this.byteBufferList = f5;
+		}
+
+		public SampleListTypes() {
+		}
+	}
+
+	@MessagePackMessage
+	public static class SampleListNestedType {
+		public byte[] f0;
+		public String f1;
+
+		public SampleListNestedType() {
+		}
+	}
+
+	@Test
+	public void testOptionalListTypes00() throws Exception {
+		SampleOptionalListTypes src = new SampleOptionalListTypes();
+		src.f0 = new ArrayList<Integer>();
+		src.f1 = new ArrayList<Integer>();
+		src.f1.add(1);
+		src.f1.add(2);
+		src.f1.add(3);
+		src.f2 = new ArrayList<String>();
+		src.f2.add("e1");
+		src.f2.add("e2");
+		src.f2.add("e3");
+		src.f3 = new ArrayList<List<String>>();
+		src.f3.add(src.f2);
+		src.f4 = new ArrayList<SampleOptionalListNestedType>();
+		SampleOptionalListNestedType slnt = new SampleOptionalListNestedType();
+		slnt.f0 = new byte[] { 0x01, 0x02 };
+		slnt.f1 = "muga";
+		src.f4.add(slnt);
+		src.f5 = new ArrayList<ByteBuffer>();
+		src.f5.add(ByteBuffer.wrap("e1".getBytes()));
+		src.f5.add(ByteBuffer.wrap("e2".getBytes()));
+		src.f5.add(ByteBuffer.wrap("e3".getBytes()));
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalListTypes dst =
+			MessagePack.unpack(raw, SampleOptionalListTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1.size(), dst.f1.size());
+		for (int i = 0; i < src.f1.size(); ++i) {
+			assertEquals(src.f1.get(i), dst.f1.get(i));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		for (int i = 0; i < src.f2.size(); ++i) {
+			assertEquals(src.f2.get(i), dst.f2.get(i));
+		}
+		assertEquals(src.f3.size(), dst.f3.size());
+		for (int i = 0; i < src.f3.size(); ++i) {
+			List<String> srclist = src.f3.get(i);
+			List<String> dstlist = dst.f3.get(i);
+			assertEquals(srclist.size(), dstlist.size());
+			for (int j = 0; j < srclist.size(); ++j) {
+				assertEquals(srclist.get(j), dstlist.get(j));
+			}
+		}
+		assertEquals(src.f4.size(), dst.f4.size());
+		for (int i = 0; i < src.f4.size(); ++i) {
+			SampleOptionalListNestedType s = src.f4.get(i);
+			SampleOptionalListNestedType d = dst.f4.get(i);
+			assertEquals(s.f0[0], d.f0[0]);
+			assertEquals(s.f0[1], d.f0[1]);
+			assertEquals(s.f1, d.f1);
+		}
+		assertEquals(src.f5.size(), dst.f5.size());
+		for (int i = 0; i < src.f5.size(); ++i) {
+			ByteBuffer s = src.f5.get(i);
+			ByteBuffer d = dst.f5.get(i);
+			assertEquals(s, d);
+		}
+	}
+
+	@Test
+	public void testOptionalListTypes01() throws Exception {
+		SampleOptionalListTypes src = new SampleOptionalListTypes();
+		src.f0 = new ArrayList<Integer>();
+		src.f1 = null;
+		src.f2 = new ArrayList<String>();
+		src.f3 = new ArrayList<List<String>>();
+		src.f4 = null;
+		src.f5 = new ArrayList<ByteBuffer>();
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalListTypes dst =
+			MessagePack.unpack(raw, SampleOptionalListTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+		assertEquals(src.f3.size(), dst.f3.size());
+		assertEquals(src.f4, dst.f4);
+		assertEquals(src.f5.size(), dst.f5.size());
+	}
+
+	@Test
+	public void testOptionalListTypes02() throws Exception {
+		SampleListTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleListTypes dst =
+			MessagePack.unpack(raw, SampleListTypes.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class SampleOptionalListTypes {
+		@Optional
+		public List<Integer> f0;
+		@Optional
+		public List<Integer> f1;
+		@Optional
+		public List<String> f2;
+		@Optional
+		public List<List<String>> f3;
+		@Optional
+		public List<SampleOptionalListNestedType> f4;
+		@Optional
+		public List<ByteBuffer> f5;
+
+		@Optional
+		public List<Integer> getF0() {
+			return f0;
+		}
+
+		@Optional
+		public void setF0(List<Integer> f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public List<Integer> getF1() {
+			return f1;
+		}
+
+		public void setF1(List<Integer> f1) {
+			this.f1 = f1;
+		}
+
+		public List<String> getF2() {
+			return f2;
+		}
+
+		@Optional
+		public void setF2(List<String> f2) {
+			this.f2 = f2;
+		}
+
+		@Optional
+		public List<List<String>> getF3() {
+			return f3;
+		}
+
+		public void setF3(List<List<String>> f3) {
+			this.f3 = f3;
+		}
+
+		@Optional
+		public List<SampleOptionalListNestedType> getF4() {
+			return f4;
+		}
+
+		public void setF4(List<SampleOptionalListNestedType> f4) {
+			this.f4 = f4;
+		}
+
+		@Optional
+		public List<ByteBuffer> getF5() {
+			return f5;
+		}
+
+		public void setF5(List<ByteBuffer> f5) {
+			this.f5 = f5;
+		}
+
+		public SampleOptionalListTypes() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class SampleOptionalListNestedType {
+		@Optional
+		public byte[] f0;
+		@Optional
+		public String f1;
+
+		@Optional
+		public byte[] getF0() {
+			return f0;
+		}
+
+		public void setF0(byte[] f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public String getF1() {
+			return f1;
+		}
+
+		public void setF1(String f1) {
+			this.f1 = f1;
+		}
+
+		public SampleOptionalListNestedType() {
+		}
+	}
+
+	@Test
+	public void testMapTypes00() throws Exception {
+		SampleMapTypes src = new SampleMapTypes();
+		src.f0 = new HashMap<Integer, Integer>();
+		src.f1 = new HashMap<Integer, Integer>();
+		src.f1.put(1, 1);
+		src.f1.put(2, 2);
+		src.f1.put(3, 3);
+		src.f2 = new HashMap<String, Integer>();
+		src.f2.put("k1", 1);
+		src.f2.put("k2", 2);
+		src.f2.put("k3", 3);
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleMapTypes dst =
+			MessagePack.unpack(raw, SampleMapTypes.class);
+		Iterator<Integer> srcf1 = src.f1.keySet().iterator();
+		Iterator<Integer> dstf1 = dst.f1.keySet().iterator();
+		while (srcf1.hasNext()) {
+			Integer s1 = srcf1.next();
+			Integer d1 = dstf1.next();
+			assertEquals(s1, d1);
+			assertEquals(src.f1.get(s1), dst.f1.get(d1));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		Iterator<String> srcf2 = src.f2.keySet().iterator();
+		Iterator<String> dstf2 = dst.f2.keySet().iterator();
+		while (srcf2.hasNext()) {
+			String s2 = srcf2.next();
+			String d2 = dstf2.next();
+			assertEquals(s2, d2);
+			assertEquals(src.f2.get(s2), dst.f2.get(d2));
+		}
+	}
+
+	@Test
+	public void testMapTypes01() throws Exception {
+		SampleMapTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleMapTypes dst =
+			MessagePack.unpack(raw, SampleMapTypes.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class SampleMapTypes {
+		public Map<Integer, Integer> f0;
+		public Map<Integer, Integer> f1;
+		public Map<String, Integer> f2;
+
+		public Map<Integer, Integer> getF0() {
+			return f0;
+		}
+
+		public void setF0(Map<Integer, Integer> f0) {
+			this.f0 = f0;
+		}
+
+		public Map<Integer, Integer> getF1() {
+			return f1;
+		}
+
+		public void setF1(Map<Integer, Integer> f1) {
+			this.f1 = f1;
+		}
+
+		public Map<String, Integer> getF2() {
+			return f2;
+		}
+
+		public void setF2(Map<String, Integer> f2) {
+			this.f2 = f2;
+		}
+
+		public SampleMapTypes() {
+		}
+	}
+
+	@Test
+	public void testOptionalMapTypes00() throws Exception {
+		SampleOptionalMapTypes src = new SampleOptionalMapTypes();
+		src.f0 = new HashMap<Integer, Integer>();
+		src.f1 = new HashMap<Integer, Integer>();
+		src.f1.put(1, 1);
+		src.f1.put(2, 2);
+		src.f1.put(3, 3);
+		src.f2 = new HashMap<String, Integer>();
+		src.f2.put("k1", 1);
+		src.f2.put("k2", 2);
+		src.f2.put("k3", 3);
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalMapTypes dst =
+			MessagePack.unpack(raw, SampleOptionalMapTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1.size(), dst.f1.size());
+		Iterator<Integer> srcf1 = src.f1.keySet().iterator();
+		Iterator<Integer> dstf1 = dst.f1.keySet().iterator();
+		while (srcf1.hasNext()) {
+			Integer s1 = srcf1.next();
+			Integer d1 = dstf1.next();
+			assertEquals(s1, d1);
+			assertEquals(src.f1.get(s1), dst.f1.get(d1));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		Iterator<String> srcf2 = src.f2.keySet().iterator();
+		Iterator<String> dstf2 = dst.f2.keySet().iterator();
+		while (srcf2.hasNext()) {
+			String s2 = srcf2.next();
+			String d2 = dstf2.next();
+			assertEquals(s2, d2);
+			assertEquals(src.f2.get(s2), dst.f2.get(d2));
+		}
+	}
+
+	@Test
+	public void testOptionalMapTypes01() throws Exception {
+		SampleOptionalMapTypes src = new SampleOptionalMapTypes();
+		src.f0 = new HashMap<Integer, Integer>();
+		src.f1 = null;
+		src.f2 = new HashMap<String, Integer>();
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalMapTypes dst =
+			MessagePack.unpack(raw, SampleOptionalMapTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+	}
+
+	@Test
+	public void testOptionalMapTypes02() throws Exception {
+		SampleOptionalMapTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalMapTypes dst =
+			MessagePack.unpack(raw, SampleOptionalMapTypes.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class SampleOptionalMapTypes {
+		@Optional
+		public Map<Integer, Integer> f0;
+		@Optional
+		public Map<Integer, Integer> f1;
+		@Optional
+		public Map<String, Integer> f2;
+
+		@Optional
+		public Map<Integer, Integer> getF0() {
+			return f0;
+		}
+
+		public void setF0(Map<Integer, Integer> f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public Map<Integer, Integer> getF1() {
+			return f1;
+		}
+
+		public void setF1(Map<Integer, Integer> f1) {
+			this.f1 = f1;
+		}
+
+		@Optional
+		public Map<String, Integer> getF2() {
+			return f2;
+		}
+
+		public void setF2(Map<String, Integer> f2) {
+			this.f2 = f2;
+		}
+
+		public SampleOptionalMapTypes() {
+		}
+	}
+
+
+	@MessagePackBeans
+	public abstract static class AbstractModifierClass {
+	}
+
+	@Test
+	public void testInterfaceType00() throws Exception {
+		try {
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(SampleInterface.class);
+			Assert.assertNull(builder);
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(SampleInterface.class);
+			fail();
+		} catch (TemplateBuildException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+	}
+
+	@Test
+	public void testInterfaceType01() throws Exception {
+		try {
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(SampleInterface.class);
+			Assert.assertNull(builder);
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(SampleInterface.class);
+			fail();
+		} catch (TemplateBuildException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+	}
+
+	public interface SampleInterface {
+	}
+
+	@Test
+	public void testEnumTypeForOrdinal00() throws Exception {
+		SampleEnumFieldClass src = new SampleEnumFieldClass();
+		src.f0 = 23;
+		src.f1 = SampleEnum.ONE;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleEnumFieldClass dst =
+			MessagePack.unpack(raw, SampleEnumFieldClass.class);
+		Assert.assertThat(dst.f0, is(src.f0));
+		Assert.assertThat(dst.f1, is(src.f1));
+	}
+
+	@Test
+	public void testEnumTypeForOrdinal01() throws Exception {
+		SampleEnumFieldClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleEnumFieldClass dst =
+			MessagePack.unpack(raw, SampleEnumFieldClass.class);
+		Assert.assertThat(dst,is(src));
+	}
+
+	@MessagePackBeans
+	public static class SampleEnumFieldClass {
+		public int f0;
+
+		public SampleEnum f1;
+
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		public SampleEnum getF1() {
+			return f1;
+		}
+
+		public void setF1(SampleEnum f1) {
+			this.f1 = f1;
+		}
+
+		public SampleEnumFieldClass() {
+		}
+	}
+
+	@MessagePackOrdinalEnum
+	public enum SampleEnum {
+		ONE, TWO, THREE;
+	}
+
+	@Test
+	public void testOptionalEnumTypeForOrdinal00() throws Exception {
+		SampleOptionalEnumFieldClass src = new SampleOptionalEnumFieldClass();
+		src.f0 = 0;
+		src.f1 = SampleOptionalEnum.ONE;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalEnumFieldClass dst =
+			MessagePack.unpack(raw, SampleOptionalEnumFieldClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1 == dst.f1);
+	}
+
+	@Test
+	public void testOptionalEnumTypeForOrdinal01() throws Exception {
+		SampleOptionalEnumFieldClass src = new SampleOptionalEnumFieldClass();
+		src.f1 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalEnumFieldClass dst =
+			MessagePack.unpack(raw, SampleOptionalEnumFieldClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertEquals(src.f1, dst.f1);
+	}
+
+	@Test
+	public void testOptionalEnumTypeForOrdinal02() throws Exception {
+		SampleEnumFieldClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleEnumFieldClass dst =
+			MessagePack.unpack(raw, SampleEnumFieldClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class SampleOptionalEnumFieldClass {
+		@Optional
+		public int f0;
+
+		@Optional
+		public SampleOptionalEnum f1;
+
+		@Optional
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public SampleOptionalEnum getF1() {
+			return f1;
+		}
+
+		public void setF1(SampleOptionalEnum f1) {
+			this.f1 = f1;
+		}
+
+		public SampleOptionalEnumFieldClass() {
+		}
+	}
+
+	@MessagePackOrdinalEnum
+	public enum SampleOptionalEnum {
+		ONE, TWO, THREE;
+	}
+
+	@Test
+	public void testFieldModifiers() throws Exception {
+		FieldModifiersClass src = new FieldModifiersClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+
+		byte[] raw = MessagePack.pack(src);
+
+		FieldModifiersClass dst =
+			MessagePack.unpack(raw, FieldModifiersClass.class);
+		Assert.assertEquals(src.f1,dst.f1);
+		Assert.assertThat(dst.f2, is( not(src.f2)));
+		Assert.assertThat(dst.f3, is( not(src.f3)));
+		Assert.assertThat(dst.f4, is( not(src.f4)));
+	}
+
+	@MessagePackBeans
+	public static class FieldModifiersClass {
+		public int f0;
+		public final int f1 = 1;
+		private int f2;
+		protected int f3;
+		int f4;
+
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+		
+		private int getF2() {
+			return f2;
+		}
+
+		private void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		public int getF3() {
+			return f3;
+		}
+
+		protected void setF3(int f3) {
+			this.f3 = f3;
+		}
+
+		public int getF4() {
+			return f4;
+		}
+
+		void setF4(int f4) {
+			this.f4 = f4;
+		}
+
+		public int getF1() {
+			return f1;
+		}
+
+		public FieldModifiersClass() {
+		}
+	}
+
+	@Test
+	public void testOptionalFieldModifiers() throws Exception {
+		OptionalFieldModifiersClass src = new OptionalFieldModifiersClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalFieldModifiersClass dst =
+			MessagePack.unpack(raw, OptionalFieldModifiersClass.class);
+		Assert.assertThat(dst.f0, is(src.f0));
+		Assert.assertThat(dst.f1, is(src.f1));
+		Assert.assertThat(dst.f2, is(not(src.f2)));
+		Assert.assertThat(dst.f3, is(not(src.f3)));
+		Assert.assertThat(dst.f4, is(not(src.f4)));
+	}
+
+	@MessagePackBeans
+	public static class OptionalFieldModifiersClass {
+		@Optional
+		public int f0;
+		@Optional
+		public final int f1 = 1;
+		private int f2;
+		protected int f3;
+		int f4;
+
+		@Optional
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		private int getF2() {
+			return f2;
+		}
+
+		public void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		protected int getF3() {
+			return f3;
+		}
+
+		protected void setF3(int f3) {
+			this.f3 = f3;
+		}
+
+		public int getF4() {
+			return f4;
+		}
+
+		void setF4(int f4) {
+			this.f4 = f4;
+		}
+
+		public int getF1() {
+			return f1;
+		}
+
+		public OptionalFieldModifiersClass() {
+		}
+	}
+
+	@Test
+	public void testNestedFieldClass00() throws Exception {
+		BaseClass src = new BaseClass();
+		NestedClass src2 = new NestedClass();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass dst =
+			MessagePack.unpack(raw, BaseClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testNestedFieldClass01() throws Exception {
+		BaseClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass dst =
+			MessagePack.unpack(raw, BaseClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class BaseClass {
+		public int f0;
+		public NestedClass f1;
+
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		public NestedClass getF1() {
+			return f1;
+		}
+
+		public void setF1(NestedClass f1) {
+			this.f1 = f1;
+		}
+
+		public BaseClass() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class NestedClass {
+		public int f2;
+
+		public int getF2() {
+			return f2;
+		}
+
+		public void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		public NestedClass() {
+		}
+	}
+
+	@Test
+	public void testOptionalNestedFieldClass00() throws Exception {
+		OptionalBaseClass src = new OptionalBaseClass();
+		OptionalNestedClass src2 = new OptionalNestedClass();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass dst =
+			MessagePack.unpack(raw, OptionalBaseClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testOptionalNestedFieldClass01() throws Exception {
+		OptionalBaseClass src = new OptionalBaseClass();
+		src.f1 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass dst =
+			MessagePack.unpack(raw, OptionalBaseClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1 == dst.f1);
+	}
+
+	@Test
+	public void testOptionalNestedFieldClass02() throws Exception {
+		OptionalBaseClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass dst =
+			MessagePack.unpack(raw, OptionalBaseClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class OptionalBaseClass {
+		@Optional
+		public int f0;
+		@Optional
+		public OptionalNestedClass f1;
+
+		@Optional
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public OptionalNestedClass getF1() {
+			return f1;
+		}
+
+		public void setF1(OptionalNestedClass f1) {
+			this.f1 = f1;
+		}
+
+		public OptionalBaseClass() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class OptionalNestedClass {
+		@Optional
+		public int f2;
+
+		@Optional
+		public int getF2() {
+			return f2;
+		}
+
+		public void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		public OptionalNestedClass() {
+		}
+	}
+
+	@Test
+	public void testMessagePackMessageFieldClass00() throws Exception {
+		BaseClass2 src = new BaseClass2();
+		MessagePackMessageClass2 src2 = new MessagePackMessageClass2();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass2 dst =
+			MessagePack.unpack(raw, BaseClass2.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testMessagePackMessageFieldClass01() throws Exception {
+		BaseClass2 src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass2 dst =
+			MessagePack.unpack(raw, BaseClass2.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class BaseClass2 {
+		public int f0;
+		public MessagePackMessageClass2 f1;
+
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		public MessagePackMessageClass2 getF1() {
+			return f1;
+		}
+
+		public void setF1(MessagePackMessageClass2 f1) {
+			this.f1 = f1;
+		}
+
+		public BaseClass2() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class MessagePackMessageClass2 {
+		public int getF2() {
+			return f2;
+		}
+
+		public void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		public int f2;
+
+		public MessagePackMessageClass2() {
+		}
+	}
+
+	@Test
+	public void testOptionalMessagePackMessageFieldClass00() throws Exception {
+		OptionalBaseClass2 src = new OptionalBaseClass2();
+		OptionalMessagePackMessageClass2 src2 = new OptionalMessagePackMessageClass2();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass2 dst =
+			MessagePack.unpack(raw, OptionalBaseClass2.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testOptionalMessagePackMessageFieldClass01() throws Exception {
+		OptionalBaseClass2 src = new OptionalBaseClass2();
+		src.f1 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass2 dst =
+			MessagePack.unpack(raw, OptionalBaseClass2.class);
+		assertTrue(src.f0 == dst.f0);
+		assertEquals(src.f1, dst.f1);
+	}
+
+	@Test
+	public void testOptionalMessagePackMessageFieldClass02() throws Exception {
+		OptionalBaseClass2 src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass2 dst =
+			MessagePack.unpack(raw, OptionalBaseClass2.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class OptionalBaseClass2 {
+		@Optional
+		public int f0;
+		@Optional
+		public OptionalMessagePackMessageClass2 f1;
+
+		@Optional
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public OptionalMessagePackMessageClass2 getF1() {
+			return f1;
+		}
+
+		public void setF1(OptionalMessagePackMessageClass2 f1) {
+			this.f1 = f1;
+		}
+
+		public OptionalBaseClass2() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class OptionalMessagePackMessageClass2 {
+		@Optional
+		public int f2;
+
+		@Optional
+		public int getF2() {
+			return f2;
+		}
+
+		public void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		public OptionalMessagePackMessageClass2() {
+		}
+	}
+
+	@Test
+	public void testExtendedClass00() throws Exception {
+		SampleSubClass src = new SampleSubClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f8 = 8;
+		src.f9 = 9;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleSubClass dst =
+			MessagePack.unpack(raw, SampleSubClass.class);
+		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);
+	}
+
+	@Test
+	public void testExtendedClass01() throws Exception {
+		SampleSubClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleSubClass dst =
+			MessagePack.unpack(raw, SampleSubClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class SampleSubClass extends SampleSuperClass {
+		public int f0;
+		public final int f1 = 1;
+		private int f2;
+		protected int f3;
+		int f4;
+
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		public int getF1() {
+			return f1;
+		}
+
+		public SampleSubClass() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class SampleSuperClass {
+		public int f5;
+		public final int f6 = 2;
+		@SuppressWarnings("unused")
+		private int f7;
+		protected int f8;
+		int f9;
+
+		public int getF5() {
+			return f5;
+		}
+
+		public void setF5(int f5) {
+			this.f5 = f5;
+		}
+
+		public SampleSuperClass() {
+		}
+	}
+
+	@Test
+	public void testOptionalExtendedClass00() throws Exception {
+		SampleOptionalSubClass src = new SampleOptionalSubClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f8 = 8;
+		src.f9 = 9;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalSubClass dst =
+			MessagePack.unpack(raw, SampleOptionalSubClass.class);
+		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);
+	}
+
+	@Test
+	public void testOptionalExtendedClass01() throws Exception {
+		SampleOptionalSubClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalSubClass dst =
+			MessagePack.unpack(raw, SampleOptionalSubClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class SampleOptionalSubClass extends SampleOptionalSuperClass {
+		@Optional
+		public int f0;
+		public final int f1 = 1;
+		private int f2;
+		protected int f3;
+		int f4;
+		@Optional
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		public SampleOptionalSubClass() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class SampleOptionalSuperClass {
+		@Optional
+		public int getF5() {
+			return f5;
+		}
+
+		public void setF5(int f5) {
+			this.f5 = f5;
+		}
+
+		@Optional
+		public int f5;
+		public final int f6 = 2;
+		@SuppressWarnings("unused")
+		private int f7;
+		protected int f8;
+		int f9;
+
+		public SampleOptionalSuperClass() {
+		}
+	}
+
+	@Test
+	public void testMessagePackableUnpackableClass00() throws Exception {
+		BaseMessagePackableUnpackableClass src = new BaseMessagePackableUnpackableClass();
+		MessagePackableUnpackableClass src1 = new MessagePackableUnpackableClass();
+		List<MessagePackableUnpackableClass> src2 = new ArrayList<MessagePackableUnpackableClass>();
+		src1.f0 = 0;
+		src1.f1 = 1;
+		src.f0 = src1;
+		src.f1 = 1;
+		src2.add(src1);
+		src.f2 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw, BaseMessagePackableUnpackableClass.class);
+		assertEquals(src.f0.f0, dst.f0.f0);
+		assertEquals(src.f0.f1, dst.f0.f1);
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+		assertEquals(src.f2.get(0).f0, dst.f2.get(0).f0);
+		assertEquals(src.f2.get(0).f1, dst.f2.get(0).f1);
+	}
+
+	@Test
+	public void testMessagePackableUnpackableClass01() throws Exception {
+		BaseMessagePackableUnpackableClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw, BaseMessagePackableUnpackableClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class BaseMessagePackableUnpackableClass {
+		public MessagePackableUnpackableClass f0;
+		public int f1;
+		public List<MessagePackableUnpackableClass> f2;
+
+		public MessagePackableUnpackableClass getF0() {
+			return f0;
+		}
+
+		public void setF0(MessagePackableUnpackableClass f0) {
+			this.f0 = f0;
+		}
+
+		public int getF1() {
+			return f1;
+		}
+
+		public void setF1(int f1) {
+			this.f1 = f1;
+		}
+
+		public List<MessagePackableUnpackableClass> getF2() {
+			return f2;
+		}
+
+		public void setF2(List<MessagePackableUnpackableClass> f2) {
+			this.f2 = f2;
+		}
+
+		public BaseMessagePackableUnpackableClass() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class MessagePackableUnpackableClass implements
+			MessagePackable, MessageUnpackable {
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		public int getF1() {
+			return f1;
+		}
+
+		public void setF1(int f1) {
+			this.f1 = f1;
+		}
+
+		public int f0;
+		public int f1;
+
+		public MessagePackableUnpackableClass() {
+		}
+
+		@Override
+		public void messagePack(Packer packer) throws IOException {
+			packer.packArray(2);
+			packer.pack(f0);
+			packer.pack(f1);
+		}
+
+		@Override
+		public void messageUnpack(Unpacker unpacker) throws IOException,
+				MessageTypeException {
+			if (unpacker.tryUnpackNull()) {
+				return;
+			}
+			unpacker.unpackArray();
+			f0 = unpacker.unpackInt();
+			f1 = unpacker.unpackInt();
+		}
+	}
+
+	@Test
+	public void testOptionalMessagePackableUnpackableClass00() throws Exception {
+		OptionalBaseMessagePackableUnpackableClass src = new OptionalBaseMessagePackableUnpackableClass();
+		OptionalMessagePackableUnpackableClass src1 = new OptionalMessagePackableUnpackableClass();
+		List<OptionalMessagePackableUnpackableClass> src2 = new ArrayList<OptionalMessagePackableUnpackableClass>();
+		src1.f0 = 0;
+		src1.f1 = 1;
+		src.f0 = src1;
+		src.f1 = 1;
+		src2.add(src1);
+		src.f2 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw, OptionalBaseMessagePackableUnpackableClass.class);
+		assertEquals(src.f0.f0, dst.f0.f0);
+		assertEquals(src.f0.f1, dst.f0.f1);
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+		assertEquals(src.f2.get(0).f0, dst.f2.get(0).f0);
+		assertEquals(src.f2.get(0).f1, dst.f2.get(0).f1);
+	}
+
+	@Test
+	public void testOptionalMessagePackableUnpackableClass01() throws Exception {
+		OptionalBaseMessagePackableUnpackableClass src = new OptionalBaseMessagePackableUnpackableClass();
+		src.f0 = null;
+		src.f1 = 1;
+		src.f2 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw, OptionalBaseMessagePackableUnpackableClass.class);
+		assertEquals(src.f0, dst.f0);
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2, dst.f2);
+	}
+
+	@Test
+	public void testOptionalMessagePackableUnpackableClass02() throws Exception {
+		OptionalBaseMessagePackableUnpackableClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw, OptionalBaseMessagePackableUnpackableClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class OptionalBaseMessagePackableUnpackableClass {
+		@Optional
+		public OptionalMessagePackableUnpackableClass f0;
+		@Optional
+		public int f1;
+		@Optional
+		public List<OptionalMessagePackableUnpackableClass> f2;
+
+		@Optional
+		public OptionalMessagePackableUnpackableClass getF0() {
+			return f0;
+		}
+
+		public void setF0(OptionalMessagePackableUnpackableClass f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public int getF1() {
+			return f1;
+		}
+
+		public void setF1(int f1) {
+			this.f1 = f1;
+		}
+
+		@Optional
+		public List<OptionalMessagePackableUnpackableClass> getF2() {
+			return f2;
+		}
+
+		public void setF2(List<OptionalMessagePackableUnpackableClass> f2) {
+			this.f2 = f2;
+		}
+
+		public OptionalBaseMessagePackableUnpackableClass() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class OptionalMessagePackableUnpackableClass implements
+			MessagePackable, MessageUnpackable {
+		@Optional
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public int getF1() {
+			return f1;
+		}
+
+		public void setF1(int f1) {
+			this.f1 = f1;
+		}
+
+		@Optional
+		public int f0;
+		@Optional
+		public int f1;
+
+		public OptionalMessagePackableUnpackableClass() {
+		}
+
+		@Override
+		public void messagePack(Packer packer) throws IOException {
+			packer.packArray(2);
+			packer.pack(f0);
+			packer.pack(f1);
+		}
+
+		@Override
+		public void messageUnpack(Unpacker unpacker) throws IOException,
+				MessageTypeException {
+			if (unpacker.tryUnpackNull()) {
+				return;
+			}
+			unpacker.unpackArray();
+			f0 = unpacker.unpackInt();
+			f1 = unpacker.unpackInt();
+		}
+	}
+}
+
diff --git a/java/src/test/java/org/msgpack/template/TestReflectionTemplateBuilderPackConvert.java b/java/src/test/java/org/msgpack/template/TestReflectionTemplateBuilderPackConvert.java
new file mode 100644
index 00000000..dcb039f7
--- /dev/null
+++ b/java/src/test/java/org/msgpack/template/TestReflectionTemplateBuilderPackConvert.java
@@ -0,0 +1,1417 @@
+package org.msgpack.template;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import org.msgpack.MessagePack;
+import org.msgpack.MessagePackable;
+import org.msgpack.MessagePacker;
+import org.msgpack.MessageTypeException;
+import org.msgpack.MessageUnpackable;
+import org.msgpack.Packer;
+import org.msgpack.Template;
+import org.msgpack.Unpacker;
+import org.msgpack.annotation.MessagePackMessage;
+import org.msgpack.annotation.MessagePackOrdinalEnum;
+import org.msgpack.annotation.Optional;
+import org.msgpack.template.builder.BuilderSelectorRegistry;
+import org.msgpack.template.builder.MessagePackBeansBuilderSelector;
+import org.msgpack.template.builder.MessagePackMessageBuilderSelector;
+import org.msgpack.template.builder.TemplateBuilder;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+public class TestReflectionTemplateBuilderPackConvert extends TestCase {
+	static {
+		//Replace template selectors from javassist to reflection.
+		BuilderSelectorRegistry instance = BuilderSelectorRegistry.getInstance();
+
+		instance.replace(
+				new MessagePackMessageBuilderSelector(
+						new ReflectionTemplateBuilder()));
+		instance.setForceBuilder( new ReflectionTemplateBuilder());
+		instance.replace(new MessagePackBeansBuilderSelector(
+				new BeansReflectionTemplateBuilder()));
+		
+		MessagePack.register(PrimitiveTypeFieldsClass.class);
+		MessagePack.register(OptionalPrimitiveTypeFieldsClass.class);
+		MessagePack.register(GeneralReferenceTypeFieldsClass.class);
+		MessagePack.register(GeneralOptionalReferenceTypeFieldsClass.class);
+		MessagePack.register(SampleListTypes.class);
+		MessagePack.register(SampleOptionalListTypes.class);
+		MessagePack.register(SampleMapTypes.class);
+		MessagePack.register(SampleOptionalMapTypes.class);
+		MessagePack.register(SampleEnumFieldClass.class);
+		MessagePack.register(SampleOptionalEnumFieldClass.class);
+		MessagePack.register(FieldModifiersClass.class);
+		MessagePack.register(OptionalFieldModifiersClass.class);
+		MessagePack.register(BaseClass.class);
+		MessagePack.register(NestedClass.class);
+		MessagePack.register(BaseClass2.class);
+		MessagePack.register(OptionalBaseClass.class);
+		MessagePack.register(OptionalNestedClass.class);
+		MessagePack.register(OptionalBaseClass2.class);
+		MessagePack.register(SampleSubClass.class);
+		MessagePack.register(SampleSuperClass.class);
+		MessagePack.register(SampleOptionalSubClass.class);
+		MessagePack.register(SampleOptionalSuperClass.class);
+		MessagePack.register(BaseMessagePackableUnpackableClass.class);
+		MessagePack.register(MessagePackableUnpackableClass.class);
+		MessagePack.register(OptionalBaseMessagePackableUnpackableClass.class);
+		MessagePack.register(OptionalMessagePackableUnpackableClass.class);
+	}
+
+	@Test
+	public void testPrimitiveTypeFields00() throws Exception {
+		PrimitiveTypeFieldsClass src = new PrimitiveTypeFieldsClass();
+		src.f0 = (byte) 0;
+		src.f1 = 1;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f6 = false;
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw).convert(PrimitiveTypeFieldsClass.class);
+		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);
+	}
+
+	@Test
+	public void testPrimitiveTypeFields01() throws Exception {
+		PrimitiveTypeFieldsClass src = new PrimitiveTypeFieldsClass();
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw).convert(PrimitiveTypeFieldsClass.class);
+		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);
+	}
+
+	@Test
+	public void testPrimitiveTypeFields02() throws Exception {
+		PrimitiveTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw).convert(PrimitiveTypeFieldsClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class PrimitiveTypeFieldsClass {
+		public byte f0;
+		public short f1;
+		public int f2;
+		public long f3;
+		public float f4;
+		public double f5;
+		public boolean f6;
+
+		public PrimitiveTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testOptionalPrimitiveTypeFields00() throws Exception {
+		OptionalPrimitiveTypeFieldsClass src = new OptionalPrimitiveTypeFieldsClass();
+		src.f0 = (byte) 0;
+		src.f1 = 1;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f6 = false;
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw).convert(PrimitiveTypeFieldsClass.class);
+		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);
+	}
+
+	@Test
+	public void testOptionalPrimitiveTypeFields01() throws Exception {
+		OptionalPrimitiveTypeFieldsClass src = new OptionalPrimitiveTypeFieldsClass();
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalPrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw).convert(OptionalPrimitiveTypeFieldsClass.class);
+		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);
+	}
+
+	@Test
+	public void testOptionalPrimitiveTypeFields02() throws Exception {
+		OptionalPrimitiveTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalPrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw).convert(OptionalPrimitiveTypeFieldsClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class OptionalPrimitiveTypeFieldsClass {
+		@Optional
+		public byte f0;
+		@Optional
+		public short f1;
+		@Optional
+		public int f2;
+		@Optional
+		public long f3;
+		@Optional
+		public float f4;
+		@Optional
+		public double f5;
+		@Optional
+		public boolean f6;
+
+		public OptionalPrimitiveTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testGeneralReferenceTypeFieldsClass00() throws Exception {
+		GeneralReferenceTypeFieldsClass src = new GeneralReferenceTypeFieldsClass();
+		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";
+		src.f9 = new byte[] { 0x01, 0x02 };
+		src.f10 = ByteBuffer.wrap("muga".getBytes());
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw).convert(GeneralReferenceTypeFieldsClass.class);
+		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);
+		assertEquals(src.f9[0], dst.f9[0]);
+		assertEquals(src.f9[1], dst.f9[1]);
+		assertEquals(src.f10, dst.f10);
+	}
+
+	@Test
+	public void testGeneralReferenceTypeFieldsClass01() throws Exception {
+		GeneralReferenceTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw).convert(GeneralReferenceTypeFieldsClass.class);
+		assertEquals(src, dst);
+	}
+
+	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 byte[] f9;
+		public ByteBuffer f10;
+
+		public GeneralReferenceTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testGeneralOptionalReferenceTypeFieldsClass00()
+			throws Exception {
+		GeneralOptionalReferenceTypeFieldsClass src = new GeneralOptionalReferenceTypeFieldsClass();
+		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";
+		src.f9 = new byte[] { 0x01, 0x02 };
+		src.f10 = ByteBuffer.wrap("muga".getBytes());
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralOptionalReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw).convert(GeneralOptionalReferenceTypeFieldsClass.class);
+		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);
+		assertEquals(src.f9[0], dst.f9[0]);
+		assertEquals(src.f9[1], dst.f9[1]);
+		assertEquals(src.f10, dst.f10);
+	}
+
+	@Test
+	public void testGeneralOptionalReferenceTypeFieldsClass01()
+			throws Exception {
+		GeneralOptionalReferenceTypeFieldsClass src = new GeneralOptionalReferenceTypeFieldsClass();
+		src.f0 = null;
+		src.f1 = null;
+		src.f2 = null;
+		src.f3 = null;
+		src.f4 = null;
+		src.f5 = null;
+		src.f6 = null;
+		src.f7 = null;
+		src.f8 = null;
+		src.f9 = null;
+		src.f10 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralOptionalReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw).convert(GeneralOptionalReferenceTypeFieldsClass.class);
+		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);
+		assertEquals(src.f9, dst.f9);
+		assertEquals(src.f10, dst.f10);
+	}
+
+	@Test
+	public void testGeneralOptionalReferenceTypeFieldsClass02()
+			throws Exception {
+		GeneralOptionalReferenceTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralOptionalReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw).convert(GeneralOptionalReferenceTypeFieldsClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class GeneralOptionalReferenceTypeFieldsClass {
+		@Optional
+		public Byte f0;
+		@Optional
+		public Short f1;
+		@Optional
+		public Integer f2;
+		@Optional
+		public Long f3;
+		@Optional
+		public Float f4;
+		@Optional
+		public Double f5;
+		@Optional
+		public Boolean f6;
+		@Optional
+		public BigInteger f7;
+		@Optional
+		public String f8;
+		@Optional
+		public byte[] f9;
+		@Optional
+		public ByteBuffer f10;
+
+		public GeneralOptionalReferenceTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testListTypes00() throws Exception {
+		SampleListTypes src = new SampleListTypes();
+		src.f0 = new ArrayList<Integer>();
+		src.f1 = new ArrayList<Integer>();
+		src.f1.add(1);
+		src.f1.add(2);
+		src.f1.add(3);
+		src.f2 = new ArrayList<String>();
+		src.f2.add("e1");
+		src.f2.add("e2");
+		src.f2.add("e3");
+		src.f3 = new ArrayList<List<String>>();
+		src.f3.add(src.f2);
+		src.f4 = new ArrayList<SampleListNestedType>();
+		SampleListNestedType slnt = new SampleListNestedType();
+		slnt.f0 = new byte[] { 0x01, 0x02 };
+		slnt.f1 = "muga";
+		src.f4.add(slnt);
+		src.f5 = new ArrayList<ByteBuffer>();
+		src.f5.add(ByteBuffer.wrap("e1".getBytes()));
+		src.f5.add(ByteBuffer.wrap("e2".getBytes()));
+		src.f5.add(ByteBuffer.wrap("e3".getBytes()));
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleListTypes dst =
+			MessagePack.unpack(raw).convert(SampleListTypes.class);
+		for (int i = 0; i < src.f1.size(); ++i) {
+			assertEquals(src.f1.get(i), dst.f1.get(i));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		for (int i = 0; i < src.f2.size(); ++i) {
+			assertEquals(src.f2.get(i), dst.f2.get(i));
+		}
+		assertEquals(src.f3.size(), dst.f3.size());
+		for (int i = 0; i < src.f3.size(); ++i) {
+			List<String> srclist = src.f3.get(i);
+			List<String> dstlist = dst.f3.get(i);
+			assertEquals(srclist.size(), dstlist.size());
+			for (int j = 0; j < srclist.size(); ++j) {
+				assertEquals(srclist.get(j), dstlist.get(j));
+			}
+		}
+		assertEquals(src.f4.size(), dst.f4.size());
+		for (int i = 0; i < src.f4.size(); ++i) {
+			SampleListNestedType s = src.f4.get(i);
+			SampleListNestedType d = dst.f4.get(i);
+			assertEquals(s.f0[0], d.f0[0]);
+			assertEquals(s.f0[1], d.f0[1]);
+			assertEquals(s.f1, d.f1);
+		}
+		assertEquals(src.f5.size(), dst.f5.size());
+		for (int i = 0; i < src.f5.size(); ++i) {
+			ByteBuffer s = src.f5.get(i);
+			ByteBuffer d = dst.f5.get(i);
+			assertEquals(s, d);
+		}
+	}
+
+	@Test
+	public void testListTypes01() throws Exception {
+		SampleListTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleListTypes dst =
+			MessagePack.unpack(raw).convert(SampleListTypes.class);
+		assertEquals(src, dst);
+	}
+
+	public static class SampleListTypes {
+		public List<Integer> f0;
+		public List<Integer> f1;
+		public List<String> f2;
+		public List<List<String>> f3;
+		public List<SampleListNestedType> f4;
+		public List<ByteBuffer> f5;
+
+		public SampleListTypes() {
+		}
+	}
+
+	@MessagePackMessage
+	public static class SampleListNestedType {
+		public byte[] f0;
+		public String f1;
+
+		public SampleListNestedType() {
+		}
+	}
+
+	@Test
+	public void testOptionalListTypes00() throws Exception {
+		SampleOptionalListTypes src = new SampleOptionalListTypes();
+		src.f0 = new ArrayList<Integer>();
+		src.f1 = new ArrayList<Integer>();
+		src.f1.add(1);
+		src.f1.add(2);
+		src.f1.add(3);
+		src.f2 = new ArrayList<String>();
+		src.f2.add("e1");
+		src.f2.add("e2");
+		src.f2.add("e3");
+		src.f3 = new ArrayList<List<String>>();
+		src.f3.add(src.f2);
+		src.f4 = new ArrayList<SampleOptionalListNestedType>();
+		SampleOptionalListNestedType slnt = new SampleOptionalListNestedType();
+		slnt.f0 = new byte[] { 0x01, 0x02 };
+		slnt.f1 = "muga";
+		src.f4.add(slnt);
+		src.f5 = new ArrayList<ByteBuffer>();
+		src.f5.add(ByteBuffer.wrap("e1".getBytes()));
+		src.f5.add(ByteBuffer.wrap("e2".getBytes()));
+		src.f5.add(ByteBuffer.wrap("e3".getBytes()));
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalListTypes dst =
+			MessagePack.unpack(raw).convert(SampleOptionalListTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1.size(), dst.f1.size());
+		for (int i = 0; i < src.f1.size(); ++i) {
+			assertEquals(src.f1.get(i), dst.f1.get(i));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		for (int i = 0; i < src.f2.size(); ++i) {
+			assertEquals(src.f2.get(i), dst.f2.get(i));
+		}
+		assertEquals(src.f3.size(), dst.f3.size());
+		for (int i = 0; i < src.f3.size(); ++i) {
+			List<String> srclist = src.f3.get(i);
+			List<String> dstlist = dst.f3.get(i);
+			assertEquals(srclist.size(), dstlist.size());
+			for (int j = 0; j < srclist.size(); ++j) {
+				assertEquals(srclist.get(j), dstlist.get(j));
+			}
+		}
+		assertEquals(src.f4.size(), dst.f4.size());
+		for (int i = 0; i < src.f4.size(); ++i) {
+			SampleOptionalListNestedType s = src.f4.get(i);
+			SampleOptionalListNestedType d = dst.f4.get(i);
+			assertEquals(s.f0[0], d.f0[0]);
+			assertEquals(s.f0[1], d.f0[1]);
+			assertEquals(s.f1, d.f1);
+		}
+		assertEquals(src.f5.size(), dst.f5.size());
+		for (int i = 0; i < src.f5.size(); ++i) {
+			ByteBuffer s = src.f5.get(i);
+			ByteBuffer d = dst.f5.get(i);
+			assertEquals(s, d);
+		}
+	}
+
+	@Test
+	public void testOptionalListTypes01() throws Exception {
+		SampleOptionalListTypes src = new SampleOptionalListTypes();
+		src.f0 = new ArrayList<Integer>();
+		src.f1 = null;
+		src.f2 = new ArrayList<String>();
+		src.f3 = new ArrayList<List<String>>();
+		src.f4 = null;
+		src.f5 = new ArrayList<ByteBuffer>();
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalListTypes dst =
+			MessagePack.unpack(raw).convert(SampleOptionalListTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+		assertEquals(src.f3.size(), dst.f3.size());
+		assertEquals(src.f4, dst.f4);
+		assertEquals(src.f5.size(), dst.f5.size());
+	}
+
+	@Test
+	public void testOptionalListTypes02() throws Exception {
+		SampleListTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleListTypes dst =
+			MessagePack.unpack(raw).convert(SampleListTypes.class);
+		assertEquals(src, dst);
+	}
+
+	public static class SampleOptionalListTypes {
+		@Optional
+		public List<Integer> f0;
+		@Optional
+		public List<Integer> f1;
+		@Optional
+		public List<String> f2;
+		@Optional
+		public List<List<String>> f3;
+		@Optional
+		public List<SampleOptionalListNestedType> f4;
+		@Optional
+		public List<ByteBuffer> f5;
+
+		public SampleOptionalListTypes() {
+		}
+	}
+
+	@MessagePackMessage
+	public static class SampleOptionalListNestedType {
+		@Optional
+		public byte[] f0;
+		@Optional
+		public String f1;
+
+		public SampleOptionalListNestedType() {
+		}
+	}
+
+	@Test
+	public void testMapTypes00() throws Exception {
+		SampleMapTypes src = new SampleMapTypes();
+		src.f0 = new HashMap<Integer, Integer>();
+		src.f1 = new HashMap<Integer, Integer>();
+		src.f1.put(1, 1);
+		src.f1.put(2, 2);
+		src.f1.put(3, 3);
+		src.f2 = new HashMap<String, Integer>();
+		src.f2.put("k1", 1);
+		src.f2.put("k2", 2);
+		src.f2.put("k3", 3);
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleMapTypes dst =
+			MessagePack.unpack(raw).convert(SampleMapTypes.class);
+		Iterator<Integer> srcf1 = src.f1.keySet().iterator();
+		Iterator<Integer> dstf1 = dst.f1.keySet().iterator();
+		while (srcf1.hasNext()) {
+			Integer s1 = srcf1.next();
+			Integer d1 = dstf1.next();
+			assertEquals(s1, d1);
+			assertEquals(src.f1.get(s1), dst.f1.get(d1));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		Iterator<String> srcf2 = src.f2.keySet().iterator();
+		Iterator<String> dstf2 = dst.f2.keySet().iterator();
+		while (srcf2.hasNext()) {
+			String s2 = srcf2.next();
+			String d2 = dstf2.next();
+			assertEquals(s2, d2);
+			assertEquals(src.f2.get(s2), dst.f2.get(d2));
+		}
+	}
+
+	@Test
+	public void testMapTypes01() throws Exception {
+		SampleMapTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleMapTypes dst =
+			MessagePack.unpack(raw).convert(SampleMapTypes.class);
+		assertEquals(src, dst);
+	}
+
+	public static class SampleMapTypes {
+		public Map<Integer, Integer> f0;
+		public Map<Integer, Integer> f1;
+		public Map<String, Integer> f2;
+
+		public SampleMapTypes() {
+		}
+	}
+
+	@Test
+	public void testOptionalMapTypes00() throws Exception {
+		SampleOptionalMapTypes src = new SampleOptionalMapTypes();
+		src.f0 = new HashMap<Integer, Integer>();
+		src.f1 = new HashMap<Integer, Integer>();
+		src.f1.put(1, 1);
+		src.f1.put(2, 2);
+		src.f1.put(3, 3);
+		src.f2 = new HashMap<String, Integer>();
+		src.f2.put("k1", 1);
+		src.f2.put("k2", 2);
+		src.f2.put("k3", 3);
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalMapTypes dst =
+			MessagePack.unpack(raw).convert(SampleOptionalMapTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1.size(), dst.f1.size());
+		Iterator<Integer> srcf1 = src.f1.keySet().iterator();
+		Iterator<Integer> dstf1 = dst.f1.keySet().iterator();
+		while (srcf1.hasNext()) {
+			Integer s1 = srcf1.next();
+			Integer d1 = dstf1.next();
+			assertEquals(s1, d1);
+			assertEquals(src.f1.get(s1), dst.f1.get(d1));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		Iterator<String> srcf2 = src.f2.keySet().iterator();
+		Iterator<String> dstf2 = dst.f2.keySet().iterator();
+		while (srcf2.hasNext()) {
+			String s2 = srcf2.next();
+			String d2 = dstf2.next();
+			assertEquals(s2, d2);
+			assertEquals(src.f2.get(s2), dst.f2.get(d2));
+		}
+	}
+
+	@Test
+	public void testOptionalMapTypes01() throws Exception {
+		SampleOptionalMapTypes src = new SampleOptionalMapTypes();
+		src.f0 = new HashMap<Integer, Integer>();
+		src.f1 = null;
+		src.f2 = new HashMap<String, Integer>();
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalMapTypes dst =
+			MessagePack.unpack(raw).convert(SampleOptionalMapTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+	}
+
+	@Test
+	public void testOptionalMapTypes02() throws Exception {
+		SampleOptionalMapTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalMapTypes dst =
+			MessagePack.unpack(raw).convert(SampleOptionalMapTypes.class);
+		assertEquals(src, dst);
+	}
+
+	public static class SampleOptionalMapTypes {
+		@Optional
+		public Map<Integer, Integer> f0;
+		@Optional
+		public Map<Integer, Integer> f1;
+		@Optional
+		public Map<String, Integer> f2;
+
+		public SampleOptionalMapTypes() {
+		}
+	}
+
+	@Test
+	public void testFinalClass() throws Exception {
+		try {
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(FinalModifierClass.class);
+			Assert.assertNull(builder);// no available builder
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(FinalModifierClass.class);
+			assertTrue(true);
+		} catch (TemplateBuildException e) {
+			fail();
+		}
+		assertTrue(true);
+	}
+
+	public final static class FinalModifierClass {
+	}
+
+	public abstract static class AbstractModifierClass {
+	}
+
+	@Test
+	public void testInterfaceType00() throws Exception {
+		try {
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(SampleInterface.class);
+			Assert.assertNull(builder);// no available builder
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(SampleInterface.class);
+			fail();
+		} catch (TemplateBuildException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+	}
+
+	@Test
+	public void testInterfaceType01() throws Exception {
+		try {
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(SampleInterface.class);
+			Assert.assertNull(builder);// no available builder
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(SampleInterface.class);
+			fail();
+		} catch (TemplateBuildException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+	}
+
+	public interface SampleInterface {
+	}
+
+	@Test
+	public void testEnumTypeForOrdinal00() throws Exception {
+		SampleEnumFieldClass src = new SampleEnumFieldClass();
+		src.f0 = 0;
+		src.f1 = SampleEnum.ONE;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleEnumFieldClass dst =
+			MessagePack.unpack(raw).convert(SampleEnumFieldClass.class);
+		assertTrue(src.f1 == dst.f1);
+	}
+
+	@Test
+	public void testEnumTypeForOrdinal01() throws Exception {
+		SampleEnumFieldClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleEnumFieldClass dst =
+			MessagePack.unpack(raw).convert(SampleEnumFieldClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class SampleEnumFieldClass {
+		public int f0;
+
+		public SampleEnum f1;
+
+		public SampleEnumFieldClass() {
+		}
+	}
+
+	@MessagePackOrdinalEnum
+	public enum SampleEnum {
+		ONE, TWO, THREE;
+	}
+
+	@Test
+	public void testOptionalEnumTypeForOrdinal00() throws Exception {
+		SampleOptionalEnumFieldClass src = new SampleOptionalEnumFieldClass();
+		src.f0 = 0;
+		src.f1 = SampleOptionalEnum.ONE;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalEnumFieldClass dst =
+			MessagePack.unpack(raw).convert(SampleOptionalEnumFieldClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1 == dst.f1);
+	}
+
+	@Test
+	public void testOptionalEnumTypeForOrdinal01() throws Exception {
+		SampleOptionalEnumFieldClass src = new SampleOptionalEnumFieldClass();
+		src.f1 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalEnumFieldClass dst =
+			MessagePack.unpack(raw).convert(SampleOptionalEnumFieldClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertEquals(src.f1, dst.f1);
+	}
+
+	@Test
+	public void testOptionalEnumTypeForOrdinal02() throws Exception {
+		SampleEnumFieldClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleEnumFieldClass dst =
+			MessagePack.unpack(raw).convert(SampleEnumFieldClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class SampleOptionalEnumFieldClass {
+		@Optional
+		public int f0;
+
+		@Optional
+		public SampleOptionalEnum f1;
+
+		public SampleOptionalEnumFieldClass() {
+		}
+	}
+
+	@MessagePackOrdinalEnum
+	public enum SampleOptionalEnum {
+		ONE, TWO, THREE;
+	}
+
+	@Test
+	public void testFieldModifiers() throws Exception {
+		FieldModifiersClass src = new FieldModifiersClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+
+		byte[] raw = MessagePack.pack(src);
+
+		FieldModifiersClass dst =
+			MessagePack.unpack(raw).convert(FieldModifiersClass.class);
+		assertTrue(src.f1 == dst.f1);
+		assertTrue(src.f2 != dst.f2);
+		assertTrue(src.f3 != dst.f3);
+		assertTrue(src.f4 != dst.f4);
+	}
+
+	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 testOptionalFieldModifiers() throws Exception {
+		OptionalFieldModifiersClass src = new OptionalFieldModifiersClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalFieldModifiersClass dst =
+			MessagePack.unpack(raw).convert(OptionalFieldModifiersClass.class);
+		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);
+	}
+
+	public static class OptionalFieldModifiersClass {
+		@Optional
+		public int f0;
+		@Optional
+		public final int f1 = 1;
+		private int f2;
+		protected int f3;
+		int f4;
+
+		public OptionalFieldModifiersClass() {
+		}
+	}
+
+	@Test
+	public void testNestedFieldClass00() throws Exception {
+		BaseClass src = new BaseClass();
+		NestedClass src2 = new NestedClass();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass dst =
+			MessagePack.unpack(raw).convert(BaseClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testNestedFieldClass01() throws Exception {
+		BaseClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass dst =
+			MessagePack.unpack(raw).convert(BaseClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class BaseClass {
+		public int f0;
+		public NestedClass f1;
+
+		public BaseClass() {
+		}
+	}
+
+	public static class NestedClass {
+		public int f2;
+
+		public NestedClass() {
+		}
+	}
+
+	@Test
+	public void testOptionalNestedFieldClass00() throws Exception {
+		OptionalBaseClass src = new OptionalBaseClass();
+		OptionalNestedClass src2 = new OptionalNestedClass();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass dst =
+			MessagePack.unpack(raw).convert(OptionalBaseClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testOptionalNestedFieldClass01() throws Exception {
+		OptionalBaseClass src = new OptionalBaseClass();
+		src.f1 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass dst =
+			MessagePack.unpack(raw).convert(OptionalBaseClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1 == dst.f1);
+	}
+
+	@Test
+	public void testOptionalNestedFieldClass02() throws Exception {
+		OptionalBaseClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass dst =
+			MessagePack.unpack(raw).convert(OptionalBaseClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class OptionalBaseClass {
+		@Optional
+		public int f0;
+		@Optional
+		public OptionalNestedClass f1;
+
+		public OptionalBaseClass() {
+		}
+	}
+
+	public static class OptionalNestedClass {
+		@Optional
+		public int f2;
+
+		public OptionalNestedClass() {
+		}
+	}
+
+	@Test
+	public void testMessagePackMessageFieldClass00() throws Exception {
+		BaseClass2 src = new BaseClass2();
+		MessagePackMessageClass2 src2 = new MessagePackMessageClass2();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass2 dst =
+			MessagePack.unpack(raw).convert(BaseClass2.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testMessagePackMessageFieldClass01() throws Exception {
+		BaseClass2 src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass2 dst =
+			MessagePack.unpack(raw).convert(BaseClass2.class);
+		assertEquals(src, dst);
+	}
+
+	public static class BaseClass2 {
+		public int f0;
+		public MessagePackMessageClass2 f1;
+
+		public BaseClass2() {
+		}
+	}
+
+	@MessagePackMessage
+	public static class MessagePackMessageClass2 {
+		public int f2;
+
+		public MessagePackMessageClass2() {
+		}
+	}
+
+	@Test
+	public void testOptionalMessagePackMessageFieldClass00() throws Exception {
+		OptionalBaseClass2 src = new OptionalBaseClass2();
+		OptionalMessagePackMessageClass2 src2 = new OptionalMessagePackMessageClass2();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass2 dst =
+			MessagePack.unpack(raw).convert(OptionalBaseClass2.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testOptionalMessagePackMessageFieldClass01() throws Exception {
+		OptionalBaseClass2 src = new OptionalBaseClass2();
+		src.f1 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass2 dst =
+			MessagePack.unpack(raw).convert(OptionalBaseClass2.class);
+		assertTrue(src.f0 == dst.f0);
+		assertEquals(src.f1, dst.f1);
+	}
+
+	@Test
+	public void testOptionalMessagePackMessageFieldClass02() throws Exception {
+		OptionalBaseClass2 src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass2 dst =
+			MessagePack.unpack(raw).convert(OptionalBaseClass2.class);
+		assertEquals(src, dst);
+	}
+
+	public static class OptionalBaseClass2 {
+		@Optional
+		public int f0;
+		@Optional
+		public OptionalMessagePackMessageClass2 f1;
+
+		public OptionalBaseClass2() {
+		}
+	}
+
+	@MessagePackMessage
+	public static class OptionalMessagePackMessageClass2 {
+		@Optional
+		public int f2;
+
+		public OptionalMessagePackMessageClass2() {
+		}
+	}
+
+	@Test
+	public void testExtendedClass00() throws Exception {
+		SampleSubClass src = new SampleSubClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f8 = 8;
+		src.f9 = 9;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleSubClass dst =
+			MessagePack.unpack(raw).convert(SampleSubClass.class);
+		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);
+	}
+
+	@Test
+	public void testExtendedClass01() throws Exception {
+		SampleSubClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleSubClass dst =
+			MessagePack.unpack(raw).convert(SampleSubClass.class);
+		assertEquals(src, dst);
+	}
+
+	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;
+		@SuppressWarnings("unused")
+		private int f7;
+		protected int f8;
+		int f9;
+
+		public SampleSuperClass() {
+		}
+	}
+
+	@Test
+	public void testOptionalExtendedClass00() throws Exception {
+		SampleOptionalSubClass src = new SampleOptionalSubClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f8 = 8;
+		src.f9 = 9;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalSubClass dst =
+			MessagePack.unpack(raw).convert(SampleOptionalSubClass.class);
+		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);
+	}
+
+	@Test
+	public void testOptionalExtendedClass01() throws Exception {
+		SampleOptionalSubClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalSubClass dst =
+			MessagePack.unpack(raw).convert(SampleOptionalSubClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class SampleOptionalSubClass extends SampleOptionalSuperClass {
+		@Optional
+		public int f0;
+		public final int f1 = 1;
+		private int f2;
+		protected int f3;
+		int f4;
+
+		public SampleOptionalSubClass() {
+		}
+	}
+
+	public static class SampleOptionalSuperClass {
+		@Optional
+		public int f5;
+		public final int f6 = 2;
+		@SuppressWarnings("unused")
+		private int f7;
+		protected int f8;
+		int f9;
+
+		public SampleOptionalSuperClass() {
+		}
+	}
+
+	@Test
+	public void testMessagePackableUnpackableClass00() throws Exception {
+		BaseMessagePackableUnpackableClass src = new BaseMessagePackableUnpackableClass();
+		MessagePackableUnpackableClass src1 = new MessagePackableUnpackableClass();
+		List<MessagePackableUnpackableClass> src2 = new ArrayList<MessagePackableUnpackableClass>();
+		src1.f0 = 0;
+		src1.f1 = 1;
+		src.f0 = src1;
+		src.f1 = 1;
+		src2.add(src1);
+		src.f2 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw).convert(BaseMessagePackableUnpackableClass.class);
+		assertEquals(src.f0.f0, dst.f0.f0);
+		assertEquals(src.f0.f1, dst.f0.f1);
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+		assertEquals(src.f2.get(0).f0, dst.f2.get(0).f0);
+		assertEquals(src.f2.get(0).f1, dst.f2.get(0).f1);
+	}
+
+	@Test
+	public void testMessagePackableUnpackableClass01() throws Exception {
+		BaseMessagePackableUnpackableClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw).convert(BaseMessagePackableUnpackableClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class BaseMessagePackableUnpackableClass {
+		public MessagePackableUnpackableClass f0;
+		public int f1;
+		public List<MessagePackableUnpackableClass> f2;
+
+		public BaseMessagePackableUnpackableClass() {
+		}
+	}
+
+	public static class MessagePackableUnpackableClass implements
+			MessagePackable, MessageUnpackable {
+		public int f0;
+		public int f1;
+
+		public MessagePackableUnpackableClass() {
+		}
+
+		@Override
+		public void messagePack(Packer packer) throws IOException {
+			packer.packArray(2);
+			packer.pack(f0);
+			packer.pack(f1);
+		}
+
+		@Override
+		public void messageUnpack(Unpacker unpacker) throws IOException,
+				MessageTypeException {
+			if (unpacker.tryUnpackNull()) {
+				return;
+			}
+			unpacker.unpackArray();
+			f0 = unpacker.unpackInt();
+			f1 = unpacker.unpackInt();
+		}
+	}
+
+	@Test
+	public void testOptionalMessagePackableUnpackableClass00() throws Exception {
+		OptionalBaseMessagePackableUnpackableClass src = new OptionalBaseMessagePackableUnpackableClass();
+		OptionalMessagePackableUnpackableClass src1 = new OptionalMessagePackableUnpackableClass();
+		List<OptionalMessagePackableUnpackableClass> src2 = new ArrayList<OptionalMessagePackableUnpackableClass>();
+		src1.f0 = 0;
+		src1.f1 = 1;
+		src.f0 = src1;
+		src.f1 = 1;
+		src2.add(src1);
+		src.f2 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw).convert(OptionalBaseMessagePackableUnpackableClass.class);
+		assertEquals(src.f0.f0, dst.f0.f0);
+		assertEquals(src.f0.f1, dst.f0.f1);
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+		assertEquals(src.f2.get(0).f0, dst.f2.get(0).f0);
+		assertEquals(src.f2.get(0).f1, dst.f2.get(0).f1);
+	}
+
+	@Test
+	public void testOptionalMessagePackableUnpackableClass01() throws Exception {
+		OptionalBaseMessagePackableUnpackableClass src = new OptionalBaseMessagePackableUnpackableClass();
+		src.f0 = null;
+		src.f1 = 1;
+		src.f2 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw).convert(OptionalBaseMessagePackableUnpackableClass.class);
+		assertEquals(src.f0, dst.f0);
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2, dst.f2);
+	}
+
+	@Test
+	public void testOptionalMessagePackableUnpackableClass02() throws Exception {
+		OptionalBaseMessagePackableUnpackableClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw).convert(OptionalBaseMessagePackableUnpackableClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class OptionalBaseMessagePackableUnpackableClass {
+		@Optional
+		public OptionalMessagePackableUnpackableClass f0;
+		@Optional
+		public int f1;
+		@Optional
+		public List<OptionalMessagePackableUnpackableClass> f2;
+
+		public OptionalBaseMessagePackableUnpackableClass() {
+		}
+	}
+
+	public static class OptionalMessagePackableUnpackableClass implements
+			MessagePackable, MessageUnpackable {
+		@Optional
+		public int f0;
+		@Optional
+		public int f1;
+
+		public OptionalMessagePackableUnpackableClass() {
+		}
+
+		@Override
+		public void messagePack(Packer packer) throws IOException {
+			packer.packArray(2);
+			packer.pack(f0);
+			packer.pack(f1);
+		}
+
+		@Override
+		public void messageUnpack(Unpacker unpacker) throws IOException,
+				MessageTypeException {
+			if (unpacker.tryUnpackNull()) {
+				return;
+			}
+			unpacker.unpackArray();
+			f0 = unpacker.unpackInt();
+			f1 = unpacker.unpackInt();
+		}
+	}
+}
+
diff --git a/java/src/test/java/org/msgpack/template/TestReflectionTemplateBuilderPackUnpack.java b/java/src/test/java/org/msgpack/template/TestReflectionTemplateBuilderPackUnpack.java
new file mode 100644
index 00000000..6080df6e
--- /dev/null
+++ b/java/src/test/java/org/msgpack/template/TestReflectionTemplateBuilderPackUnpack.java
@@ -0,0 +1,1422 @@
+package org.msgpack.template;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import org.msgpack.MessagePack;
+import org.msgpack.MessagePackable;
+import org.msgpack.MessagePacker;
+import org.msgpack.MessageTypeException;
+import org.msgpack.MessageUnpackable;
+import org.msgpack.Packer;
+import org.msgpack.Template;
+import org.msgpack.Unpacker;
+import org.msgpack.annotation.MessagePackMessage;
+import org.msgpack.annotation.MessagePackOrdinalEnum;
+import org.msgpack.annotation.Optional;
+import org.msgpack.template.TestTemplateBuilderPackConvert.SampleInterface;
+import org.msgpack.template.builder.BuilderSelectorRegistry;
+import org.msgpack.template.builder.MessagePackBeansBuilderSelector;
+import org.msgpack.template.builder.MessagePackMessageBuilderSelector;
+import org.msgpack.template.builder.TemplateBuilder;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import static org.junit.Assert.assertThat;
+
+public class TestReflectionTemplateBuilderPackUnpack extends TestCase {
+	static {
+		//Replace template selectors from javassist to reflection.
+		BuilderSelectorRegistry instance = BuilderSelectorRegistry.getInstance();
+
+		instance.replace(
+				new MessagePackMessageBuilderSelector(
+						new ReflectionTemplateBuilder()));
+		instance.setForceBuilder( new ReflectionTemplateBuilder());
+		instance.replace(new MessagePackBeansBuilderSelector(
+				new BeansReflectionTemplateBuilder()));
+		
+		
+		MessagePack.register(PrimitiveTypeFieldsClass.class);
+		MessagePack.register(OptionalPrimitiveTypeFieldsClass.class);
+		MessagePack.register(GeneralReferenceTypeFieldsClass.class);
+		MessagePack.register(GeneralOptionalReferenceTypeFieldsClass.class);
+		MessagePack.register(SampleListTypes.class);
+		MessagePack.register(SampleOptionalListTypes.class);
+		MessagePack.register(SampleMapTypes.class);
+		MessagePack.register(SampleOptionalMapTypes.class);
+		MessagePack.register(SampleEnumFieldClass.class);
+		MessagePack.register(SampleOptionalEnumFieldClass.class);
+		MessagePack.register(FieldModifiersClass.class);
+		MessagePack.register(OptionalFieldModifiersClass.class);
+		MessagePack.register(BaseClass.class);
+		MessagePack.register(NestedClass.class);
+		MessagePack.register(BaseClass2.class);
+		MessagePack.register(OptionalBaseClass.class);
+		MessagePack.register(OptionalNestedClass.class);
+		MessagePack.register(OptionalBaseClass2.class);
+		MessagePack.register(SampleSubClass.class);
+		MessagePack.register(SampleSuperClass.class);
+		MessagePack.register(SampleOptionalSubClass.class);
+		MessagePack.register(SampleOptionalSuperClass.class);
+		MessagePack.register(BaseMessagePackableUnpackableClass.class);
+		MessagePack.register(MessagePackableUnpackableClass.class);
+		MessagePack.register(OptionalBaseMessagePackableUnpackableClass.class);
+		MessagePack.register(OptionalMessagePackableUnpackableClass.class);
+	}
+
+	@Test
+	public void testPrimitiveTypeFields00() throws Exception {
+		PrimitiveTypeFieldsClass src = new PrimitiveTypeFieldsClass();
+		src.f0 = (byte) 0;
+		src.f1 = 1;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f6 = false;
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, PrimitiveTypeFieldsClass.class);
+		
+		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);
+	}
+
+	@Test
+	public void testPrimitiveTypeFields01() throws Exception {
+		PrimitiveTypeFieldsClass src = new PrimitiveTypeFieldsClass();
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, PrimitiveTypeFieldsClass.class);
+		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);
+	}
+
+	@Test
+	public void testPrimitiveTypeFields02() throws Exception {
+		PrimitiveTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, PrimitiveTypeFieldsClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class PrimitiveTypeFieldsClass {
+		public byte f0;
+		public short f1;
+		public int f2;
+		public long f3;
+		public float f4;
+		public double f5;
+		public boolean f6;
+
+		public PrimitiveTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testOptionalPrimitiveTypeFields00() throws Exception {
+		OptionalPrimitiveTypeFieldsClass src = new OptionalPrimitiveTypeFieldsClass();
+		src.f0 = (byte) 0;
+		src.f1 = 1;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f6 = false;
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, PrimitiveTypeFieldsClass.class);
+		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);
+	}
+
+	@Test
+	public void testOptionalPrimitiveTypeFields01() throws Exception {
+		OptionalPrimitiveTypeFieldsClass src = new OptionalPrimitiveTypeFieldsClass();
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalPrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, OptionalPrimitiveTypeFieldsClass.class);
+		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);
+	}
+
+	@Test
+	public void testOptionalPrimitiveTypeFields02() throws Exception {
+		OptionalPrimitiveTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalPrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, OptionalPrimitiveTypeFieldsClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class OptionalPrimitiveTypeFieldsClass {
+		@Optional
+		public byte f0;
+		@Optional
+		public short f1;
+		@Optional
+		public int f2;
+		@Optional
+		public long f3;
+		@Optional
+		public float f4;
+		@Optional
+		public double f5;
+		@Optional
+		public boolean f6;
+
+		public OptionalPrimitiveTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testGeneralReferenceTypeFieldsClass00() throws Exception {
+		GeneralReferenceTypeFieldsClass src = new GeneralReferenceTypeFieldsClass();
+		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";
+		src.f9 = new byte[] { 0x01, 0x02 };
+		src.f10 = ByteBuffer.wrap("muga".getBytes());
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw, GeneralReferenceTypeFieldsClass.class);
+		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);
+		assertEquals(src.f9[0], dst.f9[0]);
+		assertEquals(src.f9[1], dst.f9[1]);
+		assertEquals(src.f10, dst.f10);
+	}
+
+	@Test
+	public void testGeneralReferenceTypeFieldsClass01() throws Exception {
+		GeneralReferenceTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw, GeneralReferenceTypeFieldsClass.class);
+		assertEquals(src, dst);
+	}
+
+	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 byte[] f9;
+		public ByteBuffer f10;
+
+		public GeneralReferenceTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testGeneralOptionalReferenceTypeFieldsClass00()
+			throws Exception {
+		GeneralOptionalReferenceTypeFieldsClass src = new GeneralOptionalReferenceTypeFieldsClass();
+		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";
+		src.f9 = new byte[] { 0x01, 0x02 };
+		src.f10 = ByteBuffer.wrap("muga".getBytes());
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralOptionalReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw, GeneralOptionalReferenceTypeFieldsClass.class);
+		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);
+		assertEquals(src.f9[0], dst.f9[0]);
+		assertEquals(src.f9[1], dst.f9[1]);
+		assertEquals(src.f10, dst.f10);
+	}
+
+	@Test
+	public void testGeneralOptionalReferenceTypeFieldsClass01()
+			throws Exception {
+		GeneralOptionalReferenceTypeFieldsClass src = new GeneralOptionalReferenceTypeFieldsClass();
+		src.f0 = null;
+		src.f1 = null;
+		src.f2 = null;
+		src.f3 = null;
+		src.f4 = null;
+		src.f5 = null;
+		src.f6 = null;
+		src.f7 = null;
+		src.f8 = null;
+		src.f9 = null;
+		src.f10 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralOptionalReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw, GeneralOptionalReferenceTypeFieldsClass.class);
+		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);
+		assertEquals(src.f9, dst.f9);
+		assertEquals(src.f10, dst.f10);
+	}
+
+	@Test
+	public void testGeneralOptionalReferenceTypeFieldsClass02()
+			throws Exception {
+		GeneralOptionalReferenceTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralOptionalReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw, GeneralOptionalReferenceTypeFieldsClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class GeneralOptionalReferenceTypeFieldsClass {
+		@Optional
+		public Byte f0;
+		@Optional
+		public Short f1;
+		@Optional
+		public Integer f2;
+		@Optional
+		public Long f3;
+		@Optional
+		public Float f4;
+		@Optional
+		public Double f5;
+		@Optional
+		public Boolean f6;
+		@Optional
+		public BigInteger f7;
+		@Optional
+		public String f8;
+		@Optional
+		public byte[] f9;
+		@Optional
+		public ByteBuffer f10;
+
+		public GeneralOptionalReferenceTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testListTypes00() throws Exception {
+		SampleListTypes src = new SampleListTypes();
+		src.f0 = new ArrayList<Integer>();
+		src.f1 = new ArrayList<Integer>();
+		src.f1.add(1);
+		src.f1.add(2);
+		src.f1.add(3);
+		src.f2 = new ArrayList<String>();
+		src.f2.add("e1");
+		src.f2.add("e2");
+		src.f2.add("e3");
+		src.f3 = new ArrayList<List<String>>();
+		src.f3.add(src.f2);
+		src.f4 = new ArrayList<SampleListNestedType>();
+		SampleListNestedType slnt = new SampleListNestedType();
+		slnt.f0 = new byte[] { 0x01, 0x02 };
+		slnt.f1 = "muga";
+		src.f4.add(slnt);
+		src.f5 = new ArrayList<ByteBuffer>();
+		src.f5.add(ByteBuffer.wrap("e1".getBytes()));
+		src.f5.add(ByteBuffer.wrap("e2".getBytes()));
+		src.f5.add(ByteBuffer.wrap("e3".getBytes()));
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleListTypes dst =
+			MessagePack.unpack(raw, SampleListTypes.class);
+		for (int i = 0; i < src.f1.size(); ++i) {
+			assertEquals(src.f1.get(i), dst.f1.get(i));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		for (int i = 0; i < src.f2.size(); ++i) {
+			assertEquals(src.f2.get(i), dst.f2.get(i));
+		}
+		assertEquals(src.f3.size(), dst.f3.size());
+		for (int i = 0; i < src.f3.size(); ++i) {
+			List<String> srclist = src.f3.get(i);
+			List<String> dstlist = dst.f3.get(i);
+			assertEquals(srclist.size(), dstlist.size());
+			for (int j = 0; j < srclist.size(); ++j) {
+				assertEquals(srclist.get(j), dstlist.get(j));
+			}
+		}
+		assertEquals(src.f4.size(), dst.f4.size());
+		for (int i = 0; i < src.f4.size(); ++i) {
+			SampleListNestedType s = src.f4.get(i);
+			SampleListNestedType d = dst.f4.get(i);
+			assertEquals(s.f0[0], d.f0[0]);
+			assertEquals(s.f0[1], d.f0[1]);
+			assertEquals(s.f1, d.f1);
+		}
+		assertEquals(src.f5.size(), dst.f5.size());
+		for (int i = 0; i < src.f5.size(); ++i) {
+			ByteBuffer s = src.f5.get(i);
+			ByteBuffer d = dst.f5.get(i);
+			assertEquals(s, d);
+		}
+	}
+
+	@Test
+	public void testListTypes01() throws Exception {
+		SampleListTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleListTypes dst =
+			MessagePack.unpack(raw, SampleListTypes.class);
+		assertEquals(src, dst);
+	}
+
+	public static class SampleListTypes {
+		public List<Integer> f0;
+		public List<Integer> f1;
+		public List<String> f2;
+		public List<List<String>> f3;
+		public List<SampleListNestedType> f4;
+		public List<ByteBuffer> f5;
+
+		public SampleListTypes() {
+		}
+	}
+
+	@MessagePackMessage
+	public static class SampleListNestedType {
+		public byte[] f0;
+		public String f1;
+
+		public SampleListNestedType() {
+		}
+	}
+
+	@Test
+	public void testOptionalListTypes00() throws Exception {
+		SampleOptionalListTypes src = new SampleOptionalListTypes();
+		src.f0 = new ArrayList<Integer>();
+		src.f1 = new ArrayList<Integer>();
+		src.f1.add(1);
+		src.f1.add(2);
+		src.f1.add(3);
+		src.f2 = new ArrayList<String>();
+		src.f2.add("e1");
+		src.f2.add("e2");
+		src.f2.add("e3");
+		src.f3 = new ArrayList<List<String>>();
+		src.f3.add(src.f2);
+		src.f4 = new ArrayList<SampleOptionalListNestedType>();
+		SampleOptionalListNestedType slnt = new SampleOptionalListNestedType();
+		slnt.f0 = new byte[] { 0x01, 0x02 };
+		slnt.f1 = "muga";
+		src.f4.add(slnt);
+		src.f5 = new ArrayList<ByteBuffer>();
+		src.f5.add(ByteBuffer.wrap("e1".getBytes()));
+		src.f5.add(ByteBuffer.wrap("e2".getBytes()));
+		src.f5.add(ByteBuffer.wrap("e3".getBytes()));
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalListTypes dst =
+			MessagePack.unpack(raw, SampleOptionalListTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1.size(), dst.f1.size());
+		for (int i = 0; i < src.f1.size(); ++i) {
+			assertEquals(src.f1.get(i), dst.f1.get(i));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		for (int i = 0; i < src.f2.size(); ++i) {
+			assertEquals(src.f2.get(i), dst.f2.get(i));
+		}
+		assertEquals(src.f3.size(), dst.f3.size());
+		for (int i = 0; i < src.f3.size(); ++i) {
+			List<String> srclist = src.f3.get(i);
+			List<String> dstlist = dst.f3.get(i);
+			assertEquals(srclist.size(), dstlist.size());
+			for (int j = 0; j < srclist.size(); ++j) {
+				assertEquals(srclist.get(j), dstlist.get(j));
+			}
+		}
+		assertEquals(src.f4.size(), dst.f4.size());
+		for (int i = 0; i < src.f4.size(); ++i) {
+			SampleOptionalListNestedType s = src.f4.get(i);
+			SampleOptionalListNestedType d = dst.f4.get(i);
+			assertEquals(s.f0[0], d.f0[0]);
+			assertEquals(s.f0[1], d.f0[1]);
+			assertEquals(s.f1, d.f1);
+		}
+		assertEquals(src.f5.size(), dst.f5.size());
+		for (int i = 0; i < src.f5.size(); ++i) {
+			ByteBuffer s = src.f5.get(i);
+			ByteBuffer d = dst.f5.get(i);
+			assertEquals(s, d);
+		}
+	}
+
+	@Test
+	public void testOptionalListTypes01() throws Exception {
+		SampleOptionalListTypes src = new SampleOptionalListTypes();
+		src.f0 = new ArrayList<Integer>();
+		src.f1 = null;
+		src.f2 = new ArrayList<String>();
+		src.f3 = new ArrayList<List<String>>();
+		src.f4 = null;
+		src.f5 = new ArrayList<ByteBuffer>();
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalListTypes dst =
+			MessagePack.unpack(raw, SampleOptionalListTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+		assertEquals(src.f3.size(), dst.f3.size());
+		assertEquals(src.f4, dst.f4);
+		assertEquals(src.f5.size(), dst.f5.size());
+	}
+
+	@Test
+	public void testOptionalListTypes02() throws Exception {
+		SampleListTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleListTypes dst =
+			MessagePack.unpack(raw, SampleListTypes.class);
+		assertEquals(src, dst);
+	}
+
+	public static class SampleOptionalListTypes {
+		@Optional
+		public List<Integer> f0;
+		@Optional
+		public List<Integer> f1;
+		@Optional
+		public List<String> f2;
+		@Optional
+		public List<List<String>> f3;
+		@Optional
+		public List<SampleOptionalListNestedType> f4;
+		@Optional
+		public List<ByteBuffer> f5;
+
+		public SampleOptionalListTypes() {
+		}
+	}
+
+	@MessagePackMessage
+	public static class SampleOptionalListNestedType {
+		@Optional
+		public byte[] f0;
+		@Optional
+		public String f1;
+
+		public SampleOptionalListNestedType() {
+		}
+	}
+
+	@Test
+	public void testMapTypes00() throws Exception {
+		SampleMapTypes src = new SampleMapTypes();
+		src.f0 = new HashMap<Integer, Integer>();
+		src.f1 = new HashMap<Integer, Integer>();
+		src.f1.put(1, 1);
+		src.f1.put(2, 2);
+		src.f1.put(3, 3);
+		src.f2 = new HashMap<String, Integer>();
+		src.f2.put("k1", 1);
+		src.f2.put("k2", 2);
+		src.f2.put("k3", 3);
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleMapTypes dst =
+			MessagePack.unpack(raw, SampleMapTypes.class);
+		Iterator<Integer> srcf1 = src.f1.keySet().iterator();
+		Iterator<Integer> dstf1 = dst.f1.keySet().iterator();
+		while (srcf1.hasNext()) {
+			Integer s1 = srcf1.next();
+			Integer d1 = dstf1.next();
+			assertEquals(s1, d1);
+			assertEquals(src.f1.get(s1), dst.f1.get(d1));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		Iterator<String> srcf2 = src.f2.keySet().iterator();
+		Iterator<String> dstf2 = dst.f2.keySet().iterator();
+		while (srcf2.hasNext()) {
+			String s2 = srcf2.next();
+			String d2 = dstf2.next();
+			assertEquals(s2, d2);
+			assertEquals(src.f2.get(s2), dst.f2.get(d2));
+		}
+	}
+
+	@Test
+	public void testMapTypes01() throws Exception {
+		SampleMapTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleMapTypes dst =
+			MessagePack.unpack(raw, SampleMapTypes.class);
+		assertEquals(src, dst);
+	}
+
+	public static class SampleMapTypes {
+		public Map<Integer, Integer> f0;
+		public Map<Integer, Integer> f1;
+		public Map<String, Integer> f2;
+
+		public SampleMapTypes() {
+		}
+	}
+
+	@Test
+	public void testOptionalMapTypes00() throws Exception {
+		SampleOptionalMapTypes src = new SampleOptionalMapTypes();
+		src.f0 = new HashMap<Integer, Integer>();
+		src.f1 = new HashMap<Integer, Integer>();
+		src.f1.put(1, 1);
+		src.f1.put(2, 2);
+		src.f1.put(3, 3);
+		src.f2 = new HashMap<String, Integer>();
+		src.f2.put("k1", 1);
+		src.f2.put("k2", 2);
+		src.f2.put("k3", 3);
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalMapTypes dst =
+			MessagePack.unpack(raw, SampleOptionalMapTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1.size(), dst.f1.size());
+		Iterator<Integer> srcf1 = src.f1.keySet().iterator();
+		Iterator<Integer> dstf1 = dst.f1.keySet().iterator();
+		while (srcf1.hasNext()) {
+			Integer s1 = srcf1.next();
+			Integer d1 = dstf1.next();
+			assertEquals(s1, d1);
+			assertEquals(src.f1.get(s1), dst.f1.get(d1));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		Iterator<String> srcf2 = src.f2.keySet().iterator();
+		Iterator<String> dstf2 = dst.f2.keySet().iterator();
+		while (srcf2.hasNext()) {
+			String s2 = srcf2.next();
+			String d2 = dstf2.next();
+			assertEquals(s2, d2);
+			assertEquals(src.f2.get(s2), dst.f2.get(d2));
+		}
+	}
+
+	@Test
+	public void testOptionalMapTypes01() throws Exception {
+		SampleOptionalMapTypes src = new SampleOptionalMapTypes();
+		src.f0 = new HashMap<Integer, Integer>();
+		src.f1 = null;
+		src.f2 = new HashMap<String, Integer>();
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalMapTypes dst =
+			MessagePack.unpack(raw, SampleOptionalMapTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+	}
+
+	@Test
+	public void testOptionalMapTypes02() throws Exception {
+		SampleOptionalMapTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalMapTypes dst =
+			MessagePack.unpack(raw, SampleOptionalMapTypes.class);
+		assertEquals(src, dst);
+	}
+
+	public static class SampleOptionalMapTypes {
+		@Optional
+		public Map<Integer, Integer> f0;
+		@Optional
+		public Map<Integer, Integer> f1;
+		@Optional
+		public Map<String, Integer> f2;
+
+		public SampleOptionalMapTypes() {
+		}
+	}
+
+	@Test
+	public void testFinalClass() throws Exception {
+		try {
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(FinalModifierClass.class);
+			Assert.assertNull(builder);
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(FinalModifierClass.class);
+			assertTrue(true);
+		} catch (TemplateBuildException e) {
+			fail();
+		}
+		assertTrue(true);
+	}
+
+	public final static class FinalModifierClass {
+	}
+
+	public abstract static class AbstractModifierClass {
+	}
+
+	@Test
+	public void testInterfaceType00() throws Exception {
+		try {
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(SampleInterface.class);
+			Assert.assertNull(builder);
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(SampleInterface.class);
+			fail();
+		} catch (TemplateBuildException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+	}
+
+	@Test
+	public void testInterfaceType01() throws Exception {
+		try {
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(SampleInterface.class);
+			Assert.assertNull(builder);
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(SampleInterface.class);
+			fail();
+		} catch (TemplateBuildException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+	}
+
+	public interface SampleInterface {
+	}
+
+	@Test
+	public void testEnumTypeForOrdinal00() throws Exception {
+		SampleEnumFieldClass src = new SampleEnumFieldClass();
+		src.f0 = 0;
+		src.f1 = SampleEnum.ONE;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleEnumFieldClass dst =
+			MessagePack.unpack(raw, SampleEnumFieldClass.class);
+		assertTrue(src.f1 == dst.f1);
+	}
+
+	@Test
+	public void testEnumTypeForOrdinal01() throws Exception {
+		SampleEnumFieldClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleEnumFieldClass dst =
+			MessagePack.unpack(raw, SampleEnumFieldClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class SampleEnumFieldClass {
+		public int f0;
+
+		public SampleEnum f1;
+
+		public SampleEnumFieldClass() {
+		}
+	}
+
+	@MessagePackOrdinalEnum
+	public enum SampleEnum {
+		ONE, TWO, THREE;
+	}
+
+	@Test
+	public void testOptionalEnumTypeForOrdinal00() throws Exception {
+		SampleOptionalEnumFieldClass src = new SampleOptionalEnumFieldClass();
+		src.f0 = 0;
+		src.f1 = SampleOptionalEnum.ONE;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalEnumFieldClass dst =
+			MessagePack.unpack(raw, SampleOptionalEnumFieldClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1 == dst.f1);
+	}
+
+	@Test
+	public void testOptionalEnumTypeForOrdinal01() throws Exception {
+		SampleOptionalEnumFieldClass src = new SampleOptionalEnumFieldClass();
+		src.f1 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalEnumFieldClass dst =
+			MessagePack.unpack(raw, SampleOptionalEnumFieldClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertEquals(src.f1, dst.f1);
+	}
+
+	@Test
+	public void testOptionalEnumTypeForOrdinal02() throws Exception {
+		SampleEnumFieldClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleEnumFieldClass dst =
+			MessagePack.unpack(raw, SampleEnumFieldClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class SampleOptionalEnumFieldClass {
+		@Optional
+		public int f0;
+
+		@Optional
+		public SampleOptionalEnum f1;
+
+		public SampleOptionalEnumFieldClass() {
+		}
+	}
+
+	@MessagePackOrdinalEnum
+	public enum SampleOptionalEnum {
+		ONE, TWO, THREE;
+	}
+
+	@Test
+	public void testFieldModifiers() throws Exception {
+		FieldModifiersClass src = new FieldModifiersClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+
+		byte[] raw = MessagePack.pack(src);
+
+		FieldModifiersClass dst =
+			MessagePack.unpack(raw, FieldModifiersClass.class);
+		assertTrue(src.f1 == dst.f1);
+		assertTrue(src.f2 != dst.f2);
+		assertTrue(src.f3 != dst.f3);
+		assertTrue(src.f4 != dst.f4);
+	}
+
+	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 testOptionalFieldModifiers() throws Exception {
+		OptionalFieldModifiersClass src = new OptionalFieldModifiersClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalFieldModifiersClass dst =
+			MessagePack.unpack(raw, OptionalFieldModifiersClass.class);
+		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);
+	}
+
+	public static class OptionalFieldModifiersClass {
+		@Optional
+		public int f0;
+		@Optional
+		public final int f1 = 1;
+		private int f2;
+		protected int f3;
+		int f4;
+
+		public OptionalFieldModifiersClass() {
+		}
+	}
+
+	@Test
+	public void testNestedFieldClass00() throws Exception {
+		BaseClass src = new BaseClass();
+		NestedClass src2 = new NestedClass();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass dst =
+			MessagePack.unpack(raw, BaseClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testNestedFieldClass01() throws Exception {
+		BaseClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass dst =
+			MessagePack.unpack(raw, BaseClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class BaseClass {
+		public int f0;
+		public NestedClass f1;
+
+		public BaseClass() {
+		}
+	}
+
+	public static class NestedClass {
+		public int f2;
+
+		public NestedClass() {
+		}
+	}
+
+	@Test
+	public void testOptionalNestedFieldClass00() throws Exception {
+		OptionalBaseClass src = new OptionalBaseClass();
+		OptionalNestedClass src2 = new OptionalNestedClass();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass dst =
+			MessagePack.unpack(raw, OptionalBaseClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testOptionalNestedFieldClass01() throws Exception {
+		OptionalBaseClass src = new OptionalBaseClass();
+		src.f1 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass dst =
+			MessagePack.unpack(raw, OptionalBaseClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1 == dst.f1);
+	}
+
+	@Test
+	public void testOptionalNestedFieldClass02() throws Exception {
+		OptionalBaseClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass dst =
+			MessagePack.unpack(raw, OptionalBaseClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class OptionalBaseClass {
+		@Optional
+		public int f0;
+		@Optional
+		public OptionalNestedClass f1;
+
+		public OptionalBaseClass() {
+		}
+	}
+
+	public static class OptionalNestedClass {
+		@Optional
+		public int f2;
+
+		public OptionalNestedClass() {
+		}
+	}
+
+	@Test
+	public void testMessagePackMessageFieldClass00() throws Exception {
+		BaseClass2 src = new BaseClass2();
+		MessagePackMessageClass2 src2 = new MessagePackMessageClass2();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass2 dst =
+			MessagePack.unpack(raw, BaseClass2.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testMessagePackMessageFieldClass01() throws Exception {
+		BaseClass2 src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass2 dst =
+			MessagePack.unpack(raw, BaseClass2.class);
+		assertEquals(src, dst);
+	}
+
+	public static class BaseClass2 {
+		public int f0;
+		public MessagePackMessageClass2 f1;
+
+		public BaseClass2() {
+		}
+	}
+
+	@MessagePackMessage
+	public static class MessagePackMessageClass2 {
+		public int f2;
+
+		public MessagePackMessageClass2() {
+		}
+	}
+
+	@Test
+	public void testOptionalMessagePackMessageFieldClass00() throws Exception {
+		OptionalBaseClass2 src = new OptionalBaseClass2();
+		OptionalMessagePackMessageClass2 src2 = new OptionalMessagePackMessageClass2();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass2 dst =
+			MessagePack.unpack(raw, OptionalBaseClass2.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testOptionalMessagePackMessageFieldClass01() throws Exception {
+		OptionalBaseClass2 src = new OptionalBaseClass2();
+		src.f1 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass2 dst =
+			MessagePack.unpack(raw, OptionalBaseClass2.class);
+		assertTrue(src.f0 == dst.f0);
+		assertEquals(src.f1, dst.f1);
+	}
+
+	@Test
+	public void testOptionalMessagePackMessageFieldClass02() throws Exception {
+		OptionalBaseClass2 src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass2 dst =
+			MessagePack.unpack(raw, OptionalBaseClass2.class);
+		assertEquals(src, dst);
+	}
+
+	public static class OptionalBaseClass2 {
+		@Optional
+		public int f0;
+		@Optional
+		public OptionalMessagePackMessageClass2 f1;
+
+		public OptionalBaseClass2() {
+		}
+	}
+
+	@MessagePackMessage
+	public static class OptionalMessagePackMessageClass2 {
+		@Optional
+		public int f2;
+
+		public OptionalMessagePackMessageClass2() {
+		}
+	}
+
+	@Test
+	public void testExtendedClass00() throws Exception {
+		SampleSubClass src = new SampleSubClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f8 = 8;
+		src.f9 = 9;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleSubClass dst =
+			MessagePack.unpack(raw, SampleSubClass.class);
+		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);
+	}
+
+	@Test
+	public void testExtendedClass01() throws Exception {
+		SampleSubClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleSubClass dst =
+			MessagePack.unpack(raw, SampleSubClass.class);
+		assertEquals(src, dst);
+	}
+
+	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;
+		@SuppressWarnings("unused")
+		private int f7;
+		protected int f8;
+		int f9;
+
+		public SampleSuperClass() {
+		}
+	}
+
+	@Test
+	public void testOptionalExtendedClass00() throws Exception {
+		SampleOptionalSubClass src = new SampleOptionalSubClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f8 = 8;
+		src.f9 = 9;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalSubClass dst =
+			MessagePack.unpack(raw, SampleOptionalSubClass.class);
+		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);
+	}
+
+	@Test
+	public void testOptionalExtendedClass01() throws Exception {
+		SampleOptionalSubClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalSubClass dst =
+			MessagePack.unpack(raw, SampleOptionalSubClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class SampleOptionalSubClass extends SampleOptionalSuperClass {
+		@Optional
+		public int f0;
+		public final int f1 = 1;
+		private int f2;
+		protected int f3;
+		int f4;
+
+		public SampleOptionalSubClass() {
+		}
+	}
+
+	public static class SampleOptionalSuperClass {
+		@Optional
+		public int f5;
+		public final int f6 = 2;
+		@SuppressWarnings("unused")
+		private int f7;
+		protected int f8;
+		int f9;
+
+		public SampleOptionalSuperClass() {
+		}
+	}
+
+	@Test
+	public void testMessagePackableUnpackableClass00() throws Exception {
+		BaseMessagePackableUnpackableClass src = new BaseMessagePackableUnpackableClass();
+		MessagePackableUnpackableClass src1 = new MessagePackableUnpackableClass();
+		List<MessagePackableUnpackableClass> src2 = new ArrayList<MessagePackableUnpackableClass>();
+		src1.f0 = 0;
+		src1.f1 = 1;
+		src.f0 = src1;
+		src.f1 = 1;
+		src2.add(src1);
+		src.f2 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw, BaseMessagePackableUnpackableClass.class);
+		assertEquals(src.f0.f0, dst.f0.f0);
+		assertEquals(src.f0.f1, dst.f0.f1);
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+		assertEquals(src.f2.get(0).f0, dst.f2.get(0).f0);
+		assertEquals(src.f2.get(0).f1, dst.f2.get(0).f1);
+	}
+
+	@Test
+	public void testMessagePackableUnpackableClass01() throws Exception {
+		BaseMessagePackableUnpackableClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw, BaseMessagePackableUnpackableClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class BaseMessagePackableUnpackableClass {
+		public MessagePackableUnpackableClass f0;
+		public int f1;
+		public List<MessagePackableUnpackableClass> f2;
+
+		public BaseMessagePackableUnpackableClass() {
+		}
+	}
+
+	public static class MessagePackableUnpackableClass implements
+			MessagePackable, MessageUnpackable {
+		public int f0;
+		public int f1;
+
+		public MessagePackableUnpackableClass() {
+		}
+
+		@Override
+		public void messagePack(Packer packer) throws IOException {
+			packer.packArray(2);
+			packer.pack(f0);
+			packer.pack(f1);
+		}
+
+		@Override
+		public void messageUnpack(Unpacker unpacker) throws IOException,
+				MessageTypeException {
+			if (unpacker.tryUnpackNull()) {
+				return;
+			}
+			unpacker.unpackArray();
+			f0 = unpacker.unpackInt();
+			f1 = unpacker.unpackInt();
+		}
+	}
+
+	@Test
+	public void testOptionalMessagePackableUnpackableClass00() throws Exception {
+		OptionalBaseMessagePackableUnpackableClass src = new OptionalBaseMessagePackableUnpackableClass();
+		OptionalMessagePackableUnpackableClass src1 = new OptionalMessagePackableUnpackableClass();
+		List<OptionalMessagePackableUnpackableClass> src2 = new ArrayList<OptionalMessagePackableUnpackableClass>();
+		src1.f0 = 0;
+		src1.f1 = 1;
+		src.f0 = src1;
+		src.f1 = 1;
+		src2.add(src1);
+		src.f2 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw, OptionalBaseMessagePackableUnpackableClass.class);
+		assertEquals(src.f0.f0, dst.f0.f0);
+		assertEquals(src.f0.f1, dst.f0.f1);
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+		assertEquals(src.f2.get(0).f0, dst.f2.get(0).f0);
+		assertEquals(src.f2.get(0).f1, dst.f2.get(0).f1);
+	}
+
+	@Test
+	public void testOptionalMessagePackableUnpackableClass01() throws Exception {
+		OptionalBaseMessagePackableUnpackableClass src = new OptionalBaseMessagePackableUnpackableClass();
+		src.f0 = null;
+		src.f1 = 1;
+		src.f2 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw, OptionalBaseMessagePackableUnpackableClass.class);
+		assertEquals(src.f0, dst.f0);
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2, dst.f2);
+	}
+
+	@Test
+	public void testOptionalMessagePackableUnpackableClass02() throws Exception {
+		OptionalBaseMessagePackableUnpackableClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw, OptionalBaseMessagePackableUnpackableClass.class);
+		assertEquals(src, dst);
+	}
+
+	public static class OptionalBaseMessagePackableUnpackableClass {
+		@Optional
+		public OptionalMessagePackableUnpackableClass f0;
+		@Optional
+		public int f1;
+		@Optional
+		public List<OptionalMessagePackableUnpackableClass> f2;
+
+		public OptionalBaseMessagePackableUnpackableClass() {
+		}
+	}
+
+	public static class OptionalMessagePackableUnpackableClass implements
+			MessagePackable, MessageUnpackable {
+		@Optional
+		public int f0;
+		@Optional
+		public int f1;
+
+		public OptionalMessagePackableUnpackableClass() {
+		}
+
+		@Override
+		public void messagePack(Packer packer) throws IOException {
+			packer.packArray(2);
+			packer.pack(f0);
+			packer.pack(f1);
+		}
+
+		@Override
+		public void messageUnpack(Unpacker unpacker) throws IOException,
+				MessageTypeException {
+			if (unpacker.tryUnpackNull()) {
+				return;
+			}
+			unpacker.unpackArray();
+			f0 = unpacker.unpackInt();
+			f1 = unpacker.unpackInt();
+		}
+	}
+}
+
diff --git a/java/src/test/java/org/msgpack/template/TestTemplateBuilderJavaBeansPackUnpack.java b/java/src/test/java/org/msgpack/template/TestTemplateBuilderJavaBeansPackUnpack.java
new file mode 100644
index 00000000..1310cddb
--- /dev/null
+++ b/java/src/test/java/org/msgpack/template/TestTemplateBuilderJavaBeansPackUnpack.java
@@ -0,0 +1,2234 @@
+package org.msgpack.template;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import org.msgpack.MessagePack;
+import org.msgpack.MessagePackable;
+import org.msgpack.MessagePacker;
+import org.msgpack.MessageTypeException;
+import org.msgpack.MessageUnpackable;
+import org.msgpack.Packer;
+import org.msgpack.Template;
+import org.msgpack.Unpacker;
+import org.msgpack.annotation.MessagePackBeans;
+import org.msgpack.annotation.MessagePackMessage;
+import org.msgpack.annotation.MessagePackOrdinalEnum;
+import org.msgpack.annotation.Optional;
+import org.msgpack.template.TestTemplateBuilderPackConvert.SampleInterface;
+import org.msgpack.template.builder.BuilderSelectorRegistry;
+import org.msgpack.template.builder.TemplateBuilder;
+
+import org.junit.Assert;
+import junit.framework.TestCase;
+import static org.hamcrest.CoreMatchers.*;
+
+public class TestTemplateBuilderJavaBeansPackUnpack extends TestCase {
+	static {
+		MessagePack.register(PrimitiveTypeFieldsClass.class);
+		MessagePack.register(OptionalPrimitiveTypeFieldsClass.class);
+		MessagePack.register(GeneralReferenceTypeFieldsClass.class);
+		MessagePack.register(GeneralOptionalReferenceTypeFieldsClass.class);
+		MessagePack.register(SampleListTypes.class);
+		MessagePack.register(SampleOptionalListTypes.class);
+		MessagePack.register(SampleMapTypes.class);
+		MessagePack.register(SampleOptionalMapTypes.class);
+		MessagePack.register(SampleEnumFieldClass.class);
+		MessagePack.register(SampleOptionalEnumFieldClass.class);
+		MessagePack.register(FieldModifiersClass.class);
+		MessagePack.register(OptionalFieldModifiersClass.class);
+		MessagePack.register(BaseClass.class);
+		MessagePack.register(NestedClass.class);
+		MessagePack.register(BaseClass2.class);
+		MessagePack.register(OptionalBaseClass.class);
+		MessagePack.register(OptionalNestedClass.class);
+		MessagePack.register(OptionalBaseClass2.class);
+		MessagePack.register(SampleSubClass.class);
+		MessagePack.register(SampleSuperClass.class);
+		MessagePack.register(SampleOptionalSubClass.class);
+		MessagePack.register(SampleOptionalSuperClass.class);
+		MessagePack.register(BaseMessagePackableUnpackableClass.class);
+		MessagePack.register(MessagePackableUnpackableClass.class);
+		MessagePack.register(OptionalBaseMessagePackableUnpackableClass.class);
+		MessagePack.register(OptionalMessagePackableUnpackableClass.class);
+	}
+
+	@Test
+	public void testPrimitiveTypeFields00() throws Exception {
+		PrimitiveTypeFieldsClass src = new PrimitiveTypeFieldsClass();
+		src.f0 = (byte) 0;
+		src.f1 = 1;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f6 = false;
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, PrimitiveTypeFieldsClass.class);
+		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);
+	}
+
+	@Test
+	public void testPrimitiveTypeFields01() throws Exception {
+		PrimitiveTypeFieldsClass src = new PrimitiveTypeFieldsClass();
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, PrimitiveTypeFieldsClass.class);
+		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);
+	}
+
+	@Test
+	public void testPrimitiveTypeFields02() throws Exception {
+		PrimitiveTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, PrimitiveTypeFieldsClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class PrimitiveTypeFieldsClass {
+		byte f0;
+		short f1;
+		int f2;
+		long f3;
+		float f4;
+		double f5;
+		boolean f6;
+
+		public byte getF0() {
+			return f0;
+		}
+
+		public void setF0(byte f0) {
+			this.f0 = f0;
+		}
+
+		public short getF1() {
+			return f1;
+		}
+
+		public void setF1(short f1) {
+			this.f1 = f1;
+		}
+
+		public int getF2() {
+			return f2;
+		}
+
+		public void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		public long getF3() {
+			return f3;
+		}
+
+		public void setF3(long f3) {
+			this.f3 = f3;
+		}
+
+		public float getF4() {
+			return f4;
+		}
+
+		public void setF4(float f4) {
+			this.f4 = f4;
+		}
+
+		public double getF5() {
+			return f5;
+		}
+
+		public void setF5(double f5) {
+			this.f5 = f5;
+		}
+
+		public boolean isF6() {
+			return f6;
+		}
+
+		public void setF6(boolean f6) {
+			this.f6 = f6;
+		}
+
+		public PrimitiveTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testOptionalPrimitiveTypeFields00() throws Exception {
+		OptionalPrimitiveTypeFieldsClass src = new OptionalPrimitiveTypeFieldsClass();
+		src.f0 = (byte) 0;
+		src.f1 = 1;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f6 = false;
+
+		byte[] raw = MessagePack.pack(src);
+
+		PrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, PrimitiveTypeFieldsClass.class);
+		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);
+	}
+
+	@Test
+	public void testOptionalPrimitiveTypeFields01() throws Exception {
+		OptionalPrimitiveTypeFieldsClass src = new OptionalPrimitiveTypeFieldsClass();
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalPrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, OptionalPrimitiveTypeFieldsClass.class);
+		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);
+	}
+
+	@Test
+	public void testOptionalPrimitiveTypeFields02() throws Exception {
+		OptionalPrimitiveTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalPrimitiveTypeFieldsClass dst =
+			MessagePack.unpack(raw, OptionalPrimitiveTypeFieldsClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class OptionalPrimitiveTypeFieldsClass {
+		@Optional
+		public byte f0;
+		@Optional
+		public short f1;
+		@Optional
+		public int f2;
+		@Optional
+		public long f3;
+		@Optional
+		public float f4;
+		@Optional
+		public double f5;
+		@Optional
+		public boolean f6;
+
+		@Optional
+		public byte getF0() {
+			return f0;
+		}
+
+		@Optional
+		public void setF0(byte f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public short getF1() {
+			return f1;
+		}
+
+		public void setF1(short f1) {
+			this.f1 = f1;
+		}
+
+		public int getF2() {
+			return f2;
+		}
+
+		@Optional
+		public void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		@Optional
+		public long getF3() {
+			return f3;
+		}
+
+		public void setF3(long f3) {
+			this.f3 = f3;
+		}
+
+		@Optional
+		public float getF4() {
+			return f4;
+		}
+
+		public void setF4(float f4) {
+			this.f4 = f4;
+		}
+
+		@Optional
+		public double getF5() {
+			return f5;
+		}
+
+		public void setF5(double f5) {
+			this.f5 = f5;
+		}
+
+		@Optional
+		public boolean isF6() {
+			return f6;
+		}
+
+		public void setF6(boolean f6) {
+			this.f6 = f6;
+		}
+
+		public OptionalPrimitiveTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testGeneralReferenceTypeFieldsClass00() throws Exception {
+		GeneralReferenceTypeFieldsClass src = new GeneralReferenceTypeFieldsClass();
+		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";
+		src.f9 = new byte[] { 0x01, 0x02 };
+		src.f10 = ByteBuffer.wrap("muga".getBytes());
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw, GeneralReferenceTypeFieldsClass.class);
+		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);
+		assertEquals(src.f9[0], dst.f9[0]);
+		assertEquals(src.f9[1], dst.f9[1]);
+		assertEquals(src.f10, dst.f10);
+	}
+
+	@Test
+	public void testGeneralReferenceTypeFieldsClass01() throws Exception {
+		GeneralReferenceTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw, GeneralReferenceTypeFieldsClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	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 byte[] f9;
+		public ByteBuffer f10;
+
+		public Byte getF0() {
+			return f0;
+		}
+
+		public void setF0(Byte f0) {
+			this.f0 = f0;
+		}
+
+		public Short getF1() {
+			return f1;
+		}
+
+		public void setF1(Short f1) {
+			this.f1 = f1;
+		}
+
+		public Integer getF2() {
+			return f2;
+		}
+
+		public void setF2(Integer f2) {
+			this.f2 = f2;
+		}
+
+		public Long getF3() {
+			return f3;
+		}
+
+		public void setF3(Long f3) {
+			this.f3 = f3;
+		}
+
+		public Float getF4() {
+			return f4;
+		}
+
+		public void setF4(Float f4) {
+			this.f4 = f4;
+		}
+
+		public Double getF5() {
+			return f5;
+		}
+
+		public void setF5(Double f5) {
+			this.f5 = f5;
+		}
+
+		public Boolean getF6() {
+			return f6;
+		}
+
+		public void setF6(Boolean f6) {
+			this.f6 = f6;
+		}
+
+		public BigInteger getF7() {
+			return f7;
+		}
+
+		public void setF7(BigInteger f7) {
+			this.f7 = f7;
+		}
+
+		public String getF8() {
+			return f8;
+		}
+
+		public void setF8(String f8) {
+			this.f8 = f8;
+		}
+
+		public byte[] getF9() {
+			return f9;
+		}
+
+		public void setF9(byte[] f9) {
+			this.f9 = f9;
+		}
+
+		public ByteBuffer getF10() {
+			return f10;
+		}
+
+		public void setF10(ByteBuffer f10) {
+			this.f10 = f10;
+		}
+
+		public GeneralReferenceTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testGeneralOptionalReferenceTypeFieldsClass00()
+			throws Exception {
+		GeneralOptionalReferenceTypeFieldsClass src = new GeneralOptionalReferenceTypeFieldsClass();
+		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";
+		src.f9 = new byte[] { 0x01, 0x02 };
+		src.f10 = ByteBuffer.wrap("muga".getBytes());
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralOptionalReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw, GeneralOptionalReferenceTypeFieldsClass.class);
+		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);
+		assertEquals(src.f9[0], dst.f9[0]);
+		assertEquals(src.f9[1], dst.f9[1]);
+		assertEquals(src.f10, dst.f10);
+	}
+
+	@Test
+	public void testGeneralOptionalReferenceTypeFieldsClass01()
+			throws Exception {
+		GeneralOptionalReferenceTypeFieldsClass src = new GeneralOptionalReferenceTypeFieldsClass();
+		src.f0 = null;
+		src.f1 = null;
+		src.f2 = null;
+		src.f3 = null;
+		src.f4 = null;
+		src.f5 = null;
+		src.f6 = null;
+		src.f7 = null;
+		src.f8 = null;
+		src.f9 = null;
+		src.f10 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralOptionalReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw, GeneralOptionalReferenceTypeFieldsClass.class);
+		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);
+		assertEquals(src.f9, dst.f9);
+		assertEquals(src.f10, dst.f10);
+	}
+
+	@Test
+	public void testGeneralOptionalReferenceTypeFieldsClass02()
+			throws Exception {
+		GeneralOptionalReferenceTypeFieldsClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		GeneralOptionalReferenceTypeFieldsClass dst =
+			MessagePack.unpack(raw, GeneralOptionalReferenceTypeFieldsClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class GeneralOptionalReferenceTypeFieldsClass {
+		@Optional
+		public Byte f0;
+		@Optional
+		public Short f1;
+		@Optional
+		public Integer f2;
+		@Optional
+		public Long f3;
+		@Optional
+		public Float f4;
+		@Optional
+		public Double f5;
+		@Optional
+		public Boolean f6;
+		@Optional
+		public BigInteger f7;
+		@Optional
+		public String f8;
+		@Optional
+		public byte[] f9;
+		@Optional
+		public ByteBuffer f10;
+
+		@Optional
+		public Byte getF0() {
+			return f0;
+		}
+
+		@Optional
+		public void setF0(Byte f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public Short getF1() {
+			return f1;
+		}
+
+		public void setF1(Short f1) {
+			this.f1 = f1;
+		}
+
+		public Integer getF2() {
+			return f2;
+		}
+
+		@Optional
+		public void setF2(Integer f2) {
+			this.f2 = f2;
+		}
+
+		@Optional
+		public Long getF3() {
+			return f3;
+		}
+
+		public void setF3(Long f3) {
+			this.f3 = f3;
+		}
+
+		@Optional
+		public Float getF4() {
+			return f4;
+		}
+
+		public void setF4(Float f4) {
+			this.f4 = f4;
+		}
+
+		@Optional
+		public Double getF5() {
+			return f5;
+		}
+
+		public void setF5(Double f5) {
+			this.f5 = f5;
+		}
+
+		@Optional
+		public Boolean getF6() {
+			return f6;
+		}
+
+		public void setF6(Boolean f6) {
+			this.f6 = f6;
+		}
+
+		@Optional
+		public BigInteger getF7() {
+			return f7;
+		}
+
+		public void setF7(BigInteger f7) {
+			this.f7 = f7;
+		}
+
+		@Optional
+		public String getF8() {
+			return f8;
+		}
+
+		public void setF8(String f8) {
+			this.f8 = f8;
+		}
+
+		@Optional
+		public byte[] getF9() {
+			return f9;
+		}
+
+		public void setF9(byte[] f9) {
+			this.f9 = f9;
+		}
+
+		@Optional
+		public ByteBuffer getF10() {
+			return f10;
+		}
+
+		public void setF10(ByteBuffer f10) {
+			this.f10 = f10;
+		}
+
+		public GeneralOptionalReferenceTypeFieldsClass() {
+		}
+	}
+
+	@Test
+	public void testListTypes00() throws Exception {
+		SampleListTypes src = new SampleListTypes();
+		src.f0 = new ArrayList<Integer>();
+		src.f1 = new ArrayList<Integer>();
+		src.f1.add(1);
+		src.f1.add(2);
+		src.f1.add(3);
+		src.f2 = new ArrayList<String>();
+		src.f2.add("e1");
+		src.f2.add("e2");
+		src.f2.add("e3");
+		src.f3 = new ArrayList<List<String>>();
+		src.f3.add(src.f2);
+		src.f4 = new ArrayList<SampleListNestedType>();
+		SampleListNestedType slnt = new SampleListNestedType();
+		slnt.f0 = new byte[] { 0x01, 0x02 };
+		slnt.f1 = "muga";
+		src.f4.add(slnt);
+		src.f5 = new ArrayList<ByteBuffer>();
+		src.f5.add(ByteBuffer.wrap("e1".getBytes()));
+		src.f5.add(ByteBuffer.wrap("e2".getBytes()));
+		src.f5.add(ByteBuffer.wrap("e3".getBytes()));
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleListTypes dst =
+			MessagePack.unpack(raw, SampleListTypes.class);
+		for (int i = 0; i < src.f1.size(); ++i) {
+			assertEquals(src.f1.get(i), dst.f1.get(i));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		for (int i = 0; i < src.f2.size(); ++i) {
+			assertEquals(src.f2.get(i), dst.f2.get(i));
+		}
+		assertEquals(src.f3.size(), dst.f3.size());
+		for (int i = 0; i < src.f3.size(); ++i) {
+			List<String> srclist = src.f3.get(i);
+			List<String> dstlist = dst.f3.get(i);
+			assertEquals(srclist.size(), dstlist.size());
+			for (int j = 0; j < srclist.size(); ++j) {
+				assertEquals(srclist.get(j), dstlist.get(j));
+			}
+		}
+		assertEquals(src.f4.size(), dst.f4.size());
+		for (int i = 0; i < src.f4.size(); ++i) {
+			SampleListNestedType s = src.f4.get(i);
+			SampleListNestedType d = dst.f4.get(i);
+			assertEquals(s.f0[0], d.f0[0]);
+			assertEquals(s.f0[1], d.f0[1]);
+			assertEquals(s.f1, d.f1);
+		}
+		assertEquals(src.f5.size(), dst.f5.size());
+		for (int i = 0; i < src.f5.size(); ++i) {
+			ByteBuffer s = src.f5.get(i);
+			ByteBuffer d = dst.f5.get(i);
+			assertEquals(s, d);
+		}
+	}
+
+	@Test
+	public void testListTypes01() throws Exception {
+		SampleListTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleListTypes dst =
+			MessagePack.unpack(raw, SampleListTypes.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class SampleListTypes {
+		public List<Integer> f0;
+		public List<Integer> f1;
+		public List<String> f2;
+		public List<List<String>> f3;
+		public List<SampleListNestedType> f4;
+		public List<ByteBuffer> f5;
+
+		public List<Integer> getF0() {
+			return f0;
+		}
+
+		public void setF0(List<Integer> f0) {
+			this.f0 = f0;
+		}
+
+		public List<Integer> getF1() {
+			return f1;
+		}
+
+		public void setF1(List<Integer> f1) {
+			this.f1 = f1;
+		}
+
+		public List<String> getF2() {
+			return f2;
+		}
+
+		public void setF2(List<String> f2) {
+			this.f2 = f2;
+		}
+
+		public List<List<String>> getF3() {
+			return f3;
+		}
+
+		public void setF3(List<List<String>> f3) {
+			this.f3 = f3;
+		}
+
+		public List<SampleListNestedType> getF4() {
+			return f4;
+		}
+
+		public void setF4(List<SampleListNestedType> f4) {
+			this.f4 = f4;
+		}
+
+		public List<ByteBuffer> getF5() {
+			return f5;
+		}
+
+		public void setF5(List<ByteBuffer> f5) {
+			this.f5 = f5;
+		}
+
+		public SampleListTypes() {
+		}
+	}
+
+	@MessagePackMessage
+	public static class SampleListNestedType {
+		public byte[] f0;
+		public String f1;
+
+		public SampleListNestedType() {
+		}
+	}
+
+	@Test
+	public void testOptionalListTypes00() throws Exception {
+		SampleOptionalListTypes src = new SampleOptionalListTypes();
+		src.f0 = new ArrayList<Integer>();
+		src.f1 = new ArrayList<Integer>();
+		src.f1.add(1);
+		src.f1.add(2);
+		src.f1.add(3);
+		src.f2 = new ArrayList<String>();
+		src.f2.add("e1");
+		src.f2.add("e2");
+		src.f2.add("e3");
+		src.f3 = new ArrayList<List<String>>();
+		src.f3.add(src.f2);
+		src.f4 = new ArrayList<SampleOptionalListNestedType>();
+		SampleOptionalListNestedType slnt = new SampleOptionalListNestedType();
+		slnt.f0 = new byte[] { 0x01, 0x02 };
+		slnt.f1 = "muga";
+		src.f4.add(slnt);
+		src.f5 = new ArrayList<ByteBuffer>();
+		src.f5.add(ByteBuffer.wrap("e1".getBytes()));
+		src.f5.add(ByteBuffer.wrap("e2".getBytes()));
+		src.f5.add(ByteBuffer.wrap("e3".getBytes()));
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalListTypes dst =
+			MessagePack.unpack(raw, SampleOptionalListTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1.size(), dst.f1.size());
+		for (int i = 0; i < src.f1.size(); ++i) {
+			assertEquals(src.f1.get(i), dst.f1.get(i));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		for (int i = 0; i < src.f2.size(); ++i) {
+			assertEquals(src.f2.get(i), dst.f2.get(i));
+		}
+		assertEquals(src.f3.size(), dst.f3.size());
+		for (int i = 0; i < src.f3.size(); ++i) {
+			List<String> srclist = src.f3.get(i);
+			List<String> dstlist = dst.f3.get(i);
+			assertEquals(srclist.size(), dstlist.size());
+			for (int j = 0; j < srclist.size(); ++j) {
+				assertEquals(srclist.get(j), dstlist.get(j));
+			}
+		}
+		assertEquals(src.f4.size(), dst.f4.size());
+		for (int i = 0; i < src.f4.size(); ++i) {
+			SampleOptionalListNestedType s = src.f4.get(i);
+			SampleOptionalListNestedType d = dst.f4.get(i);
+			assertEquals(s.f0[0], d.f0[0]);
+			assertEquals(s.f0[1], d.f0[1]);
+			assertEquals(s.f1, d.f1);
+		}
+		assertEquals(src.f5.size(), dst.f5.size());
+		for (int i = 0; i < src.f5.size(); ++i) {
+			ByteBuffer s = src.f5.get(i);
+			ByteBuffer d = dst.f5.get(i);
+			assertEquals(s, d);
+		}
+	}
+
+	@Test
+	public void testOptionalListTypes01() throws Exception {
+		SampleOptionalListTypes src = new SampleOptionalListTypes();
+		src.f0 = new ArrayList<Integer>();
+		src.f1 = null;
+		src.f2 = new ArrayList<String>();
+		src.f3 = new ArrayList<List<String>>();
+		src.f4 = null;
+		src.f5 = new ArrayList<ByteBuffer>();
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalListTypes dst =
+			MessagePack.unpack(raw, SampleOptionalListTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+		assertEquals(src.f3.size(), dst.f3.size());
+		assertEquals(src.f4, dst.f4);
+		assertEquals(src.f5.size(), dst.f5.size());
+	}
+
+	@Test
+	public void testOptionalListTypes02() throws Exception {
+		SampleListTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleListTypes dst =
+			MessagePack.unpack(raw, SampleListTypes.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class SampleOptionalListTypes {
+		@Optional
+		public List<Integer> f0;
+		@Optional
+		public List<Integer> f1;
+		@Optional
+		public List<String> f2;
+		@Optional
+		public List<List<String>> f3;
+		@Optional
+		public List<SampleOptionalListNestedType> f4;
+		@Optional
+		public List<ByteBuffer> f5;
+
+		@Optional
+		public List<Integer> getF0() {
+			return f0;
+		}
+
+		@Optional
+		public void setF0(List<Integer> f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public List<Integer> getF1() {
+			return f1;
+		}
+
+		public void setF1(List<Integer> f1) {
+			this.f1 = f1;
+		}
+
+		public List<String> getF2() {
+			return f2;
+		}
+
+		@Optional
+		public void setF2(List<String> f2) {
+			this.f2 = f2;
+		}
+
+		@Optional
+		public List<List<String>> getF3() {
+			return f3;
+		}
+
+		public void setF3(List<List<String>> f3) {
+			this.f3 = f3;
+		}
+
+		@Optional
+		public List<SampleOptionalListNestedType> getF4() {
+			return f4;
+		}
+
+		public void setF4(List<SampleOptionalListNestedType> f4) {
+			this.f4 = f4;
+		}
+
+		@Optional
+		public List<ByteBuffer> getF5() {
+			return f5;
+		}
+
+		public void setF5(List<ByteBuffer> f5) {
+			this.f5 = f5;
+		}
+
+		public SampleOptionalListTypes() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class SampleOptionalListNestedType {
+		@Optional
+		public byte[] f0;
+		@Optional
+		public String f1;
+
+		@Optional
+		public byte[] getF0() {
+			return f0;
+		}
+
+		public void setF0(byte[] f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public String getF1() {
+			return f1;
+		}
+
+		public void setF1(String f1) {
+			this.f1 = f1;
+		}
+
+		public SampleOptionalListNestedType() {
+		}
+	}
+
+	@Test
+	public void testMapTypes00() throws Exception {
+		SampleMapTypes src = new SampleMapTypes();
+		src.f0 = new HashMap<Integer, Integer>();
+		src.f1 = new HashMap<Integer, Integer>();
+		src.f1.put(1, 1);
+		src.f1.put(2, 2);
+		src.f1.put(3, 3);
+		src.f2 = new HashMap<String, Integer>();
+		src.f2.put("k1", 1);
+		src.f2.put("k2", 2);
+		src.f2.put("k3", 3);
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleMapTypes dst =
+			MessagePack.unpack(raw, SampleMapTypes.class);
+		Iterator<Integer> srcf1 = src.f1.keySet().iterator();
+		Iterator<Integer> dstf1 = dst.f1.keySet().iterator();
+		while (srcf1.hasNext()) {
+			Integer s1 = srcf1.next();
+			Integer d1 = dstf1.next();
+			assertEquals(s1, d1);
+			assertEquals(src.f1.get(s1), dst.f1.get(d1));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		Iterator<String> srcf2 = src.f2.keySet().iterator();
+		Iterator<String> dstf2 = dst.f2.keySet().iterator();
+		while (srcf2.hasNext()) {
+			String s2 = srcf2.next();
+			String d2 = dstf2.next();
+			assertEquals(s2, d2);
+			assertEquals(src.f2.get(s2), dst.f2.get(d2));
+		}
+	}
+
+	@Test
+	public void testMapTypes01() throws Exception {
+		SampleMapTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleMapTypes dst =
+			MessagePack.unpack(raw, SampleMapTypes.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class SampleMapTypes {
+		public Map<Integer, Integer> f0;
+		public Map<Integer, Integer> f1;
+		public Map<String, Integer> f2;
+
+		public Map<Integer, Integer> getF0() {
+			return f0;
+		}
+
+		public void setF0(Map<Integer, Integer> f0) {
+			this.f0 = f0;
+		}
+
+		public Map<Integer, Integer> getF1() {
+			return f1;
+		}
+
+		public void setF1(Map<Integer, Integer> f1) {
+			this.f1 = f1;
+		}
+
+		public Map<String, Integer> getF2() {
+			return f2;
+		}
+
+		public void setF2(Map<String, Integer> f2) {
+			this.f2 = f2;
+		}
+
+		public SampleMapTypes() {
+		}
+	}
+
+	@Test
+	public void testOptionalMapTypes00() throws Exception {
+		SampleOptionalMapTypes src = new SampleOptionalMapTypes();
+		src.f0 = new HashMap<Integer, Integer>();
+		src.f1 = new HashMap<Integer, Integer>();
+		src.f1.put(1, 1);
+		src.f1.put(2, 2);
+		src.f1.put(3, 3);
+		src.f2 = new HashMap<String, Integer>();
+		src.f2.put("k1", 1);
+		src.f2.put("k2", 2);
+		src.f2.put("k3", 3);
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalMapTypes dst =
+			MessagePack.unpack(raw, SampleOptionalMapTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1.size(), dst.f1.size());
+		Iterator<Integer> srcf1 = src.f1.keySet().iterator();
+		Iterator<Integer> dstf1 = dst.f1.keySet().iterator();
+		while (srcf1.hasNext()) {
+			Integer s1 = srcf1.next();
+			Integer d1 = dstf1.next();
+			assertEquals(s1, d1);
+			assertEquals(src.f1.get(s1), dst.f1.get(d1));
+		}
+		assertEquals(src.f2.size(), dst.f2.size());
+		Iterator<String> srcf2 = src.f2.keySet().iterator();
+		Iterator<String> dstf2 = dst.f2.keySet().iterator();
+		while (srcf2.hasNext()) {
+			String s2 = srcf2.next();
+			String d2 = dstf2.next();
+			assertEquals(s2, d2);
+			assertEquals(src.f2.get(s2), dst.f2.get(d2));
+		}
+	}
+
+	@Test
+	public void testOptionalMapTypes01() throws Exception {
+		SampleOptionalMapTypes src = new SampleOptionalMapTypes();
+		src.f0 = new HashMap<Integer, Integer>();
+		src.f1 = null;
+		src.f2 = new HashMap<String, Integer>();
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalMapTypes dst =
+			MessagePack.unpack(raw, SampleOptionalMapTypes.class);
+		assertEquals(src.f0.size(), dst.f0.size());
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+	}
+
+	@Test
+	public void testOptionalMapTypes02() throws Exception {
+		SampleOptionalMapTypes src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalMapTypes dst =
+			MessagePack.unpack(raw, SampleOptionalMapTypes.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class SampleOptionalMapTypes {
+		@Optional
+		public Map<Integer, Integer> f0;
+		@Optional
+		public Map<Integer, Integer> f1;
+		@Optional
+		public Map<String, Integer> f2;
+
+		@Optional
+		public Map<Integer, Integer> getF0() {
+			return f0;
+		}
+
+		public void setF0(Map<Integer, Integer> f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public Map<Integer, Integer> getF1() {
+			return f1;
+		}
+
+		public void setF1(Map<Integer, Integer> f1) {
+			this.f1 = f1;
+		}
+
+		@Optional
+		public Map<String, Integer> getF2() {
+			return f2;
+		}
+
+		public void setF2(Map<String, Integer> f2) {
+			this.f2 = f2;
+		}
+
+		public SampleOptionalMapTypes() {
+		}
+	}
+
+
+	@MessagePackBeans
+	public abstract static class AbstractModifierClass {
+	}
+
+	@Test
+	public void testInterfaceType00() throws Exception {
+		try {
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(SampleInterface.class);
+			Assert.assertNull(builder);
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(SampleInterface.class);
+			fail();
+		} catch (TemplateBuildException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+	}
+
+	@Test
+	public void testInterfaceType01() throws Exception {
+		try {
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(SampleInterface.class);
+			Assert.assertNull(builder);
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(SampleInterface.class);
+			fail();
+		} catch (TemplateBuildException e) {
+			assertTrue(true);
+		}
+		assertTrue(true);
+	}
+
+	public interface SampleInterface {
+	}
+
+	@Test
+	public void testEnumTypeForOrdinal00() throws Exception {
+		SampleEnumFieldClass src = new SampleEnumFieldClass();
+		src.f0 = 23;
+		src.f1 = SampleEnum.ONE;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleEnumFieldClass dst =
+			MessagePack.unpack(raw, SampleEnumFieldClass.class);
+		Assert.assertThat(dst.f0, is(src.f0));
+		Assert.assertThat(dst.f1, is(src.f1));
+	}
+
+	@Test
+	public void testEnumTypeForOrdinal01() throws Exception {
+		SampleEnumFieldClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleEnumFieldClass dst =
+			MessagePack.unpack(raw, SampleEnumFieldClass.class);
+		Assert.assertThat(dst,is(src));
+	}
+
+	@MessagePackBeans
+	public static class SampleEnumFieldClass {
+		public int f0;
+
+		public SampleEnum f1;
+
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		public SampleEnum getF1() {
+			return f1;
+		}
+
+		public void setF1(SampleEnum f1) {
+			this.f1 = f1;
+		}
+
+		public SampleEnumFieldClass() {
+		}
+	}
+
+	@MessagePackOrdinalEnum
+	public enum SampleEnum {
+		ONE, TWO, THREE;
+	}
+
+	@Test
+	public void testOptionalEnumTypeForOrdinal00() throws Exception {
+		SampleOptionalEnumFieldClass src = new SampleOptionalEnumFieldClass();
+		src.f0 = 0;
+		src.f1 = SampleOptionalEnum.ONE;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalEnumFieldClass dst =
+			MessagePack.unpack(raw, SampleOptionalEnumFieldClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1 == dst.f1);
+	}
+
+	@Test
+	public void testOptionalEnumTypeForOrdinal01() throws Exception {
+		SampleOptionalEnumFieldClass src = new SampleOptionalEnumFieldClass();
+		src.f1 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalEnumFieldClass dst =
+			MessagePack.unpack(raw, SampleOptionalEnumFieldClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertEquals(src.f1, dst.f1);
+	}
+
+	@Test
+	public void testOptionalEnumTypeForOrdinal02() throws Exception {
+		SampleEnumFieldClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleEnumFieldClass dst =
+			MessagePack.unpack(raw, SampleEnumFieldClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class SampleOptionalEnumFieldClass {
+		@Optional
+		public int f0;
+
+		@Optional
+		public SampleOptionalEnum f1;
+
+		@Optional
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public SampleOptionalEnum getF1() {
+			return f1;
+		}
+
+		public void setF1(SampleOptionalEnum f1) {
+			this.f1 = f1;
+		}
+
+		public SampleOptionalEnumFieldClass() {
+		}
+	}
+
+	@MessagePackOrdinalEnum
+	public enum SampleOptionalEnum {
+		ONE, TWO, THREE;
+	}
+
+	@Test
+	public void testFieldModifiers() throws Exception {
+		FieldModifiersClass src = new FieldModifiersClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+
+		byte[] raw = MessagePack.pack(src);
+
+		FieldModifiersClass dst =
+			MessagePack.unpack(raw, FieldModifiersClass.class);
+		Assert.assertEquals(src.f1,dst.f1);
+		Assert.assertThat(dst.f2, is( not(src.f2)));
+		Assert.assertThat(dst.f3, is( not(src.f3)));
+		Assert.assertThat(dst.f4, is( not(src.f4)));
+	}
+
+	@MessagePackBeans
+	public static class FieldModifiersClass {
+		public int f0;
+		public final int f1 = 1;
+		private int f2;
+		protected int f3;
+		int f4;
+
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+		
+		private int getF2() {
+			return f2;
+		}
+
+		private void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		public int getF3() {
+			return f3;
+		}
+
+		protected void setF3(int f3) {
+			this.f3 = f3;
+		}
+
+		public int getF4() {
+			return f4;
+		}
+
+		void setF4(int f4) {
+			this.f4 = f4;
+		}
+
+		public int getF1() {
+			return f1;
+		}
+
+		public FieldModifiersClass() {
+		}
+	}
+
+	@Test
+	public void testOptionalFieldModifiers() throws Exception {
+		OptionalFieldModifiersClass src = new OptionalFieldModifiersClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalFieldModifiersClass dst =
+			MessagePack.unpack(raw, OptionalFieldModifiersClass.class);
+		Assert.assertThat(dst.f0, is(src.f0));
+		Assert.assertThat(dst.f1, is(src.f1));
+		Assert.assertThat(dst.f2, is(not(src.f2)));
+		Assert.assertThat(dst.f3, is(not(src.f3)));
+		Assert.assertThat(dst.f4, is(not(src.f4)));
+	}
+
+	@MessagePackBeans
+	public static class OptionalFieldModifiersClass {
+		@Optional
+		public int f0;
+		@Optional
+		public final int f1 = 1;
+		private int f2;
+		protected int f3;
+		int f4;
+
+		@Optional
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		private int getF2() {
+			return f2;
+		}
+
+		public void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		protected int getF3() {
+			return f3;
+		}
+
+		protected void setF3(int f3) {
+			this.f3 = f3;
+		}
+
+		public int getF4() {
+			return f4;
+		}
+
+		void setF4(int f4) {
+			this.f4 = f4;
+		}
+
+		public int getF1() {
+			return f1;
+		}
+
+		public OptionalFieldModifiersClass() {
+		}
+	}
+
+	@Test
+	public void testNestedFieldClass00() throws Exception {
+		BaseClass src = new BaseClass();
+		NestedClass src2 = new NestedClass();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass dst =
+			MessagePack.unpack(raw, BaseClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testNestedFieldClass01() throws Exception {
+		BaseClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass dst =
+			MessagePack.unpack(raw, BaseClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class BaseClass {
+		public int f0;
+		public NestedClass f1;
+
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		public NestedClass getF1() {
+			return f1;
+		}
+
+		public void setF1(NestedClass f1) {
+			this.f1 = f1;
+		}
+
+		public BaseClass() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class NestedClass {
+		public int f2;
+
+		public int getF2() {
+			return f2;
+		}
+
+		public void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		public NestedClass() {
+		}
+	}
+
+	@Test
+	public void testOptionalNestedFieldClass00() throws Exception {
+		OptionalBaseClass src = new OptionalBaseClass();
+		OptionalNestedClass src2 = new OptionalNestedClass();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass dst =
+			MessagePack.unpack(raw, OptionalBaseClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testOptionalNestedFieldClass01() throws Exception {
+		OptionalBaseClass src = new OptionalBaseClass();
+		src.f1 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass dst =
+			MessagePack.unpack(raw, OptionalBaseClass.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1 == dst.f1);
+	}
+
+	@Test
+	public void testOptionalNestedFieldClass02() throws Exception {
+		OptionalBaseClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass dst =
+			MessagePack.unpack(raw, OptionalBaseClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class OptionalBaseClass {
+		@Optional
+		public int f0;
+		@Optional
+		public OptionalNestedClass f1;
+
+		@Optional
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public OptionalNestedClass getF1() {
+			return f1;
+		}
+
+		public void setF1(OptionalNestedClass f1) {
+			this.f1 = f1;
+		}
+
+		public OptionalBaseClass() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class OptionalNestedClass {
+		@Optional
+		public int f2;
+
+		@Optional
+		public int getF2() {
+			return f2;
+		}
+
+		public void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		public OptionalNestedClass() {
+		}
+	}
+
+	@Test
+	public void testMessagePackMessageFieldClass00() throws Exception {
+		BaseClass2 src = new BaseClass2();
+		MessagePackMessageClass2 src2 = new MessagePackMessageClass2();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass2 dst =
+			MessagePack.unpack(raw, BaseClass2.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testMessagePackMessageFieldClass01() throws Exception {
+		BaseClass2 src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseClass2 dst =
+			MessagePack.unpack(raw, BaseClass2.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class BaseClass2 {
+		public int f0;
+		public MessagePackMessageClass2 f1;
+
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		public MessagePackMessageClass2 getF1() {
+			return f1;
+		}
+
+		public void setF1(MessagePackMessageClass2 f1) {
+			this.f1 = f1;
+		}
+
+		public BaseClass2() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class MessagePackMessageClass2 {
+		public int getF2() {
+			return f2;
+		}
+
+		public void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		public int f2;
+
+		public MessagePackMessageClass2() {
+		}
+	}
+
+	@Test
+	public void testOptionalMessagePackMessageFieldClass00() throws Exception {
+		OptionalBaseClass2 src = new OptionalBaseClass2();
+		OptionalMessagePackMessageClass2 src2 = new OptionalMessagePackMessageClass2();
+		src.f0 = 0;
+		src2.f2 = 2;
+		src.f1 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass2 dst =
+			MessagePack.unpack(raw, OptionalBaseClass2.class);
+		assertTrue(src.f0 == dst.f0);
+		assertTrue(src.f1.f2 == dst.f1.f2);
+	}
+
+	@Test
+	public void testOptionalMessagePackMessageFieldClass01() throws Exception {
+		OptionalBaseClass2 src = new OptionalBaseClass2();
+		src.f1 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass2 dst =
+			MessagePack.unpack(raw, OptionalBaseClass2.class);
+		assertTrue(src.f0 == dst.f0);
+		assertEquals(src.f1, dst.f1);
+	}
+
+	@Test
+	public void testOptionalMessagePackMessageFieldClass02() throws Exception {
+		OptionalBaseClass2 src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseClass2 dst =
+			MessagePack.unpack(raw, OptionalBaseClass2.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class OptionalBaseClass2 {
+		@Optional
+		public int f0;
+		@Optional
+		public OptionalMessagePackMessageClass2 f1;
+
+		@Optional
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public OptionalMessagePackMessageClass2 getF1() {
+			return f1;
+		}
+
+		public void setF1(OptionalMessagePackMessageClass2 f1) {
+			this.f1 = f1;
+		}
+
+		public OptionalBaseClass2() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class OptionalMessagePackMessageClass2 {
+		@Optional
+		public int f2;
+
+		@Optional
+		public int getF2() {
+			return f2;
+		}
+
+		public void setF2(int f2) {
+			this.f2 = f2;
+		}
+
+		public OptionalMessagePackMessageClass2() {
+		}
+	}
+
+	@Test
+	public void testExtendedClass00() throws Exception {
+		SampleSubClass src = new SampleSubClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f8 = 8;
+		src.f9 = 9;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleSubClass dst =
+			MessagePack.unpack(raw, SampleSubClass.class);
+		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);
+	}
+
+	@Test
+	public void testExtendedClass01() throws Exception {
+		SampleSubClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleSubClass dst =
+			MessagePack.unpack(raw, SampleSubClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class SampleSubClass extends SampleSuperClass {
+		public int f0;
+		public final int f1 = 1;
+		private int f2;
+		protected int f3;
+		int f4;
+
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		public int getF1() {
+			return f1;
+		}
+
+		public SampleSubClass() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class SampleSuperClass {
+		public int f5;
+		public final int f6 = 2;
+		@SuppressWarnings("unused")
+		private int f7;
+		protected int f8;
+		int f9;
+
+		public int getF5() {
+			return f5;
+		}
+
+		public void setF5(int f5) {
+			this.f5 = f5;
+		}
+
+		public SampleSuperClass() {
+		}
+	}
+
+	@Test
+	public void testOptionalExtendedClass00() throws Exception {
+		SampleOptionalSubClass src = new SampleOptionalSubClass();
+		src.f0 = 0;
+		src.f2 = 2;
+		src.f3 = 3;
+		src.f4 = 4;
+		src.f5 = 5;
+		src.f8 = 8;
+		src.f9 = 9;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalSubClass dst =
+			MessagePack.unpack(raw, SampleOptionalSubClass.class);
+		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);
+	}
+
+	@Test
+	public void testOptionalExtendedClass01() throws Exception {
+		SampleOptionalSubClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		SampleOptionalSubClass dst =
+			MessagePack.unpack(raw, SampleOptionalSubClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class SampleOptionalSubClass extends SampleOptionalSuperClass {
+		@Optional
+		public int f0;
+		public final int f1 = 1;
+		private int f2;
+		protected int f3;
+		int f4;
+		@Optional
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		public SampleOptionalSubClass() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class SampleOptionalSuperClass {
+		@Optional
+		public int getF5() {
+			return f5;
+		}
+
+		public void setF5(int f5) {
+			this.f5 = f5;
+		}
+
+		@Optional
+		public int f5;
+		public final int f6 = 2;
+		@SuppressWarnings("unused")
+		private int f7;
+		protected int f8;
+		int f9;
+
+		public SampleOptionalSuperClass() {
+		}
+	}
+
+	@Test
+	public void testMessagePackableUnpackableClass00() throws Exception {
+		BaseMessagePackableUnpackableClass src = new BaseMessagePackableUnpackableClass();
+		MessagePackableUnpackableClass src1 = new MessagePackableUnpackableClass();
+		List<MessagePackableUnpackableClass> src2 = new ArrayList<MessagePackableUnpackableClass>();
+		src1.f0 = 0;
+		src1.f1 = 1;
+		src.f0 = src1;
+		src.f1 = 1;
+		src2.add(src1);
+		src.f2 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw, BaseMessagePackableUnpackableClass.class);
+		assertEquals(src.f0.f0, dst.f0.f0);
+		assertEquals(src.f0.f1, dst.f0.f1);
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+		assertEquals(src.f2.get(0).f0, dst.f2.get(0).f0);
+		assertEquals(src.f2.get(0).f1, dst.f2.get(0).f1);
+	}
+
+	@Test
+	public void testMessagePackableUnpackableClass01() throws Exception {
+		BaseMessagePackableUnpackableClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		BaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw, BaseMessagePackableUnpackableClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class BaseMessagePackableUnpackableClass {
+		public MessagePackableUnpackableClass f0;
+		public int f1;
+		public List<MessagePackableUnpackableClass> f2;
+
+		public MessagePackableUnpackableClass getF0() {
+			return f0;
+		}
+
+		public void setF0(MessagePackableUnpackableClass f0) {
+			this.f0 = f0;
+		}
+
+		public int getF1() {
+			return f1;
+		}
+
+		public void setF1(int f1) {
+			this.f1 = f1;
+		}
+
+		public List<MessagePackableUnpackableClass> getF2() {
+			return f2;
+		}
+
+		public void setF2(List<MessagePackableUnpackableClass> f2) {
+			this.f2 = f2;
+		}
+
+		public BaseMessagePackableUnpackableClass() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class MessagePackableUnpackableClass implements
+			MessagePackable, MessageUnpackable {
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		public int getF1() {
+			return f1;
+		}
+
+		public void setF1(int f1) {
+			this.f1 = f1;
+		}
+
+		public int f0;
+		public int f1;
+
+		public MessagePackableUnpackableClass() {
+		}
+
+		@Override
+		public void messagePack(Packer packer) throws IOException {
+			packer.packArray(2);
+			packer.pack(f0);
+			packer.pack(f1);
+		}
+
+		@Override
+		public void messageUnpack(Unpacker unpacker) throws IOException,
+				MessageTypeException {
+			if (unpacker.tryUnpackNull()) {
+				return;
+			}
+			unpacker.unpackArray();
+			f0 = unpacker.unpackInt();
+			f1 = unpacker.unpackInt();
+		}
+	}
+
+	@Test
+	public void testOptionalMessagePackableUnpackableClass00() throws Exception {
+		OptionalBaseMessagePackableUnpackableClass src = new OptionalBaseMessagePackableUnpackableClass();
+		OptionalMessagePackableUnpackableClass src1 = new OptionalMessagePackableUnpackableClass();
+		List<OptionalMessagePackableUnpackableClass> src2 = new ArrayList<OptionalMessagePackableUnpackableClass>();
+		src1.f0 = 0;
+		src1.f1 = 1;
+		src.f0 = src1;
+		src.f1 = 1;
+		src2.add(src1);
+		src.f2 = src2;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw, OptionalBaseMessagePackableUnpackableClass.class);
+		assertEquals(src.f0.f0, dst.f0.f0);
+		assertEquals(src.f0.f1, dst.f0.f1);
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2.size(), dst.f2.size());
+		assertEquals(src.f2.get(0).f0, dst.f2.get(0).f0);
+		assertEquals(src.f2.get(0).f1, dst.f2.get(0).f1);
+	}
+
+	@Test
+	public void testOptionalMessagePackableUnpackableClass01() throws Exception {
+		OptionalBaseMessagePackableUnpackableClass src = new OptionalBaseMessagePackableUnpackableClass();
+		src.f0 = null;
+		src.f1 = 1;
+		src.f2 = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw, OptionalBaseMessagePackableUnpackableClass.class);
+		assertEquals(src.f0, dst.f0);
+		assertEquals(src.f1, dst.f1);
+		assertEquals(src.f2, dst.f2);
+	}
+
+	@Test
+	public void testOptionalMessagePackableUnpackableClass02() throws Exception {
+		OptionalBaseMessagePackableUnpackableClass src = null;
+
+		byte[] raw = MessagePack.pack(src);
+
+		OptionalBaseMessagePackableUnpackableClass dst =
+			MessagePack.unpack(raw, OptionalBaseMessagePackableUnpackableClass.class);
+		assertEquals(src, dst);
+	}
+
+	@MessagePackBeans
+	public static class OptionalBaseMessagePackableUnpackableClass {
+		@Optional
+		public OptionalMessagePackableUnpackableClass f0;
+		@Optional
+		public int f1;
+		@Optional
+		public List<OptionalMessagePackableUnpackableClass> f2;
+
+		@Optional
+		public OptionalMessagePackableUnpackableClass getF0() {
+			return f0;
+		}
+
+		public void setF0(OptionalMessagePackableUnpackableClass f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public int getF1() {
+			return f1;
+		}
+
+		public void setF1(int f1) {
+			this.f1 = f1;
+		}
+
+		@Optional
+		public List<OptionalMessagePackableUnpackableClass> getF2() {
+			return f2;
+		}
+
+		public void setF2(List<OptionalMessagePackableUnpackableClass> f2) {
+			this.f2 = f2;
+		}
+
+		public OptionalBaseMessagePackableUnpackableClass() {
+		}
+	}
+
+	@MessagePackBeans
+	public static class OptionalMessagePackableUnpackableClass implements
+			MessagePackable, MessageUnpackable {
+		@Optional
+		public int getF0() {
+			return f0;
+		}
+
+		public void setF0(int f0) {
+			this.f0 = f0;
+		}
+
+		@Optional
+		public int getF1() {
+			return f1;
+		}
+
+		public void setF1(int f1) {
+			this.f1 = f1;
+		}
+
+		@Optional
+		public int f0;
+		@Optional
+		public int f1;
+
+		public OptionalMessagePackableUnpackableClass() {
+		}
+
+		@Override
+		public void messagePack(Packer packer) throws IOException {
+			packer.packArray(2);
+			packer.pack(f0);
+			packer.pack(f1);
+		}
+
+		@Override
+		public void messageUnpack(Unpacker unpacker) throws IOException,
+				MessageTypeException {
+			if (unpacker.tryUnpackNull()) {
+				return;
+			}
+			unpacker.unpackArray();
+			f0 = unpacker.unpackInt();
+			f1 = unpacker.unpackInt();
+		}
+	}
+}
+
diff --git a/java/src/test/java/org/msgpack/template/TestTemplateBuilderPackConvert.java b/java/src/test/java/org/msgpack/template/TestTemplateBuilderPackConvert.java
index a99fa260..f316e64a 100644
--- a/java/src/test/java/org/msgpack/template/TestTemplateBuilderPackConvert.java
+++ b/java/src/test/java/org/msgpack/template/TestTemplateBuilderPackConvert.java
@@ -24,7 +24,10 @@ import org.msgpack.Unpacker;
 import org.msgpack.annotation.MessagePackMessage;
 import org.msgpack.annotation.MessagePackOrdinalEnum;
 import org.msgpack.annotation.Optional;
+import org.msgpack.template.builder.BuilderSelectorRegistry;
+import org.msgpack.template.builder.TemplateBuilder;
 
+import junit.framework.Assert;
 import junit.framework.TestCase;
 
 public class TestTemplateBuilderPackConvert extends TestCase {
@@ -714,7 +717,9 @@ public class TestTemplateBuilderPackConvert extends TestCase {
 	@Test
 	public void testFinalClass() throws Exception {
 		try {
-			TemplateBuilder.build(FinalModifierClass.class);
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(FinalModifierClass.class);
+			Assert.assertNull(builder);// no available builder
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(FinalModifierClass.class);
 			assertTrue(true);
 		} catch (TemplateBuildException e) {
 			fail();
@@ -731,7 +736,9 @@ public class TestTemplateBuilderPackConvert extends TestCase {
 	@Test
 	public void testInterfaceType00() throws Exception {
 		try {
-			TemplateBuilder.build(SampleInterface.class);
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(SampleInterface.class);
+			Assert.assertNull(builder);// no available builder
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(SampleInterface.class);
 			fail();
 		} catch (TemplateBuildException e) {
 			assertTrue(true);
@@ -742,7 +749,9 @@ public class TestTemplateBuilderPackConvert extends TestCase {
 	@Test
 	public void testInterfaceType01() throws Exception {
 		try {
-			TemplateBuilder.build(SampleInterface.class);
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(SampleInterface.class);
+			Assert.assertNull(builder);// no available builder
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(SampleInterface.class);
 			fail();
 		} catch (TemplateBuildException e) {
 			assertTrue(true);
diff --git a/java/src/test/java/org/msgpack/template/TestTemplateBuilderPackUnpack.java b/java/src/test/java/org/msgpack/template/TestTemplateBuilderPackUnpack.java
index a2430a43..9ac94ef7 100644
--- a/java/src/test/java/org/msgpack/template/TestTemplateBuilderPackUnpack.java
+++ b/java/src/test/java/org/msgpack/template/TestTemplateBuilderPackUnpack.java
@@ -24,7 +24,11 @@ import org.msgpack.Unpacker;
 import org.msgpack.annotation.MessagePackMessage;
 import org.msgpack.annotation.MessagePackOrdinalEnum;
 import org.msgpack.annotation.Optional;
+import org.msgpack.template.TestTemplateBuilderPackConvert.SampleInterface;
+import org.msgpack.template.builder.BuilderSelectorRegistry;
+import org.msgpack.template.builder.TemplateBuilder;
 
+import junit.framework.Assert;
 import junit.framework.TestCase;
 
 public class TestTemplateBuilderPackUnpack extends TestCase {
@@ -714,7 +718,9 @@ public class TestTemplateBuilderPackUnpack extends TestCase {
 	@Test
 	public void testFinalClass() throws Exception {
 		try {
-			TemplateBuilder.build(FinalModifierClass.class);
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(FinalModifierClass.class);
+			Assert.assertNull(builder);
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(FinalModifierClass.class);
 			assertTrue(true);
 		} catch (TemplateBuildException e) {
 			fail();
@@ -731,7 +737,9 @@ public class TestTemplateBuilderPackUnpack extends TestCase {
 	@Test
 	public void testInterfaceType00() throws Exception {
 		try {
-			TemplateBuilder.build(SampleInterface.class);
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(SampleInterface.class);
+			Assert.assertNull(builder);
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(SampleInterface.class);
 			fail();
 		} catch (TemplateBuildException e) {
 			assertTrue(true);
@@ -742,7 +750,9 @@ public class TestTemplateBuilderPackUnpack extends TestCase {
 	@Test
 	public void testInterfaceType01() throws Exception {
 		try {
-			TemplateBuilder.build(SampleInterface.class);
+			TemplateBuilder builder = BuilderSelectorRegistry.getInstance().select(SampleInterface.class);
+			Assert.assertNull(builder);
+			BuilderSelectorRegistry.getInstance().getForceBuilder().buildTemplate(SampleInterface.class);
 			fail();
 		} catch (TemplateBuildException e) {
 			assertTrue(true);
diff --git a/scala/.gitignore b/scala/.gitignore
new file mode 100644
index 00000000..97250138
--- /dev/null
+++ b/scala/.gitignore
@@ -0,0 +1,40 @@
+# use glob syntax.
+syntax: glob
+*.ser
+*.class
+*~
+*.bak
+#*.off
+*.old
+
+# eclipse conf file
+.settings
+.classpath
+.project
+.manager
+.scala_dependencies
+
+# idea
+.idea
+*.iml
+
+# building
+target
+build
+null
+tmp*
+dist
+test-output
+build.log
+
+# other scm
+.svn
+.CVS
+.hg*
+
+# switch to regexp syntax.
+#  syntax: regexp
+#  ^\.pc/
+
+#SHITTY output not in target directory
+build.log
diff --git a/scala/pom.xml b/scala/pom.xml
new file mode 100644
index 00000000..b6a14f2c
--- /dev/null
+++ b/scala/pom.xml
@@ -0,0 +1,158 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.msgpack</groupId>
+  <artifactId>scala-msgpack</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+  <name>${project.artifactId}</name>
+  <description>My wonderfull scala app</description>
+  <inceptionYear>2010</inceptionYear>
+  <licenses>
+    <license>
+      <name>My License</name>
+      <url>http://....</url>
+      <distribution>repo</distribution>
+    </license>
+  </licenses>
+
+  <properties>
+    <maven.compiler.source>1.5</maven.compiler.source>
+    <maven.compiler.target>1.5</maven.compiler.target>
+    <encoding>UTF-8</encoding>
+    <scala.version>2.8.1</scala.version>
+  </properties>
+    <repositories>
+      <repository>
+      <id>msgpack.org</id>
+      <name>MessagePack Repository for Maven</name>
+      <url>http://msgpack.org/maven2/</url>
+      </repository>
+    </repositories>
+
+<!--
+  <repositories>
+    <repository>
+      <id>scala-tools.org</id>
+      <name>Scala-Tools Maven2 Repository</name>
+      <url>http://scala-tools.org/repo-releases</url>
+    </repository>
+  </repositories>
+
+  <pluginRepositories>
+    <pluginRepository>
+      <id>scala-tools.org</id>
+      <name>Scala-Tools Maven2 Repository</name>
+      <url>http://scala-tools.org/repo-releases</url>
+    </pluginRepository>
+  </pluginRepositories>
+-->
+  <dependencies>
+    <dependency>
+      <groupId>org.scala-lang</groupId>
+      <artifactId>scala-library</artifactId>
+      <version>${scala.version}</version>
+    </dependency>
+
+    <!-- Test -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.8.1</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.scala-tools.testing</groupId>
+      <artifactId>specs_${scala.version}</artifactId>
+      <version>1.6.6</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.msgpack</groupId>
+        <artifactId>msgpack</artifactId>
+        <version>0.5.2-SNAPSHOT</version>
+    </dependency>
+  </dependencies>
+
+  <distributionManagement>
+    <repository>
+      <uniqueVersion>false</uniqueVersion>
+      <id>msgpack.org</id>
+      <name>Repository at msgpack.org</name>
+	  <!--<url>file://${project.build.directory}/website/maven2/</url>-->
+	  <url>${deploy-release-url}</url>
+    </repository>
+    <snapshotRepository>
+       <uniqueVersion>true</uniqueVersion>
+       <id>msgpack.org</id>
+       <name>Repository at msgpack.org</name>
+	   <!--<url>file://${project.build.directory}/website/maven2/</url>-->
+	   <url>${deploy-snapshot-url}</url>
+    </snapshotRepository>
+  </distributionManagement>
+  
+  <build>
+    <sourceDirectory>src/main/scala</sourceDirectory>
+    <testSourceDirectory>src/test/scala</testSourceDirectory>
+    <plugins>
+      <plugin>
+        <groupId>org.scala-tools</groupId>
+        <artifactId>maven-scala-plugin</artifactId>
+        <version>2.15.0</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>compile</goal>
+              <goal>testCompile</goal>
+            </goals>
+            <configuration>
+              <args>
+                <arg>-make:transitive</arg>
+                <arg>-dependencyfile</arg>
+                <arg>${project.build.directory}/.scala_dependencies</arg>
+              </args>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <version>2.6</version>
+        <configuration>
+          <useFile>false</useFile>
+          <disableXmlReport>true</disableXmlReport>
+          <!-- If you have classpath issue like NoDefClassError,... -->
+          <!-- useManifestOnlyJar>false</useManifestOnlyJar -->
+          <includes>
+            <include>**/*Test.*</include>
+            <include>**/*Suite.*</include>
+          </includes>
+        </configuration>
+      </plugin>
+      <plugin>
+	    <groupId>org.apache.maven.plugins</groupId>
+	    <artifactId>maven-surefire-report-plugin</artifactId>
+      </plugin>
+      <plugin>
+    <artifactId>maven-assembly-plugin</artifactId>
+    <configuration>
+      <descriptorRefs>
+            <!-- ここはこのままでOK -->
+            <descriptorRef>jar-with-dependencies</descriptorRef>
+      </descriptorRefs>
+
+    </configuration>
+        <executions>
+          <!-- ここでpackageのPhaseで実行されるように設定している-->
+          <execution>
+            <id>make-assembly</id> <!-- this is used for inheritance merges -->
+            <phase>package</phase> <!-- append to the packaging phase. -->
+            <goals>
+              <goal>single</goal> <!-- goals == mojos -->
+            </goals>
+          </execution>
+        </executions>
+
+    </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/scala/src/main/scala/org/msgpack/App.scala b/scala/src/main/scala/org/msgpack/App.scala
new file mode 100644
index 00000000..f80d7a0d
--- /dev/null
+++ b/scala/src/main/scala/org/msgpack/App.scala
@@ -0,0 +1,15 @@
+package org.msgpack
+
+/**
+ * @author ${user.name}
+ */
+object App {
+  
+  def foo(x : Array[String]) = x.foldLeft("")((a,b) => a + b)
+  
+  def main(args : Array[String]) {
+    println( "Hello World!" )
+    println("concat arguments = " + foo(args))
+  }
+
+}
diff --git a/scala/src/main/scala/org/msgpack/JavassistTypeScalaTemplateBuilder.scala b/scala/src/main/scala/org/msgpack/JavassistTypeScalaTemplateBuilder.scala
new file mode 100644
index 00000000..879bddea
--- /dev/null
+++ b/scala/src/main/scala/org/msgpack/JavassistTypeScalaTemplateBuilder.scala
@@ -0,0 +1,521 @@
+package org.msgpack
+
+import _root_.javassist.{CtClass, CtNewConstructor}
+import annotation._
+import template._
+import java.lang.Class
+import collection.immutable.{ListMap, TreeMap}
+import java.lang.reflect.{Type, Modifier, Method, Field}
+import java.lang.annotation.{Annotation => JavaAnnotation}
+import javassist.{JavassistTemplate, BuildContextBase, BuildContext}
+import scala.collection.JavaConverters._
+;
+/*
+ * Created by IntelliJ IDEA.
+ * User: takeshita
+ * Date: 11/03/10
+ * Time: 12:29
+ */
+
+
+  class BuildContextForScala(builder : JavassistTemplateBuilder) extends BuildContextBase[IFieldEntry](builder){
+
+    var entries : Array[IFieldEntry] = null
+    var origClass : Class[_] = null
+    var origName : String = null
+    var templates : Array[Template] = null
+    var minimumArrayLength : Int = 0
+
+    def buildTemplate(targetClass : Class[_] ,  entries : Array[IFieldEntry],  templates : Array[Template]) = {
+      this.entries = entries;
+      this.templates = templates;
+      this.origClass = targetClass;
+      this.origName = this.origClass.getName();
+      build(this.origName);
+	}
+
+    def setSuperClass() = {
+      tmplCtClass.setSuperclass(director.getCtClass(classOf[JavassistTemplate].getName))
+    }
+
+    def buildConstructor() = {
+      val newCtCons = CtNewConstructor.make(
+        Array[CtClass](
+          director.getCtClass(classOf[Class[_]].getName),
+          director.getCtClass(classOf[Template].getName + "[]")
+        ),
+      new Array[CtClass](0),
+      tmplCtClass
+      )
+      this.tmplCtClass.addConstructor(newCtCons)
+    }
+    def buildInstance(c : Class[_]) = {
+      val cons = c.getConstructor(classOf[Class[_]], classOf[Array[Template]])
+      val tmpl = cons.newInstance(origClass,templates)
+      tmpl.asInstanceOf[Template]
+    }
+    override def buildMethodInit() = {
+      this.minimumArrayLength = 0;
+      var i : Int = 0
+      for(e <- entries) {
+        if(e.isRequired() || e.isNullable()) {
+          this.minimumArrayLength = i+1;
+        }
+        i += 1
+      }
+    }
+
+    lazy val newInstanceDeclaration : String = {
+
+      def defCon = "new " + origClass.getName + "();"
+      try{
+        val c = origClass.getClassLoader.loadClass(origClass.getName + "$")
+        if(Modifier.isPublic(c.getModifiers)){
+          val method = c.getMethod("apply")
+
+          if(Modifier.isPublic(method.getModifiers) &&
+            origClass.isAssignableFrom(method.getReturnType)){
+            val staticField = c.getDeclaredField("MODULE$")
+             "%s.%s.apply();".format(c.getName,staticField.getName)
+          }else{
+            defCon
+          }
+        }else{
+          defCon
+        }
+      }catch{
+        case e : ClassNotFoundException => {
+          defCon
+        }
+      }
+    }
+
+    protected def buildPackMethodBody() : String = {
+      resetStringBuilder();
+      buildString("{");
+      buildString("%s _$$_t = (%s)$2;", this.origName, this.origName);
+      buildString("$1.packArray(%d);", entries.length.asInstanceOf[AnyRef]);
+      for(i <- 0 until entries.length) {
+        val e = entries(i)
+
+        if(!e.isAvailable) {
+          buildString("$1.packNil();");
+        }else{
+          val t = e.getType;
+          if(t.isPrimitive()) {
+            buildString("$1.%s(_$$_t.%s());", primitivePackName(t), e.getName());
+          } else {
+            buildString("if(_$$_t.%s() == null) {", e.getName());
+            if(!e.isNullable() && !e.isOptional()) {
+              buildString("throw new %s();", classOf[MessageTypeException].getName());
+            } else {
+              buildString("$1.packNil();");
+            }
+            buildString("} else {");
+            buildString("  this.templates[%d].pack($1, _$$_t.%s());", i.asInstanceOf[AnyRef], e.getName());
+            buildString("}");
+          }
+        }
+
+      }
+      buildString("}");
+      return getBuiltString();
+    }
+
+
+    def buildUnpackMethodBody() : String = {
+        resetStringBuilder();
+        buildString("{ ");
+
+        buildString("%s _$$_t;", this.origName);
+        buildString("if($2 == null) {");
+        buildString("  _$$_t = " + newInstanceDeclaration) //new %s();", this.origName);
+        buildString("} else {");
+        buildString("  _$$_t = (%s)$2;", this.origName);
+        buildString("}");
+
+        buildString("int length = $1.unpackArray();");
+        buildString("if(length < %d) {", this.minimumArrayLength.asInstanceOf[AnyRef]);
+        buildString("  throw new %s();", classOf[MessageTypeException].getName());
+        buildString("}");
+
+        for(i <- 0 until this.minimumArrayLength) {
+          val e = entries(i);
+          if(!e.isAvailable()) {
+            buildString("$1.unpackObject();");
+          }else{
+
+            buildString("if($1.tryUnpackNull()) {");
+              if(e.isRequired()) {
+                // Required + nil => exception
+                buildString("throw new %s();", classOf[MessageTypeException].getName());
+              } else if(e.isOptional()) {
+                // Optional + nil => keep default value
+              } else {  // Nullable
+                // Nullable + nil => set null
+                buildString("_$$_t.%s_$eq(null);", e.getName());
+              }
+            buildString("} else {");
+              val t = e.getType();
+              if(t.isPrimitive()) {
+                buildString("_$$_t.%s_$eq( $1.%s() );", e.getName(), primitiveUnpackName(t));
+              } else {
+                buildString("_$$_t.%s_$eq( (%s)this.templates[%d].unpack($1, _$$_t.%s()));", e.getName(), e.getJavaTypeName(), i.asInstanceOf[AnyRef], e.getName());
+              }
+            buildString("}");
+          }
+        }
+
+        for(i <- this.minimumArrayLength until entries.length) {
+          buildString("if(length <= %d) { return _$$_t; }", i.asInstanceOf[AnyRef]);
+
+          val e = entries(i);
+          if(!e.isAvailable()) {
+            buildString("$1.unpackObject();");
+          }else{
+
+            buildString("if($1.tryUnpackNull()) {");
+              // this is Optional field becaue i >= minimumArrayLength
+              // Optional + nil => keep default value
+            buildString("} else {");
+              val t = e.getType();
+              if(t.isPrimitive()) {
+                buildString("_$$_t.%s_$eq( $1.%s());", e.getName(), primitiveUnpackName(t));
+              } else {
+                buildString("_$$_t.%s_$eq( (%s)this.templates[%d].unpack($1, _$$_t.%s) );", e.getName(), e.getJavaTypeName(), i.asInstanceOf[AnyRef], e.getName());
+              }
+            buildString("}");
+          }
+        }
+
+        // latter entries are all Optional + nil => keep default value
+
+        buildString("for(int i=%d; i < length; i++) {", entries.length.asInstanceOf[AnyRef]);
+        buildString("  $1.unpackObject();");
+        buildString("}");
+
+        buildString("return _$$_t;");
+
+        buildString("}");
+        return getBuiltString();
+      }
+
+    protected override def buildConvertMethodBody() : String = {
+      resetStringBuilder();
+      buildString("{ ");
+
+      buildString("%s _$$_t;", this.origName);
+      buildString("if($2 == null) {");
+      buildString("  _$$_t = " + newInstanceDeclaration) //new %s();", this.origName);
+      buildString("} else {");
+      buildString("  _$$_t = (%s)$2;", this.origName);
+      buildString("}");
+
+      buildString("%s[] array = $1.asArray();", classOf[MessagePackObject].getName());
+      buildString("int length = array.length;");
+      buildString("if(length < %d) {", this.minimumArrayLength.asInstanceOf[AnyRef]);
+      buildString("  throw new %s();", classOf[MessageTypeException].getName());
+      buildString("}");
+
+      buildString("%s obj;", classOf[MessagePackObject].getName());
+
+      for(i <- 0 until this.minimumArrayLength) {
+        val e = entries(i);
+        if(e.isAvailable()) {
+          buildString("obj = array[%d];", i.asInstanceOf[AnyRef]);
+          buildString("if(obj.isNil()) {");
+            if(e.isRequired()) {
+              // Required + nil => exception
+              buildString("throw new %s();", classOf[MessageTypeException].getName());
+            } else if(e.isOptional()) {
+              // Optional + nil => keep default value
+            } else {  // Nullable
+              // Nullable + nil => set null
+              buildString("_$$_t.%s_$eq( null );", e.getName());
+            }
+          buildString("} else {");
+            val t = e.getType();
+            if(t.isPrimitive()) {
+              buildString("_$$_t.%s_$eq( obj.%s());", e.getName(), primitiveConvertName(t));
+            } else {
+              buildString("_$$_t.%s_$eq( (%s)this.templates[%d].convert(obj, _$$_t.%s()) );", e.getName(), e.getJavaTypeName(), i.asInstanceOf[AnyRef], e.getName());
+            }
+          buildString("}");
+        }
+      }
+
+      for(i <- this.minimumArrayLength until entries.length) {
+        buildString("if(length <= %d) { return _$$_t; }", i.asInstanceOf[AnyRef]);
+
+        val e = entries(i);
+        if(e.isAvailable()) {
+
+
+          buildString("obj = array[%d];", i.asInstanceOf[AnyRef]);
+          buildString("if(obj.isNil()) {");
+            // this is Optional field becaue i >= minimumArrayLength
+            // Optional + nil => keep default value
+          buildString("} else {");
+            val t = e.getType();
+            if(t.isPrimitive()) {
+              buildString("_$$_t.%s_$eq( obj.%s());", e.getName(), primitiveConvertName(t));
+            } else {
+              buildString("_$$_t.%s_$eq( (%s)this.templates[%d].convert(obj, _$$_t.%s) );", e.getName(), e.getJavaTypeName(), i.asInstanceOf[AnyRef], e.getName());
+            }
+          buildString("}");
+        }
+      }
+
+      // latter entries are all Optional + nil => keep default value
+
+      buildString("return _$$_t;");
+
+      buildString("}");
+      return getBuiltString();
+    }
+  }
+
+class ScalaFieldEntryReader extends IFieldEntryReader{
+
+  type Property = (Method,Method,Field)
+  type PropertySet = (String,Property)
+
+  def readImplicitFieldOption(targetClass: Class[_]) = {
+    FieldOption.NULLABLE
+  }
+
+  def convertFieldEntries(targetClass: Class[_], flist: FieldList) = {
+
+    val list : List[FieldList.Entry] = flist.getList.asScala.toList
+
+    list.map( s => {
+      if(s.isAvailable){
+        val getter = targetClass.getMethod(s.getName)
+        if(getter.getReturnType.getName != "void"){
+          val setter = targetClass.getMethod(s.getName + "_$eq",getter.getReturnType)
+          if(setter.getReturnType.getName == "void"){
+            val f = try{targetClass.getDeclaredField(s.getName)}
+            catch{
+              case e : NoSuchFieldException => null
+            }
+            val prop = (s.getName,(getter,setter,f))
+            convertToScalaFieldEntry(prop)
+          }else{
+            new ScalaFieldEntry("")
+          }
+        }else new ScalaFieldEntry("")
+      }else{
+        new ScalaFieldEntry("")
+      }
+    }).toArray
+  }
+
+  def readFieldEntries(targetClass: Class[_], implicitOption: FieldOption) = {
+    val props = findPropertyMethods(targetClass) filter( !hasAnnotation(_,classOf[Ignore]))
+
+    val indexed = indexing(props)
+    indexed.map(convertToScalaFieldEntry(_))
+  }
+
+
+  def setter_?(method : Method) : Boolean = {
+    Modifier.isPublic(method.getModifiers) &&
+    method.getReturnType.getName == "void" &&
+    method.getName.endsWith("_$eq") &&
+    method.getParameterTypes.length == 1
+  }
+
+  def getter_?(method : Method) : Boolean = {
+    Modifier.isPublic(method.getModifiers) &&
+    method.getReturnType.getName != "void" &&
+    method.getParameterTypes.length == 0
+
+  }
+
+
+
+
+  def findPropertyMethods(targetClass: Class[_]) : Map[String,Property] = {
+    var getters : Map[String,Method] = ListMap.empty
+    var setters : Map[String,Method] = ListMap.empty
+
+    def extractName( n : String) = {
+      n.substring(0,n.length - 4)
+    }
+
+    //Find getters and setters
+    for( m <- targetClass.getMethods){
+      if(setter_?(m)){
+        setters +=(extractName(m.getName) -> m)
+      }else if(getter_?(m)){
+        getters +=(m.getName -> m)
+      }
+    }
+
+    var props : Map[String,Property] = ListMap.empty
+
+    def sameType_?( getter : Method,setter : Method) = {
+      getter.getReturnType == setter.getParameterTypes()(0)
+    }
+
+    for(g <- getters){
+      setters.get(g._1).map( s => {
+        if(sameType_?(g._2,s)){
+
+          val name = g._1
+          val f = try{targetClass.getDeclaredField(name)}
+          catch{
+            case e : NoSuchFieldException => null
+          }
+
+          //TODO add validation for field
+          props +=( name -> (g._2,s,f))
+        }
+      })
+    }
+    props
+  }
+
+  def indexing( props : Map[String , Property]) : Array[PropertySet] = {
+    val indexed = new Array[PropertySet](props.size)
+
+    var notIndexed : List[PropertySet]  = Nil
+
+    for(s <- props){
+      val i = getAnnotation(s,classOf[Index])
+      if(i == null){
+        notIndexed = notIndexed :+ s
+      }else{
+        val index = i.value
+        if(indexed(index) != null){
+          throw new TemplateBuildException("duplicated index: "+index);
+        }else{
+          try{
+            indexed(index) = s
+          }catch{
+            case e : Exception => {
+              throw new TemplateBuildException("invalid index: %s index must be 0 <= x < %s".format(index,indexed.length));
+            }
+          }
+        }
+      }
+    }
+
+    for( i <- 0 until indexed.length ){
+      if(indexed(i) == null){
+        indexed(i) = notIndexed.head
+        notIndexed = notIndexed.drop(1)
+      }
+    }
+
+
+    indexed
+  }
+
+  def convertToScalaFieldEntry( propInfo : PropertySet) = {
+    val entry = new ScalaFieldEntry(propInfo._1)
+    entry.option = readFieldOption(propInfo,FieldOption.NULLABLE)
+    entry.normalType = readValueType(propInfo)
+    entry.genericType = readGenericType(propInfo)
+
+    entry
+  }
+
+
+  def hasAnnotation[T <: JavaAnnotation](prop : PropertySet , classOfAnno : Class[T]) : Boolean = {
+    val getter = prop._2._1
+    val setter = prop._2._2
+    val field = prop._2._3
+    getter.getAnnotation(classOfAnno) != null ||
+    setter.getAnnotation(classOfAnno) != null ||
+      {if(field != null) field.getAnnotation(classOfAnno) != null
+    else false}
+  }
+  def getAnnotation[T <: JavaAnnotation](prop : PropertySet , classOfAnno : Class[T]) : T = {
+    val getter = prop._2._1
+    val setter = prop._2._2
+    val field = prop._2._3
+
+
+
+    val a = getter.getAnnotation(classOfAnno)
+    if(a != null){
+      a
+    }else{
+      val b = setter.getAnnotation(classOfAnno)
+      if(b != null){
+        b
+      }else if(field != null){
+        field.getAnnotation(classOfAnno)
+      }else{
+        null.asInstanceOf[T]
+      }
+    }
+  }
+
+  def readFieldOption(prop : PropertySet , implicitOption : FieldOption) = {
+    if(hasAnnotation(prop,classOf[Required])){
+      FieldOption.REQUIRED
+    } else if(hasAnnotation(prop,classOf[Optional])){
+      FieldOption.OPTIONAL
+    } else if(hasAnnotation(prop,classOf[Nullable])){
+      if(readValueType(prop).isPrimitive){
+        FieldOption.REQUIRED
+      }else{
+        FieldOption.NULLABLE
+      }
+    } else{
+      if(implicitOption == FieldOption.NULLABLE){
+        if(readValueType(prop).isPrimitive){
+          FieldOption.REQUIRED
+        }else{
+          FieldOption.NULLABLE
+        }
+      }else{
+        implicitOption
+      }
+    }
+
+  }
+
+  def readValueType(prop : PropertySet) = {
+    prop._2._1.getReturnType
+  }
+  def readGenericType(prop : PropertySet) = {
+    prop._2._1.getGenericReturnType
+  }
+}
+
+class ScalaFieldEntry(name : String) extends IFieldEntry{
+
+  def getName() = name
+
+  def isNullable() = {getOption == FieldOption.NULLABLE}
+
+  def isOptional = {getOption == FieldOption.OPTIONAL}
+
+  def isRequired = {getOption == FieldOption.REQUIRED}
+
+  def isAvailable = {getOption != FieldOption.IGNORE}
+
+  var option : FieldOption = null
+
+  var genericType : Type = null
+
+  def getJavaTypeName = {
+    if(getType.isArray){
+      //TODO implement here
+      getType.getName()
+    }else{
+      getType.getName()
+    }
+
+  }
+
+  var normalType : Class[_] = null
+
+  def getOption() = option
+  def getType() = normalType
+  def getGenericType() = genericType
+
+}
\ No newline at end of file
diff --git a/scala/src/main/scala/org/msgpack/ScalaMessagePack.scala b/scala/src/main/scala/org/msgpack/ScalaMessagePack.scala
new file mode 100644
index 00000000..45b6f5fc
--- /dev/null
+++ b/scala/src/main/scala/org/msgpack/ScalaMessagePack.scala
@@ -0,0 +1,76 @@
+package org.msgpack
+
+import template._
+import builder.{MessagePackMessageBuilderSelector, BuilderSelectorRegistry}
+import template.javassist.BuildContextFactory
+import collection.mutable.{MutableList, LinkedList}
+import collection.mutable.{Map => MMap,HashMap => MHashMap}
+;
+/*
+ * Created by IntelliJ IDEA.
+ * User: takeshita
+ * Date: 11/03/10
+ * Time: 1:34
+ */
+
+object ScalaMessagePack {
+
+  {
+    // for scala object
+    BuilderSelectorRegistry.getInstance.insertBefore(
+      MessagePackMessageBuilderSelector.NAME,
+      new ScalaTemplateBuilderSelector)
+
+    // register scala's list classes
+    TemplateRegistry.register(classOf[List[_]],new ImmutableListTemplate(AnyTemplate.getInstance))
+    TemplateRegistry.registerGeneric(classOf[List[_]],new GenericTemplate1(classOf[ImmutableListTemplate]))
+    TemplateRegistry.register(classOf[Seq[_]],new ImmutableListTemplate(AnyTemplate.getInstance))
+    TemplateRegistry.registerGeneric(classOf[Seq[_]],new GenericTemplate1(classOf[ImmutableListTemplate]))
+    TemplateRegistry.register(classOf[LinkedList[_]],new LinkedListTemplate(AnyTemplate.getInstance))
+    TemplateRegistry.registerGeneric(classOf[LinkedList[_]],new GenericTemplate1(classOf[LinkedListTemplate]))
+    TemplateRegistry.register(classOf[MutableList[_]],new MutableListCTemplate(AnyTemplate.getInstance))
+    TemplateRegistry.registerGeneric(classOf[MutableList[_]],new GenericTemplate1(classOf[MutableListCTemplate]))
+
+    // register scala's map classes
+    TemplateRegistry.register(classOf[Map[_,_]],new ImmutableMapTemplate(
+      AnyTemplate.getInstance,AnyTemplate.getInstance))
+    TemplateRegistry.registerGeneric(classOf[Map[_,_]],new GenericTemplate2(
+      classOf[ImmutableMapTemplate]))
+    TemplateRegistry.register(classOf[MMap[_,_]],new MutableHashMapTemplate(
+      AnyTemplate.getInstance,AnyTemplate.getInstance))
+    TemplateRegistry.registerGeneric(classOf[MMap[_,_]],new GenericTemplate2(
+      classOf[MutableHashMapTemplate]))
+    TemplateRegistry.register(classOf[MHashMap[_,_]],new MutableHashMapTemplate(
+      AnyTemplate.getInstance,AnyTemplate.getInstance))
+    TemplateRegistry.registerGeneric(classOf[MHashMap[_,_]],new GenericTemplate2(
+      classOf[MutableHashMapTemplate]))
+
+
+
+
+  }
+
+  /**
+   * dammy method for initialize
+   */
+  def init() = {}
+
+
+  def pack( obj : Any) = {
+    MessagePack.pack(obj.asInstanceOf[AnyRef])
+  }
+
+  def unpack[T]( buffer : Array[Byte])(implicit manifest : ClassManifest[T]) : T = {
+    MessagePack.unpack[T]( buffer, manifest.erasure.asInstanceOf[Class[T]])
+  }
+
+  def unpackD(buffer : Array[Byte]) = {
+    MessagePack.unpack(buffer)
+  }
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/scala/src/main/scala/org/msgpack/ScalaTemplateBuilder.scala b/scala/src/main/scala/org/msgpack/ScalaTemplateBuilder.scala
new file mode 100644
index 00000000..f8cc2a7a
--- /dev/null
+++ b/scala/src/main/scala/org/msgpack/ScalaTemplateBuilder.scala
@@ -0,0 +1,16 @@
+package org.msgpack;
+/*
+ * Created by IntelliJ IDEA.
+ * User: takeshita
+ * Date: 11/03/10
+ * Time: 1:52
+ */
+
+class ScalaTemplateBuilder {
+
+
+  def pack(v : Int) = {
+
+  }
+
+}
\ No newline at end of file
diff --git a/scala/src/main/scala/org/msgpack/ScalaTemplateBuilderSelector.scala b/scala/src/main/scala/org/msgpack/ScalaTemplateBuilderSelector.scala
new file mode 100644
index 00000000..b8b7c122
--- /dev/null
+++ b/scala/src/main/scala/org/msgpack/ScalaTemplateBuilderSelector.scala
@@ -0,0 +1,48 @@
+package org.msgpack
+
+import annotation.MessagePackMessage
+import template.builder.BuilderSelector
+import java.lang.reflect.Type
+import template.javassist.BuildContextFactory
+import template.JavassistTemplateBuilder
+import java.lang.annotation.{Annotation => JAnnotation}
+;
+/*
+ * Created by IntelliJ IDEA.
+ * User: takeshita
+ * Date: 11/03/14
+ * Time: 17:59
+ */
+
+class ScalaTemplateBuilderSelector extends BuilderSelector
+{
+  val b = new JavassistTemplateBuilder()
+
+  {
+    b.setFieldEntryReader(new ScalaFieldEntryReader)
+    b.setBuildContextFactory(new BuildContextFactory{
+      def createBuildContext(builder: JavassistTemplateBuilder) = {
+        new BuildContextForScala(builder)
+      }
+    })
+  }
+
+  def getName = "ScalaMessagePackMessageTemplateBuilderSelector";
+
+  def getTemplateBuilder(targetType: Type) = {
+    b
+  }
+
+  def matchType(targetType: Type) = {
+    val c : Class[_] = targetType.asInstanceOf[Class[Object]]
+    isAnnotated(c, classOf[MessagePackMessage]) &&
+    classOf[ScalaObject].isAssignableFrom(c)//c.isAssignableFrom(classOf[ScalaObject])
+
+  }
+
+  private def isAnnotated(targetType : Class[_], annotation : Class[_ <: JAnnotation]) = {
+    targetType.getAnnotation(annotation) != null
+
+  }
+
+}
\ No newline at end of file
diff --git a/scala/src/main/scala/org/msgpack/template/ImmutableListTemplate.scala b/scala/src/main/scala/org/msgpack/template/ImmutableListTemplate.scala
new file mode 100644
index 00000000..5fab23ba
--- /dev/null
+++ b/scala/src/main/scala/org/msgpack/template/ImmutableListTemplate.scala
@@ -0,0 +1,49 @@
+package org.msgpack.template
+
+import org.msgpack._
+
+;
+/*
+ * Created by IntelliJ IDEA.
+ * User: takeshita
+ * Date: 11/03/11
+ * Time: 2:25
+ */
+
+class ImmutableListTemplate(elementTemplate : Template) extends Template{
+  def unpack(pac: Unpacker, to: AnyRef) = {
+
+    val length = pac.unpackArray();
+    val array : Array[Object] = new Array(length)
+
+    for(i <- 0 until length){
+      array(i) = elementTemplate.unpack(pac,null)
+    }
+
+    array.toList
+
+  }
+
+  def pack(pk: Packer, target: AnyRef) = {
+
+    val list = try{target.asInstanceOf[List[_]]}
+    catch{
+      case e : ClassCastException => {
+        throw new MessageTypeException("target is not List type: " + target.getClass());
+      }
+      case e : NullPointerException => {
+        throw new MessageTypeException(new NullPointerException("target is null."));
+      }
+    }
+
+		pk.packArray(list.size)
+    for( e <- list){
+      elementTemplate.pack(pk,e)
+    }
+
+  }
+
+  def convert(from: MessagePackObject, to: AnyRef) = {
+    from.asArray.map(elementTemplate.convert(_,null)).toList
+  }
+}
diff --git a/scala/src/main/scala/org/msgpack/template/ImmutableMapTemplate.scala b/scala/src/main/scala/org/msgpack/template/ImmutableMapTemplate.scala
new file mode 100644
index 00000000..d4dd01f4
--- /dev/null
+++ b/scala/src/main/scala/org/msgpack/template/ImmutableMapTemplate.scala
@@ -0,0 +1,54 @@
+package org.msgpack.template
+
+import org.msgpack._
+
+import scala.collection.JavaConverters._
+
+/*
+ * Created by IntelliJ IDEA.
+ * User: takeshita
+ * Date: 11/03/11
+ * Time: 11:11
+ */
+
+class ImmutableMapTemplate(keyTemplate : Template , valueTemplate : Template) extends Template {
+
+  def unpack(pac: Unpacker, to: AnyRef) = {
+
+    val length = pac.unpackMap()
+    val array : Array[(Object,Object)] = new Array(length)
+
+    for(i <- 0 until length){
+      array(i) = (keyTemplate.unpack(pac,null),valueTemplate.unpack(pac,null))
+    }
+
+    array.toMap
+
+  }
+
+  def pack(pk: Packer, target: AnyRef) = {
+
+    val map = try{target.asInstanceOf[Map[_,_]]}
+    catch{
+      case e : ClassCastException => {
+        throw new MessageTypeException("target is not List type: " + target.getClass());
+      }
+      case e : NullPointerException => {
+        throw new MessageTypeException(new NullPointerException("target is null."));
+      }
+    }
+		pk.packMap(map.size)
+    for( e <- map){
+      keyTemplate.pack(pk,e._1)
+      valueTemplate.pack(pk,e._2)
+    }
+
+  }
+
+  def convert(from: MessagePackObject, to: AnyRef) = {
+    from.asMap.asScala.map(p => (keyTemplate.convert(p._1,null),valueTemplate.convert(p._2,null))).toMap
+  }
+
+
+
+}
\ No newline at end of file
diff --git a/scala/src/main/scala/org/msgpack/template/MutableListTemplate.scala b/scala/src/main/scala/org/msgpack/template/MutableListTemplate.scala
new file mode 100644
index 00000000..c1973ae6
--- /dev/null
+++ b/scala/src/main/scala/org/msgpack/template/MutableListTemplate.scala
@@ -0,0 +1,56 @@
+package org.msgpack.template
+
+import org.msgpack.{MessagePackObject, Packer, Unpacker, Template}
+import collection.mutable.{MutableList, LinearSeq, LinkedList}
+;
+/*
+ * Created by IntelliJ IDEA.
+ * User: takeshita
+ * Date: 11/03/11
+ * Time: 2:37
+ */
+
+abstract class MutableListTemplate[T <: LinearSeq[_]](elementTemplate : Template)  extends Template{
+  def unpack(pac: Unpacker, to: AnyRef) = {
+
+    val length = pac.unpackArray();
+
+    var list : LinearSeq[_] = if(to == null){
+      toList(new Array[Object](0))
+    }else{
+      to.asInstanceOf[T]
+    }
+    for(i <- 0 until length){
+      list = list :+ elementTemplate.unpack(pac,null)
+    }
+
+    list
+
+  }
+  def toList(array : Array[Object]) : T
+
+  def pack(pk: Packer, target: AnyRef) = {
+
+    val list = target.asInstanceOf[LinearSeq[_]]
+		pk.packArray(list.size)
+    for( e <- list){
+      elementTemplate.pack(pk,e)
+    }
+
+  }
+
+  def convert(from: MessagePackObject, to: AnyRef) = {
+    toList(from.asArray.map(elementTemplate.convert(_,null)))
+  }
+}
+
+class LinkedListTemplate(elementTemplate : Template)   extends MutableListTemplate[LinkedList[_]](elementTemplate){
+  def toList(array : Array[Object]) = LinkedList(array :_*)
+}
+class MutableListCTemplate(elementTemplate : Template)   extends MutableListTemplate[MutableList[_]](elementTemplate){
+  def toList(array : Array[Object]) = {
+    val list : MutableList[Object] = new MutableList
+    list ++= array
+    list
+  }
+}
\ No newline at end of file
diff --git a/scala/src/main/scala/org/msgpack/template/MutableMapTemplate.scala b/scala/src/main/scala/org/msgpack/template/MutableMapTemplate.scala
new file mode 100644
index 00000000..0503f069
--- /dev/null
+++ b/scala/src/main/scala/org/msgpack/template/MutableMapTemplate.scala
@@ -0,0 +1,70 @@
+package org.msgpack.template;
+/*
+ * Created by IntelliJ IDEA.
+ * User: takeshita
+ * Date: 11/03/11
+ * Time: 12:06
+ */
+
+import org.msgpack._
+import collection.mutable.{HashMap,Map => MMap}
+import scala.collection.JavaConverters._
+
+abstract class MutableMapTemplate[T <: MMap[_,_]](keyTemplate : Template , valueTemplate : Template) extends Template {
+
+  def unpack(pac: Unpacker, to: AnyRef) = {
+
+    val length = pac.unpackMap()
+    val array : Array[(Object,Object)] = new Array(length)
+
+    for(i <- 0 until length){
+      array(i) = (keyTemplate.unpack(pac,null),valueTemplate.unpack(pac,null))
+    }
+
+    toMap(array)
+
+  }
+
+  def toMap(array : Array[(Object,Object)]) : T
+
+
+  def pack(pk: Packer, target: AnyRef) = {
+
+    val map = try{target.asInstanceOf[MMap[_,_]]}
+    catch{
+      case e : ClassCastException => {
+        throw new MessageTypeException("target is not List type: " + target.getClass());
+      }
+      case e : NullPointerException => {
+        throw new MessageTypeException(new NullPointerException("target is null."));
+      }
+    }
+		pk.packMap(map.size)
+    for( e <- map){
+      keyTemplate.pack(pk,e._1)
+      valueTemplate.pack(pk,e._2)
+    }
+
+
+  }
+
+  def convert(from: MessagePackObject, to: AnyRef) = {
+    toMap(from.asMap.asScala.map(p => (keyTemplate.convert(p._1,null),valueTemplate.convert(p._2,null))).toArray)
+
+  }
+
+
+
+}
+
+
+class MutableHashMapTemplate(keyTemplate : Template , valueTemplate : Template)
+  extends MutableMapTemplate[HashMap[_,_]](keyTemplate,valueTemplate ) {
+
+  def toMap(array : Array[(Object,Object)]) = {
+    HashMap(array :_*)
+  }
+
+
+
+}
diff --git a/scala/src/test/scala/org/msgpack/ClassWithList.scala b/scala/src/test/scala/org/msgpack/ClassWithList.scala
new file mode 100644
index 00000000..4fc5b4ff
--- /dev/null
+++ b/scala/src/test/scala/org/msgpack/ClassWithList.scala
@@ -0,0 +1,21 @@
+package org.msgpack;
+/*
+ * Created by IntelliJ IDEA.
+ * User: takeshita
+ * Date: 11/03/11
+ * Time: 2:13
+ */
+
+import annotation.MessagePackMessage
+import collection.mutable.{MutableList, LinkedList}
+
+@MessagePackMessage
+class ClassWithList {
+  var immutable : List[String] = Nil
+
+  var mutable : LinkedList[String] = LinkedList.empty
+
+  var mutable2 : MutableList[String] = new MutableList
+
+  //var tuple2 : (String,String) = (null,null)
+}
\ No newline at end of file
diff --git a/scala/src/test/scala/org/msgpack/ClassWithMap.scala b/scala/src/test/scala/org/msgpack/ClassWithMap.scala
new file mode 100644
index 00000000..6256dd76
--- /dev/null
+++ b/scala/src/test/scala/org/msgpack/ClassWithMap.scala
@@ -0,0 +1,19 @@
+package org.msgpack;
+/*
+ * Created by IntelliJ IDEA.
+ * User: takeshita
+ * Date: 11/03/11
+ * Time: 2:22
+ */
+
+import annotation.MessagePackMessage
+import scala.collection.mutable.{Map => MMap}
+
+@MessagePackMessage
+class ClassWithMap {
+
+  var immutable : Map[String,String] = Map.empty
+
+  var mutable : MMap[String,String] = MMap.empty
+
+}
\ No newline at end of file
diff --git a/scala/src/test/scala/org/msgpack/CollectionPackSpec.scala b/scala/src/test/scala/org/msgpack/CollectionPackSpec.scala
new file mode 100644
index 00000000..ec305507
--- /dev/null
+++ b/scala/src/test/scala/org/msgpack/CollectionPackSpec.scala
@@ -0,0 +1,71 @@
+package org.msgpack
+
+import org.junit.runner.RunWith
+import org.specs._
+import org.specs.matcher._
+import org.specs.runner.{ JUnitSuiteRunner, JUnit }
+import scala.collection.mutable.LinkedList
+
+//import org.scalacheck.Gen
+
+/**
+ * Sample specification.
+ * 
+ * This specification can be executed with: scala -cp <your classpath=""> ${package}.SpecsTest
+ * Or using maven: mvn test
+ *
+ * For more information on how to write or run specifications, please visit: http://code.google.com/p/specs.
+ *
+ */
+@RunWith(classOf[JUnitSuiteRunner])
+class CollectionPackSpec extends Specification with JUnit  {
+
+  "ScalaMessagePack" should {
+    "pack scala-list" in {
+      val c = new ClassWithList
+
+      c.immutable = List("a","b","c")
+      c.mutable = LinkedList("a","b","d")
+      c.mutable2 ++= List("gh","fjei")
+      //c.tuple2 = ("hoge","wahoo")
+
+      val b = ScalaMessagePack.pack(c)
+      val des = ScalaMessagePack.unpack[ClassWithList](b)
+
+      des.immutable must be_==(c.immutable)
+      des.mutable must be_==(c.mutable)
+      //des.tuple2 must be_==(c.tuple2)
+
+      val mpo = ScalaMessagePack.unpackD(b)
+      val des2 = mpo.convert(classOf[ClassWithList])
+
+      des2.immutable must be_==(c.immutable)
+      des2.mutable must be_==(c.mutable)
+
+    }
+    "pack scala-map" in {
+      val c = new ClassWithMap
+      c.immutable = Map("a" -> "hoge","b" -> "fuga","c" -> "hehe")
+      c.mutable = scala.collection.mutable.Map("d" -> "oo" , "e" -> "aa")
+
+      val b = ScalaMessagePack.pack(c)
+      val des = ScalaMessagePack.unpack[ClassWithMap](b)
+
+      des.immutable must be_==(c.immutable)
+      des.mutable must be_==(c.mutable)
+
+      val mpo = ScalaMessagePack.unpackD(b)
+      val des2 = mpo.convert(classOf[ClassWithMap])
+
+      des2.immutable must be_==(c.immutable)
+      des2.mutable must be_==(c.mutable)
+
+    }
+
+  }
+
+
+
+
+}
+
diff --git a/scala/src/test/scala/org/msgpack/SampleClass.scala b/scala/src/test/scala/org/msgpack/SampleClass.scala
new file mode 100644
index 00000000..4979ec61
--- /dev/null
+++ b/scala/src/test/scala/org/msgpack/SampleClass.scala
@@ -0,0 +1,77 @@
+package org.msgpack
+
+import annotation.MessagePackMessage
+import annotation.{Optional, Index}
+import java.util.Date
+;
+/*
+ * Created by IntelliJ IDEA.
+ * User: takeshita
+ * Date: 11/03/10
+ * Time: 1:35
+ */
+
+@MessagePackMessage
+class SampleClass {
+  var name : String = "hoge"
+  var number : Int = 2
+
+}
+
+trait SampleTrait {
+
+  var traitName : String = ""
+
+  var traitNum : Int = 12
+
+}
+
+class SampleClass2 extends SampleClass with SampleTrait {
+
+
+  @Index(3)
+  var sampleClass2Name : String = "sampleclass2"
+
+  @Index(0)
+  def sampleClass2Num : Int = 22
+  def sampleClass2Num_=(v : Int) = {}
+
+  val notProperty : String ="This is not prop.Only getter"
+
+  // wrong property
+  def wrongValue : Int = 53
+  def wrongValue_=(v : String) = {}
+
+}
+
+object NotDefaultCons{
+
+  def apply() : NotDefaultCons2 = {
+    new NotDefaultCons2()
+  }
+}
+@MessagePackMessage
+class NotDefaultCons(var name : String){
+}
+
+class NotDefaultCons2 extends NotDefaultCons("hoge")
+
+@MessagePackMessage
+class BasicalTypes{
+
+  var intVar : Int = 0
+  var longVar : Long = 0
+  var shortVar : Short = 0
+  var byteVar : Byte = 0
+  var boolVar : Boolean = false
+
+  var floatVar : Float = 0
+  var doubleVar : Double = 0
+
+  var strVar : String = ""
+
+  var dateVar : Date = null
+
+
+
+}
\ No newline at end of file
diff --git a/scala/src/test/scala/org/msgpack/ScalaFieldEntryReaderSpec.scala b/scala/src/test/scala/org/msgpack/ScalaFieldEntryReaderSpec.scala
new file mode 100644
index 00000000..3eeffaad
--- /dev/null
+++ b/scala/src/test/scala/org/msgpack/ScalaFieldEntryReaderSpec.scala
@@ -0,0 +1,120 @@
+package org.msgpack
+
+import org.junit.runner.RunWith
+import org.specs._
+import org.specs.matcher._
+import org.specs.runner.{ JUnitSuiteRunner, JUnit }
+//import org.scalacheck.Gen
+
+/**
+ * Sample specification.
+ * 
+ * This specification can be executed with: scala -cp <your classpath=""> ${package}.SpecsTest
+ * Or using maven: mvn test
+ *
+ * For more information on how to write or run specifications, please visit: http://code.google.com/p/specs.
+ *
+ */
+@RunWith(classOf[JUnitSuiteRunner])
+class ScalaFieldEntryReaderSpec extends Specification with JUnit  {
+
+  "ScalaFieldEntryReader" should {
+    "check setter " in {
+      val reader = new ScalaFieldEntryReader()
+
+      val c = classOf[SampleClass]
+
+      reader.setter_?(c.getMethod("name")) must be_==(false)
+      reader.setter_?(c.getMethod("name_$eq",classOf[String])) must be_==(true)
+    }
+    "check getter " in {
+      val reader = new ScalaFieldEntryReader()
+
+      val c = classOf[SampleClass]
+
+      reader.getter_?(c.getMethod("name")) must be_==(true)
+      reader.getter_?(c.getMethod("name_$eq",classOf[String])) must be_==(false)
+    }
+
+    "find props " in {
+      val reader = new ScalaFieldEntryReader()
+
+      {
+        val c = classOf[SampleClass]
+
+        val props = reader.findPropertyMethods(c)
+
+        props.size must be_==(2)
+        props must haveKey("name")
+        props must haveKey("number")
+      }
+
+      {
+        val c = classOf[SampleClass2]
+
+        val props = reader.findPropertyMethods(c)
+
+        println(props.keys)
+
+        props.size must be_==(6)
+        props must haveKey("name")
+        props must haveKey("number")
+        props must haveKey("traitName")
+        props must haveKey("traitNum")
+        props must haveKey("sampleClass2Name")
+        props must haveKey("sampleClass2Num")
+      }
+
+    }
+
+    "indexing " in {
+      val reader = new ScalaFieldEntryReader()
+
+      val c = classOf[SampleClass2]
+
+      def printDecs(c : Class[_]) : Unit = {
+        println(c.getName + "---")
+        val ds = c.getDeclaredMethods
+        ds.foreach(m => {println(m)
+          println(m.getAnnotations.toList)
+        })
+        if(c.getSuperclass != classOf[Object]){
+          printDecs(c.getSuperclass)
+        }
+      }
+      printDecs(c)
+
+
+
+      val props = reader.findPropertyMethods(c)
+
+      val indexed = reader.indexing(props)
+
+      println(indexed.map(_._1).toList)
+
+      indexed.size must be_==(6)
+      indexed(0)._1 must be_==("sampleClass2Num")
+      indexed(3)._1 must be_==("sampleClass2Name")
+      indexed must notContain(null)
+
+    }
+
+    "read entries" in {
+      val reader = new ScalaFieldEntryReader()
+
+      val c = classOf[SampleClass2]
+      import org.msgpack.template.FieldOption
+
+      val e = reader.readFieldEntries(c, FieldOption.NULLABLE)
+
+      e.size must be_==(6)
+
+    }
+
+  }
+
+
+
+
+}
+
diff --git a/scala/src/test/scala/org/msgpack/ScalaMessagePackSpec.scala b/scala/src/test/scala/org/msgpack/ScalaMessagePackSpec.scala
new file mode 100644
index 00000000..8f464000
--- /dev/null
+++ b/scala/src/test/scala/org/msgpack/ScalaMessagePackSpec.scala
@@ -0,0 +1,90 @@
+package org.msgpack
+
+import org.junit.runner.RunWith
+import org.specs._
+import org.specs.matcher._
+import org.specs.runner.{ JUnitSuiteRunner, JUnit }
+import java.util.Date
+
+//import org.scalacheck.Gen
+
+/**
+ * Sample specification.
+ * 
+ * This specification can be executed with: scala -cp <your classpath=""> ${package}.SpecsTest
+ * Or using maven: mvn test
+ *
+ * For more information on how to write or run specifications, please visit: http://code.google.com/p/specs.
+ *
+ */
+@RunWith(classOf[JUnitSuiteRunner])
+class ScalaMessagePackTest extends Specification with JUnit /*with ScalaCheck*/ {
+
+  "ScalaMessagePackTest" should {
+    "pack and unpack" in {
+
+      val sc = new SampleClass()
+      sc.name = "Test object"
+      sc.number = 123456
+
+      println("Sampleclass is inherit ScalaObject " + classOf[ScalaObject].isAssignableFrom(classOf[SampleClass]))
+      new ScalaTemplateBuilderSelector().matchType(classOf[SampleClass]) must be_==(true)
+      val b = ScalaMessagePack.pack(sc)
+
+      val deser = ScalaMessagePack.unpack[SampleClass](b)
+
+      deser.name must be_==(sc.name)
+      deser.number must be_==(sc.number)
+
+      val mso = ScalaMessagePack.unpackD(b)
+      val conv = mso.convert(classOf[SampleClass])
+      conv.name must be_==(sc.name)
+      conv.number must be_==(sc.number)
+    }
+    "check basical types" in {
+      val v = new BasicalTypes
+      v.intVar = 20
+      v.longVar = 11
+      v.shortVar = 7
+      v.byteVar = 1
+      v.floatVar = 1.5f
+      v.doubleVar = 2.5
+      v.strVar = "fugafuga"
+      v.dateVar = new Date(1233333)
+
+      val b = ScalaMessagePack.pack(v)
+      val des : BasicalTypes = ScalaMessagePack.unpack[BasicalTypes](b)
+
+      des.intVar must be_==(v.intVar)
+      des.longVar must be_==(v.longVar)
+      des.shortVar must be_==(v.shortVar)
+      des.byteVar must be_==(v.byteVar)
+      des.floatVar must be_==(v.floatVar)
+      des.doubleVar must be_==(v.doubleVar)
+      des.strVar must be_==(v.strVar)
+      des.dateVar must be_==(v.dateVar)
+
+
+    }
+
+    "pack and unpack none-default constructor class" in {
+
+      val sc = new NotDefaultCons("hehehehe")
+
+      val b = ScalaMessagePack.pack(sc)
+
+      val deser = ScalaMessagePack.unpack[NotDefaultCons](b)
+
+      deser.name must be_==(sc.name)
+
+      val mso = ScalaMessagePack.unpackD(b)
+      val conv = mso.convert(classOf[NotDefaultCons])
+      conv.name must be_==(sc.name)
+    }
+
+
+  }
+
+
+}
+

From 21f0d0bfc47ddc6d9092621705047f3bef385ba5 Mon Sep 17 00:00:00 2001
From: takeshita <takeshita@geishatokyo.com>
Date: Mon, 4 Apr 2011 01:46:29 +0900
Subject: [PATCH 2/2] Delete moved class. (TemplateBuilder.java is moved to
 org.msgpack.template.builder)

---
 .../org/msgpack/template/TemplateBuilder.java | 381 ------------------
 1 file changed, 381 deletions(-)
 delete mode 100644 java/src/main/java/org/msgpack/template/TemplateBuilder.java

diff --git a/java/src/main/java/org/msgpack/template/TemplateBuilder.java b/java/src/main/java/org/msgpack/template/TemplateBuilder.java
deleted file mode 100644
index e3d25b29..00000000
--- a/java/src/main/java/org/msgpack/template/TemplateBuilder.java
+++ /dev/null
@@ -1,381 +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.io.IOException;
-import java.lang.reflect.*;
-import java.lang.annotation.*;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import org.msgpack.*;
-import org.msgpack.annotation.*;
-
-public abstract class TemplateBuilder {
-	public static class FieldEntry {
-		private Field field;
-		private FieldOption option;
-
-		public FieldEntry() {
-			this.field = null;
-			this.option = FieldOption.IGNORE;
-		}
-
-		public FieldEntry(FieldEntry e) {
-			this.field = e.field;
-			this.option = e.option;
-		}
-
-		public FieldEntry(Field field, FieldOption option) {
-			this.field = field;
-			this.option = option;
-		}
-
-		public Field getField() {
-			return field;
-		}
-
-		public String getName() {
-			return field.getName();
-		}
-
-		public Class<?> getType() {
-			return field.getType();
-		}
-
-		public String getJavaTypeName() {
-			Class<?> type = field.getType();
-			if(type.isArray()) {
-				return arrayTypeToString(type);
-			} else {
-				return type.getName();
-			}
-		}
-
-		public Type getGenericType() {
-			return field.getGenericType();
-		}
-
-		public FieldOption getOption() {
-			return option;
-		}
-
-		public boolean isAvailable() {
-			return option != FieldOption.IGNORE;
-		}
-
-		public boolean isRequired() {
-			return option == FieldOption.REQUIRED;
-		}
-
-		public boolean isOptional() {
-			return option == FieldOption.OPTIONAL;
-		}
-
-		public boolean isNullable() {
-			return option == FieldOption.NULLABLE;
-		}
-
-		static String arrayTypeToString(Class<?> type) {
-			int dim = 1;
-			Class<?> baseType = type.getComponentType();
-			while(baseType.isArray()) {
-				baseType = baseType.getComponentType();
-				dim += 1;
-			}
-			StringBuilder sb = new StringBuilder();
-			sb.append(baseType.getName());
-			for (int i = 0; i < dim; ++i) {
-				sb.append("[]");
-			}
-			return sb.toString();
-		}
-	}
-
-	// Override this method
-	public abstract Template buildTemplate(Class<?> targetClass, FieldEntry[] entries);
-
-	// 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);
-		return buildTemplate(targetClass, convertFieldEntries(targetClass, flist));
-	}
-
-	public Template buildTemplate(Class<?> targetClass, FieldOption implicitOption) {
-		checkValidation(targetClass);
-		return buildTemplate(targetClass, readFieldEntries(targetClass, implicitOption));
-	}
-
-	public Template buildTemplate(Class<?> targetClass) {
-		FieldOption implicitOption = readImplicitFieldOption(targetClass);
-		return buildTemplate(targetClass, implicitOption);
-	}
-
-	public Template buildOrdinalEnumTemplate(Class<?> targetClass) {
-		checkOrdinalEnumValidation(targetClass);
-		Enum<?>[] entries = (Enum<?>[])targetClass.getEnumConstants();
-		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;
-			}
-			if(baseType instanceof ParameterizedType) {
-				baseClass = (Class<?>)((ParameterizedType)baseType).getRawType();
-			} else {
-				baseClass = (Class<?>)baseType;
-			}
-		} 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 {
-		instance = selectDefaultTemplateBuilder();
-	}
-
-	private static TemplateBuilder selectDefaultTemplateBuilder() {
-		try {
-			// FIXME JavassistTemplateBuilder doesn't work on DalvikVM
-			if(System.getProperty("java.vm.name").equals("Dalvik")) {
-				return ReflectionTemplateBuilder.getInstance();
-			}
-		} catch (Exception e) {
-		}
-		return JavassistTemplateBuilder.getInstance();
-	}
-
-	synchronized static void setInstance(TemplateBuilder builder) {
-		instance = builder;
-	}
-
-	public static Template build(Class<?> targetClass) {
-		return instance.buildTemplate(targetClass);
-	}
-
-	public static Template build(Class<?> targetClass, FieldOption implicitOption) {
-		return instance.buildTemplate(targetClass, implicitOption);
-	}
-
-	public static Template build(Class<?> targetClass, FieldList flist) throws NoSuchFieldException {
-		return instance.buildTemplate(targetClass, flist);
-	}
-
-	public static Template buildOrdinalEnum(Class<?> targetClass) {
-		return instance.buildOrdinalEnumTemplate(targetClass);
-	}
-
-	public static Template buildArray(Type arrayType) {
-		return instance.buildArrayTemplate(arrayType);
-	}
-
-
-	private static void checkValidation(Class<?> targetClass) {
-		if(targetClass.isInterface()) {
-			throw new TemplateBuildException("cannot build template of interface");
-		}
-		if(targetClass.isArray()) {
-			throw new TemplateBuildException("cannot build template of array class");
-		}
-		if(targetClass.isPrimitive()) {
-			throw new TemplateBuildException("cannot build template of primitive type");
-		}
-	}
-
-	private static void checkOrdinalEnumValidation(Class<?> targetClass) {
-		if(!targetClass.isEnum()) {
-			throw new TemplateBuildException("tried to build ordinal enum template of non-enum class");
-		}
-	}
-
-
-	static FieldEntry[] convertFieldEntries(Class<?> targetClass, FieldList flist) throws NoSuchFieldException {
-		List<FieldList.Entry> src = flist.getList();
-		FieldEntry[] result = new FieldEntry[src.size()];
-		for(int i=0; i < src.size(); i++) {
-			FieldList.Entry s = src.get(i);
-			if(s.isAvailable()) {
-				result[i] = new FieldEntry(targetClass.getDeclaredField(s.getName()), s.getOption());
-			} else {
-				result[i] = new FieldEntry();
-			}
-		}
-		return result;
-	}
-
-	static FieldEntry[] readFieldEntries(Class<?> targetClass, FieldOption implicitOption) {
-		Field[] allFields = readAllFields(targetClass);
-
-		/* index:
-		 *   @Index(0) int field_a;   // 0
-		 *             int field_b;   // 1
-		 *   @Index(3) int field_c;   // 3
-		 *             int field_d;   // 4
-		 *   @Index(2) int field_e;   // 2
-		 *             int field_f;   // 5
-		 */
-		List<FieldEntry> indexed = new ArrayList<FieldEntry>();
-		int maxIndex = -1;
-		for(Field f : allFields) {
-			FieldOption opt = readFieldOption(f, implicitOption);
-			if(opt == FieldOption.IGNORE) {
-				// skip
-				continue;
-			}
-
-			int index = readFieldIndex(f, maxIndex);
-
-			if(indexed.size() > index && indexed.get(index) != null) {
-				throw new TemplateBuildException("duplicated index: "+index);
-			}
-			if(index < 0) {
-				throw new TemplateBuildException("invalid index: "+index);
-			}
-
-			while(indexed.size() <= index) {
-				indexed.add(null);
-			}
-			indexed.set(index, new FieldEntry(f, opt));
-
-			if(maxIndex < index) {
-				maxIndex = index;
-			}
-		}
-
-		FieldEntry[] result = new FieldEntry[maxIndex+1];
-		for(int i=0; i < indexed.size(); i++) {
-			FieldEntry e = indexed.get(i);
-			if(e == null) {
-				result[i] = new FieldEntry();
-			} else {
-				result[i] = e;
-			}
-		}
-
-		return result;
-	}
-
-	private static Field[] readAllFields(Class<?> targetClass) {
-		// order: [fields of super class, ..., fields of this class]
-		List<Field[]> succ = new ArrayList<Field[]>();
-		int total = 0;
-		for(Class<?> c = targetClass; c != Object.class; c = c.getSuperclass()) {
-			Field[] fields = c.getDeclaredFields();
-			total += fields.length;
-			succ.add(fields);
-		}
-		Field[] result = new Field[total];
-		int off = 0;
-		for(int i=succ.size()-1; i >= 0; i--) {
-			Field[] fields = succ.get(i);
-			System.arraycopy(fields, 0, result, off, fields.length);
-			off += fields.length;
-		}
-		return result;
-	}
-
-	private static FieldOption readImplicitFieldOption(Class<?> targetClass) {
-		MessagePackMessage a = targetClass.getAnnotation(MessagePackMessage.class);
-		if(a == null) {
-			return FieldOption.DEFAULT;
-		}
-		return a.value();
-	}
-
-	private static FieldOption readFieldOption(Field field, FieldOption implicitOption) {
-		int mod = field.getModifiers();
-		if(Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
-			return FieldOption.IGNORE;
-		}
-
-		if(isAnnotated(field, Ignore.class)) {
-			return FieldOption.IGNORE;
-		} else if(isAnnotated(field, Required.class)) {
-			return FieldOption.REQUIRED;
-		} else if(isAnnotated(field, Optional.class)) {
-			return FieldOption.OPTIONAL;
-		} else if(isAnnotated(field, Nullable.class)) {
-			if(field.getDeclaringClass().isPrimitive()) {
-				return FieldOption.REQUIRED;
-			} else {
-				return FieldOption.NULLABLE;
-			}
-		}
-
-		if(implicitOption != FieldOption.DEFAULT) {
-			return implicitOption;
-		}
-
-		// default mode:
-		//   transient : Ignore
-		//   public    : Required
-		//   others    : Ignore
-		if(Modifier.isTransient(mod)) {
-			return FieldOption.IGNORE;
-		} else if(Modifier.isPublic(mod)) {
-			return FieldOption.REQUIRED;
-		} else {
-			return FieldOption.IGNORE;
-		}
-	}
-
-	private static int readFieldIndex(Field field, int maxIndex) {
-		Index a = field.getAnnotation(Index.class);
-		if(a == null) {
-			return maxIndex + 1;
-		} else {
-			return a.value();
-		}
-	}
-
-	private static boolean isAnnotated(AccessibleObject ao, Class<? extends Annotation> with) {
-		return ao.getAnnotation(with) != null;
-	}
-}
-