summaryrefslogtreecommitdiff
path: root/java-plan2/src
diff options
context:
space:
mode:
authorfrsyuki <frsyuki@users.sourceforge.jp>2009-11-12 01:10:36 +0900
committerfrsyuki <frsyuki@users.sourceforge.jp>2009-11-12 01:10:36 +0900
commite39e1d4f602b0202b830f8e672e2116bdb8b9f34 (patch)
tree30ff5419760ae2c2550ff759ae8e345b204cb435 /java-plan2/src
parent93a95725fc45d7d0047578ecdc5b2ae4e900970f (diff)
downloadmsgpack-python-e39e1d4f602b0202b830f8e672e2116bdb8b9f34.tar.gz
import MessagePack for Java implementation plan 2
Diffstat (limited to 'java-plan2/src')
-rw-r--r--java-plan2/src/org/msgpack/GenericArray.java25
-rw-r--r--java-plan2/src/org/msgpack/GenericBoolean.java17
-rw-r--r--java-plan2/src/org/msgpack/GenericLong.java17
-rw-r--r--java-plan2/src/org/msgpack/GenericMap.java25
-rw-r--r--java-plan2/src/org/msgpack/GenericObject.java52
-rw-r--r--java-plan2/src/org/msgpack/GenericRaw.java59
-rw-r--r--java-plan2/src/org/msgpack/GenericRawRef.java55
-rw-r--r--java-plan2/src/org/msgpack/GenericShort.java29
-rw-r--r--java-plan2/src/org/msgpack/MessageConvertable.java7
-rw-r--r--java-plan2/src/org/msgpack/MessageMergeable.java7
-rw-r--r--java-plan2/src/org/msgpack/MessagePackException.java10
-rw-r--r--java-plan2/src/org/msgpack/MessagePackable.java9
-rw-r--r--java-plan2/src/org/msgpack/Packer.java332
-rw-r--r--java-plan2/src/org/msgpack/UnbufferedUnpacker.java74
-rw-r--r--java-plan2/src/org/msgpack/UnpackException.java35
-rw-r--r--java-plan2/src/org/msgpack/UnpackIterator.java53
-rw-r--r--java-plan2/src/org/msgpack/Unpacker.java269
-rw-r--r--java-plan2/src/org/msgpack/impl/ArrayBuilder.java7
-rw-r--r--java-plan2/src/org/msgpack/impl/GenericObjectBuilder.java127
-rw-r--r--java-plan2/src/org/msgpack/impl/GenericZeroCopyObjectBuilder.java12
-rw-r--r--java-plan2/src/org/msgpack/impl/MapBuilder.java8
-rw-r--r--java-plan2/src/org/msgpack/impl/ObjectBuilder.java16
-rw-r--r--java-plan2/src/org/msgpack/impl/SpecificObjectBuilder.java203
-rw-r--r--java-plan2/src/org/msgpack/impl/UnpackerImpl.java395
-rw-r--r--java-plan2/src/org/msgpack/schema/ArraySchema.java61
-rw-r--r--java-plan2/src/org/msgpack/schema/ClassGenerator.java230
-rw-r--r--java-plan2/src/org/msgpack/schema/ClassSchema.java39
-rw-r--r--java-plan2/src/org/msgpack/schema/FieldSchema.java31
-rw-r--r--java-plan2/src/org/msgpack/schema/GenericClassSchema.java89
-rw-r--r--java-plan2/src/org/msgpack/schema/GenericFieldSchema.java25
-rw-r--r--java-plan2/src/org/msgpack/schema/IntSchema.java58
-rw-r--r--java-plan2/src/org/msgpack/schema/LongSchema.java58
-rw-r--r--java-plan2/src/org/msgpack/schema/MapSchema.java70
-rw-r--r--java-plan2/src/org/msgpack/schema/ObjectSchema.java33
-rw-r--r--java-plan2/src/org/msgpack/schema/PrimitiveSchema.java21
-rw-r--r--java-plan2/src/org/msgpack/schema/RawSchema.java44
-rw-r--r--java-plan2/src/org/msgpack/schema/SSchemaParser.java246
-rw-r--r--java-plan2/src/org/msgpack/schema/Schema.java93
-rw-r--r--java-plan2/src/org/msgpack/schema/ShortSchema.java46
-rw-r--r--java-plan2/src/org/msgpack/schema/SpecificClassSchema.java156
-rw-r--r--java-plan2/src/org/msgpack/schema/SpecificFieldSchema.java78
-rw-r--r--java-plan2/src/org/msgpack/schema/StringSchema.java48
42 files changed, 3269 insertions, 0 deletions
diff --git a/java-plan2/src/org/msgpack/GenericArray.java b/java-plan2/src/org/msgpack/GenericArray.java
new file mode 100644
index 0000000..53799ca
--- /dev/null
+++ b/java-plan2/src/org/msgpack/GenericArray.java
@@ -0,0 +1,25 @@
+package org.msgpack;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class GenericArray extends GenericObject {
+ private ArrayList<GenericObject> array;
+
+ public GenericArray(int length)
+ {
+ this.array = new ArrayList<GenericObject>(length);
+ }
+
+ public void add(GenericObject element)
+ {
+ array.add(element);
+ }
+
+ @Override
+ public List<GenericObject> asArray()
+ {
+ return array;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/GenericBoolean.java b/java-plan2/src/org/msgpack/GenericBoolean.java
new file mode 100644
index 0000000..908128f
--- /dev/null
+++ b/java-plan2/src/org/msgpack/GenericBoolean.java
@@ -0,0 +1,17 @@
+package org.msgpack;
+
+public class GenericBoolean extends GenericObject {
+ boolean value;
+
+ public GenericBoolean(boolean value)
+ {
+ this.value = value;
+ }
+
+ @Override
+ public boolean asBoolean()
+ {
+ return value;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/GenericLong.java b/java-plan2/src/org/msgpack/GenericLong.java
new file mode 100644
index 0000000..7cc9110
--- /dev/null
+++ b/java-plan2/src/org/msgpack/GenericLong.java
@@ -0,0 +1,17 @@
+package org.msgpack;
+
+public class GenericLong extends GenericObject {
+ long value;
+
+ public GenericLong(long value)
+ {
+ this.value = value;
+ }
+
+ @Override
+ public long asLong()
+ {
+ return value;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/GenericMap.java b/java-plan2/src/org/msgpack/GenericMap.java
new file mode 100644
index 0000000..0a5512a
--- /dev/null
+++ b/java-plan2/src/org/msgpack/GenericMap.java
@@ -0,0 +1,25 @@
+package org.msgpack;
+
+import java.util.Map;
+import java.util.HashMap;
+
+public class GenericMap extends GenericObject {
+ private HashMap<GenericObject,GenericObject> map;
+
+ public GenericMap(int length)
+ {
+ this.map = new HashMap<GenericObject,GenericObject>(length);
+ }
+
+ public void put(GenericObject key, GenericObject value)
+ {
+ map.put(key, value);
+ }
+
+ @Override
+ public Map<GenericObject,GenericObject> asMap()
+ {
+ return map;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/GenericObject.java b/java-plan2/src/org/msgpack/GenericObject.java
new file mode 100644
index 0000000..32eacd3
--- /dev/null
+++ b/java-plan2/src/org/msgpack/GenericObject.java
@@ -0,0 +1,52 @@
+package org.msgpack;
+
+import java.util.List;
+import java.util.Map;
+
+public abstract class GenericObject {
+ public boolean asBoolean()
+ {
+ throw new RuntimeException("type error");
+ }
+
+ public byte asByte()
+ {
+ throw new RuntimeException("type error");
+ }
+
+ public short asShort()
+ {
+ throw new RuntimeException("type error");
+ }
+
+ public int asInt()
+ {
+ throw new RuntimeException("type error");
+ }
+
+ public long asLong()
+ {
+ throw new RuntimeException("type error");
+ }
+
+ public String asString()
+ {
+ throw new RuntimeException("type error");
+ }
+
+ public byte[] asBytes()
+ {
+ throw new RuntimeException("type error");
+ }
+
+ public List<GenericObject> asArray()
+ {
+ throw new RuntimeException("type error");
+ }
+
+ public Map<GenericObject,GenericObject> asMap()
+ {
+ throw new RuntimeException("type error");
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/GenericRaw.java b/java-plan2/src/org/msgpack/GenericRaw.java
new file mode 100644
index 0000000..6de03b2
--- /dev/null
+++ b/java-plan2/src/org/msgpack/GenericRaw.java
@@ -0,0 +1,59 @@
+package org.msgpack;
+
+import java.nio.charset.Charset;
+
+public class GenericRaw extends GenericObject {
+ byte[] bytes;
+ String string;
+
+ public GenericRaw()
+ {
+ this.bytes = new byte[0];
+ this.string = null;
+ }
+
+ public GenericRaw(byte[] bytes)
+ {
+ this.bytes = bytes;
+ this.string = null;
+ }
+
+ public GenericRaw(String string)
+ {
+ this.bytes = null;
+ this.string = string;
+ }
+
+ public synchronized void setString(String string)
+ {
+ this.string = string;
+ this.bytes = null;
+ }
+
+ public synchronized void setBytes(byte[] bytes)
+ {
+ this.bytes = bytes;
+ this.string = null;
+ }
+
+ private static Charset UTF8_CHARSET = Charset.forName("UTF-8");
+
+ @Override
+ public synchronized String asString()
+ {
+ if(string == null) {
+ return string = new String(bytes, UTF8_CHARSET);
+ }
+ return string;
+ }
+
+ @Override
+ public synchronized byte[] asBytes()
+ {
+ if(bytes == null) {
+ return bytes = string.getBytes(UTF8_CHARSET);
+ }
+ return bytes;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/GenericRawRef.java b/java-plan2/src/org/msgpack/GenericRawRef.java
new file mode 100644
index 0000000..7007810
--- /dev/null
+++ b/java-plan2/src/org/msgpack/GenericRawRef.java
@@ -0,0 +1,55 @@
+package org.msgpack;
+
+import java.nio.charset.Charset;
+
+public class GenericRawRef extends GenericRaw {
+ int offset;
+ int length;
+
+ public GenericRawRef(byte[] bytes, int offset, int length)
+ {
+ this.bytes = bytes;
+ this.offset = offset;
+ this.length = length;
+ this.string = null;
+ }
+
+ public GenericRawRef(String string)
+ {
+ this.bytes = null;
+ this.string = string;
+ }
+
+ public synchronized void setString(String string)
+ {
+ this.string = string;
+ this.bytes = null;
+ }
+
+ public synchronized void setBytes(byte[] bytes)
+ {
+ this.bytes = bytes;
+ this.string = null;
+ }
+
+ private static Charset UTF8_CHARSET = Charset.forName("UTF-8");
+
+ @Override
+ public synchronized String asString()
+ {
+ if(string == null) {
+ return string = new String(bytes, UTF8_CHARSET);
+ }
+ return string;
+ }
+
+ @Override
+ public synchronized byte[] asBytes()
+ {
+ if(bytes == null) {
+ return bytes = string.getBytes(UTF8_CHARSET);
+ }
+ return bytes;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/GenericShort.java b/java-plan2/src/org/msgpack/GenericShort.java
new file mode 100644
index 0000000..eb2463e
--- /dev/null
+++ b/java-plan2/src/org/msgpack/GenericShort.java
@@ -0,0 +1,29 @@
+package org.msgpack;
+
+public class GenericShort extends GenericObject {
+ short value;
+
+ public GenericShort(short value)
+ {
+ this.value = value;
+ }
+
+ @Override
+ public short asShort()
+ {
+ return value;
+ }
+
+ @Override
+ public int asInt()
+ {
+ return value;
+ }
+
+ @Override
+ public long asLong()
+ {
+ return value;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/MessageConvertable.java b/java-plan2/src/org/msgpack/MessageConvertable.java
new file mode 100644
index 0000000..68c123e
--- /dev/null
+++ b/java-plan2/src/org/msgpack/MessageConvertable.java
@@ -0,0 +1,7 @@
+package org.msgpack;
+
+public interface MessageConvertable
+{
+ public void messageConvert(GenericObject obj);
+}
+
diff --git a/java-plan2/src/org/msgpack/MessageMergeable.java b/java-plan2/src/org/msgpack/MessageMergeable.java
new file mode 100644
index 0000000..dc50749
--- /dev/null
+++ b/java-plan2/src/org/msgpack/MessageMergeable.java
@@ -0,0 +1,7 @@
+package org.msgpack;
+
+public interface MessageMergeable {
+ public void setField(int index, Object value);
+ public Object getField(int index);
+}
+
diff --git a/java-plan2/src/org/msgpack/MessagePackException.java b/java-plan2/src/org/msgpack/MessagePackException.java
new file mode 100644
index 0000000..dc37989
--- /dev/null
+++ b/java-plan2/src/org/msgpack/MessagePackException.java
@@ -0,0 +1,10 @@
+package org.msgpack;
+
+public class MessagePackException extends RuntimeException
+{
+ public MessagePackException(String message)
+ {
+ super(message);
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/MessagePackable.java b/java-plan2/src/org/msgpack/MessagePackable.java
new file mode 100644
index 0000000..1efff3f
--- /dev/null
+++ b/java-plan2/src/org/msgpack/MessagePackable.java
@@ -0,0 +1,9 @@
+package org.msgpack;
+
+import java.io.IOException;
+
+public interface MessagePackable
+{
+ public void messagePack(Packer pk) throws IOException;
+}
+
diff --git a/java-plan2/src/org/msgpack/Packer.java b/java-plan2/src/org/msgpack/Packer.java
new file mode 100644
index 0000000..4545849
--- /dev/null
+++ b/java-plan2/src/org/msgpack/Packer.java
@@ -0,0 +1,332 @@
+package org.msgpack;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Map;
+
+public class Packer {
+ protected byte[] castBytes = new byte[9];
+ protected ByteBuffer castBuffer = ByteBuffer.wrap(castBytes);
+ protected OutputStream out;
+
+ public Packer(OutputStream out)
+ {
+ this.out = out;
+ }
+
+ public Packer packByte(byte d) throws IOException
+ {
+ if(d < -(1<<5)) {
+ castBytes[0] = (byte)0xd1;
+ castBytes[1] = d;
+ out.write(castBytes, 0, 2);
+ } else {
+ out.write(d);
+ }
+ return this;
+ }
+
+ public Packer packShort(short d) throws IOException
+ {
+ if(d < -(1<<5)) {
+ if(d < -(1<<7)) {
+ // signed 16
+ castBytes[0] = (byte)0xd1;
+ castBuffer.putShort(1, d);
+ out.write(castBytes, 0, 3);
+ } else {
+ // signed 8
+ castBytes[0] = (byte)0xd0;
+ castBytes[1] = (byte)d;
+ out.write(castBytes, 0, 2);
+ }
+ } else if(d < (1<<7)) {
+ // fixnum
+ out.write((byte)d);
+ } else {
+ if(d < (1<<8)) {
+ // unsigned 8
+ castBytes[0] = (byte)0xcc;
+ castBytes[1] = (byte)d;
+ out.write(castBytes, 0, 2);
+ } else {
+ // unsigned 16
+ castBytes[0] = (byte)0xcd;
+ castBuffer.putShort(1, d);
+ out.write(castBytes, 0, 3);
+ }
+ }
+ return this;
+ }
+
+ public Packer packInt(int d) throws IOException
+ {
+ if(d < -(1<<5)) {
+ if(d < -(1<<15)) {
+ // signed 32
+ castBytes[0] = (byte)0xd2;
+ castBuffer.putInt(1, d);
+ out.write(castBytes, 0, 5);
+ } else if(d < -(1<<7)) {
+ // signed 16
+ castBytes[0] = (byte)0xd1;
+ castBuffer.putShort(1, (short)d);
+ out.write(castBytes, 0, 3);
+ } else {
+ // signed 8
+ castBytes[0] = (byte)0xd0;
+ castBytes[1] = (byte)d;
+ out.write(castBytes, 0, 2);
+ }
+ } else if(d < (1<<7)) {
+ // fixnum
+ out.write((byte)d);
+ } else {
+ if(d < (1<<8)) {
+ // unsigned 8
+ castBytes[0] = (byte)0xcc;
+ castBytes[1] = (byte)d;
+ out.write(castBytes, 0, 2);
+ } else if(d < (1<<16)) {
+ // unsigned 16
+ castBytes[0] = (byte)0xcd;
+ castBuffer.putShort(1, (short)d);
+ out.write(castBytes, 0, 3);
+ } else {
+ // unsigned 32
+ castBytes[0] = (byte)0xce;
+ castBuffer.putInt(1, d);
+ out.write(castBytes, 0, 5);
+ }
+ }
+ return this;
+ }
+
+ public Packer packLong(long d) throws IOException
+ {
+ if(d < -(1L<<5)) {
+ if(d < -(1L<<15)) {
+ if(d < -(1L<<31)) {
+ // signed 64
+ castBytes[0] = (byte)0xd3;
+ castBuffer.putLong(1, d);
+ out.write(castBytes, 0, 9);
+ } else {
+ // signed 32
+ castBytes[0] = (byte)0xd2;
+ castBuffer.putInt(1, (int)d);
+ out.write(castBytes, 0, 5);
+ }
+ } else {
+ if(d < -(1<<7)) {
+ // signed 16
+ castBytes[0] = (byte)0xd1;
+ castBuffer.putShort(1, (short)d);
+ out.write(castBytes, 0, 3);
+ } else {
+ // signed 8
+ castBytes[0] = (byte)0xd0;
+ castBytes[1] = (byte)d;
+ out.write(castBytes, 0, 2);
+ }
+ }
+ } else if(d < (1<<7)) {
+ // fixnum
+ out.write((byte)d);
+ } else {
+ if(d < (1L<<16)) {
+ if(d < (1<<8)) {
+ // unsigned 8
+ castBytes[0] = (byte)0xcc;
+ castBytes[1] = (byte)d;
+ out.write(castBytes, 0, 2);
+ } else {
+ // unsigned 16
+ castBytes[0] = (byte)0xcd;
+ castBuffer.putShort(1, (short)d);
+ out.write(castBytes, 0, 3);
+ //System.out.println("pack uint 16 "+(short)d);
+ }
+ } else {
+ if(d < (1L<<32)) {
+ // unsigned 32
+ castBytes[0] = (byte)0xce;
+ castBuffer.putInt(1, (int)d);
+ out.write(castBytes, 0, 5);
+ } else {
+ // unsigned 64
+ castBytes[0] = (byte)0xcf;
+ castBuffer.putLong(1, d);
+ out.write(castBytes, 0, 9);
+ }
+ }
+ }
+ return this;
+ }
+
+ public Packer packFloat(float d) throws IOException
+ {
+ castBytes[0] = (byte)0xca;
+ castBuffer.putFloat(1, d);
+ out.write(castBytes, 0, 5);
+ return this;
+ }
+
+ public Packer packDouble(double d) throws IOException
+ {
+ castBytes[0] = (byte)0xcb;
+ castBuffer.putDouble(1, d);
+ out.write(castBytes, 0, 9);
+ return this;
+ }
+
+ public Packer packNil() throws IOException
+ {
+ out.write((byte)0xc0);
+ return this;
+ }
+
+ public Packer packTrue() throws IOException
+ {
+ out.write((byte)0xc3);
+ return this;
+ }
+
+ public Packer packFalse() throws IOException
+ {
+ out.write((byte)0xc2);
+ return this;
+ }
+
+ public Packer packArray(int n) throws IOException
+ {
+ if(n < 16) {
+ final int d = 0x90 | n;
+ out.write((byte)d);
+ } else if(n < 65536) {
+ castBytes[0] = (byte)0xdc;
+ castBuffer.putShort(1, (short)n);
+ out.write(castBytes, 0, 3);
+ } else {
+ castBytes[0] = (byte)0xdd;
+ castBuffer.putInt(1, n);
+ out.write(castBytes, 0, 5);
+ }
+ return this;
+ }
+
+ public Packer packMap(int n) throws IOException
+ {
+ if(n < 16) {
+ final int d = 0x80 | n;
+ out.write((byte)d);
+ } else if(n < 65536) {
+ castBytes[0] = (byte)0xde;
+ castBuffer.putShort(1, (short)n);
+ out.write(castBytes, 0, 3);
+ } else {
+ castBytes[0] = (byte)0xdf;
+ castBuffer.putInt(1, n);
+ out.write(castBytes, 0, 5);
+ }
+ return this;
+ }
+
+ public Packer packRaw(int n) throws IOException
+ {
+ if(n < 32) {
+ final int d = 0xa0 | n;
+ out.write((byte)d);
+ } else if(n < 65536) {
+ castBytes[0] = (byte)0xda;
+ castBuffer.putShort(1, (short)n);
+ out.write(castBytes, 0, 3);
+ } else {
+ castBytes[0] = (byte)0xdb;
+ castBuffer.putInt(1, n);
+ out.write(castBytes, 0, 5);
+ }
+ return this;
+ }
+
+ public Packer packRawBody(byte[] b) throws IOException
+ {
+ out.write(b);
+ return this;
+ }
+
+ public Packer packRawBody(byte[] b, int off, int length) throws IOException
+ {
+ out.write(b, off, length);
+ return this;
+ }
+
+ private static Charset UTF8_CHARSET = Charset.forName("UTF-8");
+
+ public Packer packString(String s) throws IOException
+ {
+ byte[] b = ((String)s).getBytes(UTF8_CHARSET);
+ packRaw(b.length);
+ packRawBody(b);
+ return this;
+ }
+
+ public Packer pack(MessagePackable o) throws IOException
+ {
+ o.messagePack(this);
+ return this;
+ }
+
+ public Packer pack(Object o) throws IOException
+ {
+ if(o == null) {
+ packNil();
+ } else if(o instanceof String) {
+ byte[] b = ((String)o).getBytes(UTF8_CHARSET);
+ packRaw(b.length);
+ packRawBody(b);
+ } else if(o instanceof byte[]) {
+ byte[] b = (byte[])o;
+ packRaw(b.length);
+ packRawBody(b);
+ } else if(o instanceof List) {
+ List<Object> l = (List<Object>)o;
+ packArray(l.size());
+ for(Object i : l) { pack(i); }
+ } else if(o instanceof Map) {
+ Map<Object,Object> m = (Map<Object,Object>)o;
+ packMap(m.size());
+ for(Map.Entry e : m.entrySet()) {
+ pack(e.getKey());
+ pack(e.getValue());
+ }
+ } else if(o instanceof Boolean) {
+ if((Boolean)o) {
+ packTrue();
+ } else {
+ packFalse();
+ }
+ } else if(o instanceof Integer) {
+ packInt((Integer)o);
+ } else if(o instanceof Long) {
+ packLong((Long)o);
+ } else if(o instanceof Short) {
+ packShort((Short)o);
+ } else if(o instanceof Byte) {
+ packByte((Byte)o);
+ } else if(o instanceof Float) {
+ packFloat((Float)o);
+ } else if(o instanceof Double) {
+ packDouble((Double)o);
+ } else if(o instanceof MessagePackable) {
+ ((MessagePackable)o).messagePack(this);
+ } else {
+ throw new IOException("unknown object "+o+" ("+o.getClass()+")");
+ }
+ return this;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/UnbufferedUnpacker.java b/java-plan2/src/org/msgpack/UnbufferedUnpacker.java
new file mode 100644
index 0000000..5b5eb97
--- /dev/null
+++ b/java-plan2/src/org/msgpack/UnbufferedUnpacker.java
@@ -0,0 +1,74 @@
+package org.msgpack;
+
+import java.lang.Iterable;
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import org.msgpack.impl.*;
+import org.msgpack.schema.Schema;
+
+public class UnbufferedUnpacker extends UnpackerImpl {
+ private int offset;
+ private boolean finished;
+ private Object data;
+
+ public UnbufferedUnpacker()
+ {
+ super(new GenericObjectBuilder());
+ this.offset = 0;
+ this.finished = false;
+ }
+
+ public UnbufferedUnpacker useSchema(Schema s)
+ {
+ super.setBuilder(new SpecificObjectBuilder(s));
+ return this;
+ }
+
+ public Object getData()
+ {
+ return data;
+ }
+
+ public boolean isFinished()
+ {
+ return finished;
+ }
+
+ public void reset()
+ {
+ super.reset();
+ this.offset = 0;
+ }
+
+ int getOffset()
+ {
+ return offset;
+ }
+
+ void setOffset(int offset)
+ {
+ this.offset = offset;
+ }
+
+ public int execute(byte[] buffer) throws UnpackException
+ {
+ return execute(buffer, 0, buffer.length);
+ }
+
+ // FIXME
+ public int execute(byte[] buffer, int offset, int length) throws UnpackException
+ {
+ int noffset = super.execute(buffer, offset + this.offset, length);
+ this.offset = noffset - offset;
+ if(super.isFinished()) {
+ this.data = super.getData();
+ this.finished = true;
+ super.reset();
+ } else {
+ this.finished = false;
+ }
+ return noffset;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/UnpackException.java b/java-plan2/src/org/msgpack/UnpackException.java
new file mode 100644
index 0000000..2081faf
--- /dev/null
+++ b/java-plan2/src/org/msgpack/UnpackException.java
@@ -0,0 +1,35 @@
+package org.msgpack;
+
+public class UnpackException extends MessagePackException
+{
+ public static final int EXTRA_BYTES = 1;
+ public static final int INSUFFICIENT_BYTES = 0;
+ public static final int PARSE_ERROR = -1;
+
+ private int errorCode;
+ private Object data;
+
+ public UnpackException(String message, int errorCode)
+ {
+ super(message);
+ this.errorCode = errorCode;
+ }
+
+ public UnpackException(String message, int errorCode, Object data)
+ {
+ super(message);
+ this.errorCode = errorCode;
+ this.data = data;
+ }
+
+ public int getErrorCode()
+ {
+ return errorCode;
+ }
+
+ public Object getData()
+ {
+ return data;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/UnpackIterator.java b/java-plan2/src/org/msgpack/UnpackIterator.java
new file mode 100644
index 0000000..9010101
--- /dev/null
+++ b/java-plan2/src/org/msgpack/UnpackIterator.java
@@ -0,0 +1,53 @@
+package org.msgpack;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class UnpackIterator implements Iterator<Object> {
+ private Unpacker pac;
+ private boolean have;
+ private Object data;
+
+ UnpackIterator(Unpacker pac)
+ {
+ this.pac = pac;
+ this.have = false;
+ }
+
+ public boolean hasNext()
+ {
+ if(have) { return true; }
+ try {
+ while(true) {
+ if(pac.execute()) {
+ data = pac.getData();
+ pac.reset();
+ have = true;
+ return true;
+ }
+
+ if(!pac.fill()) {
+ return false;
+ }
+ }
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ public Object next()
+ {
+ if(!have) {
+ throw new NoSuchElementException();
+ }
+ have = false;
+ return data;
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/Unpacker.java b/java-plan2/src/org/msgpack/Unpacker.java
new file mode 100644
index 0000000..5a58ce8
--- /dev/null
+++ b/java-plan2/src/org/msgpack/Unpacker.java
@@ -0,0 +1,269 @@
+package org.msgpack;
+
+import java.lang.Iterable;
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import org.msgpack.impl.*;
+import org.msgpack.schema.Schema;
+
+public class Unpacker extends UnpackerImpl implements Iterable<Object> {
+
+ public static final int DEFAULT_BUFFER_SIZE = 32*1024;
+
+ private int used;
+ private int offset;
+ private int parsed;
+ private byte[] buffer;
+ private int bufferReserveSize;
+ private InputStream stream;
+
+ public Unpacker()
+ {
+ super(new GenericZeroCopyObjectBuilder());
+ this.used = 0;
+ this.offset = 0;
+ this.parsed = 0;
+ this.buffer = new byte[DEFAULT_BUFFER_SIZE];
+ this.bufferReserveSize = DEFAULT_BUFFER_SIZE/2;
+ this.stream = null;
+ }
+
+ public Unpacker(int bufferReserveSize)
+ {
+ super(new GenericZeroCopyObjectBuilder());
+ this.used = 0;
+ this.offset = 0;
+ this.parsed = 0;
+ this.buffer = new byte[bufferReserveSize];
+ this.bufferReserveSize = bufferReserveSize/2;
+ this.stream = null;
+ }
+
+ public Unpacker(InputStream stream)
+ {
+ super(new GenericZeroCopyObjectBuilder());
+ this.used = 0;
+ this.offset = 0;
+ this.parsed = 0;
+ this.buffer = new byte[DEFAULT_BUFFER_SIZE];
+ this.bufferReserveSize = DEFAULT_BUFFER_SIZE/2;
+ this.stream = stream;
+ }
+
+ public Unpacker(InputStream stream, int bufferReserveSize)
+ {
+ super(new GenericZeroCopyObjectBuilder());
+ this.used = 0;
+ this.offset = 0;
+ this.parsed = 0;
+ this.buffer = new byte[bufferReserveSize];
+ this.bufferReserveSize = bufferReserveSize/2;
+ this.stream = stream;
+ }
+
+ public Unpacker useSchema(Schema s)
+ {
+ super.setBuilder(new SpecificObjectBuilder(s));
+ return this;
+ }
+
+ public void reserveBuffer(int size)
+ {
+ if(buffer.length - used >= size) {
+ return;
+ }
+ /*
+ if(used == parsed && buffer.length >= size) {
+ // rewind buffer
+ used = 0;
+ offset = 0;
+ return;
+ }
+ */
+
+ int nextSize = buffer.length * 2;
+ while(nextSize < size + used) {
+ nextSize *= 2;
+ }
+
+ byte[] tmp = new byte[nextSize];
+ System.arraycopy(buffer, offset, tmp, 0, used - offset);
+
+ buffer = tmp;
+ used -= offset;
+ offset = 0;
+ }
+
+ public byte[] getBuffer()
+ {
+ return buffer;
+ }
+
+ public int getBufferOffset()
+ {
+ return used;
+ }
+
+ public int getBufferCapacity()
+ {
+ return buffer.length - used;
+ }
+
+ public void bufferConsumed(int size)
+ {
+ used += size;
+ }
+
+ public void feed(byte[] buffer)
+ {
+ feed(buffer, 0, buffer.length);
+ }
+
+ public void feed(byte[] buffer, int offset, int length)
+ {
+ reserveBuffer(length);
+ System.arraycopy(buffer, offset, this.buffer, this.offset, length);
+ bufferConsumed(length);
+ }
+
+ public boolean fill() throws IOException
+ {
+ if(stream == null) {
+ return false;
+ }
+ reserveBuffer(bufferReserveSize);
+ int rl = stream.read(getBuffer(), getBufferOffset(), getBufferCapacity());
+ if(rl <= 0) {
+ return false;
+ }
+ bufferConsumed(rl);
+ return true;
+ }
+
+ public Iterator<Object> iterator()
+ {
+ return new UnpackIterator(this);
+ }
+
+ public boolean execute() throws UnpackException
+ {
+ int noffset = super.execute(buffer, offset, used);
+ if(noffset <= offset) {
+ return false;
+ }
+ parsed += noffset - offset;
+ offset = noffset;
+ return super.isFinished();
+ }
+
+ public Object getData()
+ {
+ return super.getData();
+ }
+
+ public void reset()
+ {
+ super.reset();
+ parsed = 0;
+ }
+
+ public int getMessageSize()
+ {
+ return parsed - offset + used;
+ }
+
+ public int getParsedSize()
+ {
+ return parsed;
+ }
+
+ public int getNonParsedSize()
+ {
+ return used - offset;
+ }
+
+ public void skipNonparsedBuffer(int size)
+ {
+ offset += size;
+ }
+
+ public void removeNonparsedBuffer()
+ {
+ used = offset;
+ }
+
+ /*
+ public static class Context {
+ private boolean finished;
+ private Object data;
+ private int offset;
+ private UnpackerImpl impl;
+
+ public Context()
+ {
+ this.finished = false;
+ this.impl = new UnpackerImpl();
+ }
+
+ public boolean isFinished()
+ {
+ return finished;
+ }
+
+ public Object getData()
+ {
+ return data;
+ }
+
+ int getOffset()
+ {
+ return offset;
+ }
+
+ void setFinished(boolean finished)
+ {
+ this.finished = finished;
+ }
+
+ void setData(Object data)
+ {
+ this.data = data;
+ }
+
+ void setOffset(int offset)
+ {
+ this.offset = offset;
+ }
+
+ UnpackerImpl getImpl()
+ {
+ return impl;
+ }
+ }
+
+ public static int unpack(Context ctx, byte[] buffer) throws UnpackException
+ {
+ return unpack(ctx, buffer, 0, buffer.length);
+ }
+
+ public static int unpack(Context ctx, byte[] buffer, int offset, int length) throws UnpackException
+ {
+ UnpackerImpl impl = ctx.getImpl();
+ int noffset = impl.execute(buffer, offset + ctx.getOffset(), length);
+ ctx.setOffset(noffset - offset);
+ if(impl.isFinished()) {
+ ctx.setData(impl.getData());
+ ctx.setFinished(false);
+ impl.reset();
+ } else {
+ ctx.setData(null);
+ ctx.setFinished(true);
+ }
+ int parsed = noffset - offset;
+ ctx.setOffset(parsed);
+ return noffset;
+ }
+ */
+}
+
diff --git a/java-plan2/src/org/msgpack/impl/ArrayBuilder.java b/java-plan2/src/org/msgpack/impl/ArrayBuilder.java
new file mode 100644
index 0000000..9bb099b
--- /dev/null
+++ b/java-plan2/src/org/msgpack/impl/ArrayBuilder.java
@@ -0,0 +1,7 @@
+package org.msgpack.impl;
+
+public interface ArrayBuilder {
+ public void add(Object element);
+ public Object finish();
+}
+
diff --git a/java-plan2/src/org/msgpack/impl/GenericObjectBuilder.java b/java-plan2/src/org/msgpack/impl/GenericObjectBuilder.java
new file mode 100644
index 0000000..814e302
--- /dev/null
+++ b/java-plan2/src/org/msgpack/impl/GenericObjectBuilder.java
@@ -0,0 +1,127 @@
+package org.msgpack.impl;
+
+import org.msgpack.*;
+
+public class GenericObjectBuilder implements ObjectBuilder {
+ public Object createNil()
+ {
+ return null;
+ }
+
+ @Override
+ public Object createBoolean(boolean v)
+ {
+ return new GenericBoolean(v);
+ }
+
+ @Override
+ public Object createByte(byte v)
+ {
+ //return new GenericByte(v);
+ return null; // FIXME
+ }
+
+ @Override
+ public Object createShort(short v)
+ {
+ return new GenericShort(v);
+ }
+
+ @Override
+ public Object createInt(int v)
+ {
+ //return new GenericInt(v);
+ return null; // FIXME
+ }
+
+ @Override
+ public Object createLong(long v)
+ {
+ return new GenericLong(v);
+ }
+
+ @Override
+ public Object createFloat(float v)
+ {
+ //return new GenericFloat(v);
+ return null; // FIXME
+ }
+
+ @Override
+ public Object createDouble(double v)
+ {
+ //return new GenericDouble(v);
+ return null; // FIXME
+ }
+
+ @Override
+ public Object createRaw(byte[] b, int offset, int length)
+ {
+ byte[] copy = new byte[length];
+ System.arraycopy(b, offset, copy, 0, length);
+ return new GenericRaw(copy);
+ }
+
+ @Override
+ public ArrayBuilder createArray(int length)
+ {
+ return new GenericArrayBuilder(length);
+ }
+
+ @Override
+ public MapBuilder createMap(int length)
+ {
+ return new GenericMapBuilder(length);
+ }
+}
+
+final class GenericArrayBuilder implements ArrayBuilder {
+ private GenericArray a;
+
+ GenericArrayBuilder(int length)
+ {
+ this.a = new GenericArray(length);
+ }
+
+ @Override
+ public void add(Object element)
+ {
+ a.add((GenericObject)element);
+ }
+
+ @Override
+ public Object finish()
+ {
+ return a;
+ }
+}
+
+final class GenericMapBuilder implements MapBuilder {
+ private GenericMap m;
+ private GenericObject key;
+
+ GenericMapBuilder(int length)
+ {
+ this.m = new GenericMap(length);
+ }
+
+ @Override
+ public void putKey(Object key)
+ {
+ this.key = (GenericObject)key;
+ }
+
+ @Override
+ public void putValue(Object value)
+ {
+ m.put(this.key, (GenericObject)value);
+ this.key = null;
+ }
+
+ @Override
+ public Object finish()
+ {
+ return m;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/impl/GenericZeroCopyObjectBuilder.java b/java-plan2/src/org/msgpack/impl/GenericZeroCopyObjectBuilder.java
new file mode 100644
index 0000000..5569fbf
--- /dev/null
+++ b/java-plan2/src/org/msgpack/impl/GenericZeroCopyObjectBuilder.java
@@ -0,0 +1,12 @@
+package org.msgpack.impl;
+
+import org.msgpack.*;
+
+public class GenericZeroCopyObjectBuilder extends GenericObjectBuilder {
+ @Override
+ public Object createRaw(byte[] b, int offset, int length)
+ {
+ return new GenericRawRef(b, offset, length);
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/impl/MapBuilder.java b/java-plan2/src/org/msgpack/impl/MapBuilder.java
new file mode 100644
index 0000000..57859a6
--- /dev/null
+++ b/java-plan2/src/org/msgpack/impl/MapBuilder.java
@@ -0,0 +1,8 @@
+package org.msgpack.impl;
+
+public interface MapBuilder {
+ public void putKey(Object key);
+ public void putValue(Object value);
+ public Object finish();
+}
+
diff --git a/java-plan2/src/org/msgpack/impl/ObjectBuilder.java b/java-plan2/src/org/msgpack/impl/ObjectBuilder.java
new file mode 100644
index 0000000..3268903
--- /dev/null
+++ b/java-plan2/src/org/msgpack/impl/ObjectBuilder.java
@@ -0,0 +1,16 @@
+package org.msgpack.impl;
+
+public interface ObjectBuilder {
+ public Object createNil();
+ public Object createBoolean(boolean v);
+ public Object createByte(byte v);
+ public Object createShort(short v);
+ public Object createInt(int v);
+ public Object createLong(long v);
+ public Object createFloat(float v);
+ public Object createDouble(double v);
+ public Object createRaw(byte[] b, int offset, int length);
+ public ArrayBuilder createArray(int length);
+ public MapBuilder createMap(int length);
+}
+
diff --git a/java-plan2/src/org/msgpack/impl/SpecificObjectBuilder.java b/java-plan2/src/org/msgpack/impl/SpecificObjectBuilder.java
new file mode 100644
index 0000000..7748844
--- /dev/null
+++ b/java-plan2/src/org/msgpack/impl/SpecificObjectBuilder.java
@@ -0,0 +1,203 @@
+package org.msgpack.impl;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.HashMap;
+import org.msgpack.schema.*;
+
+public final class SpecificObjectBuilder implements ObjectBuilder {
+ private int top;
+ private Schema schema;
+
+ public SpecificObjectBuilder(Schema schema)
+ {
+ this.top = 0;
+ this.schema = schema;
+ }
+
+ void setSchema(Schema s)
+ {
+ schema = s;
+ }
+
+ Schema swapSchema(Schema s)
+ {
+ Schema old = schema;
+ schema = s;
+ return old;
+ }
+
+ @Override
+ public Object createNil()
+ {
+ return schema.createNil();
+ }
+
+ @Override
+ public Object createBoolean(boolean v)
+ {
+ return schema.createBoolean(v);
+ }
+
+ @Override
+ public Object createByte(byte v)
+ {
+ return schema.createByte(v);
+ }
+
+ @Override
+ public Object createShort(short v)
+ {
+ return schema.createShort(v);
+ }
+
+ @Override
+ public Object createInt(int v)
+ {
+ return schema.createInt(v);
+ }
+
+ @Override
+ public Object createLong(long v)
+ {
+ return schema.createLong(v);
+ }
+
+ @Override
+ public Object createFloat(float v)
+ {
+ return schema.createFloat(v);
+ }
+
+ @Override
+ public Object createDouble(double v)
+ {
+ return schema.createDouble(v);
+ }
+
+ @Override
+ public Object createRaw(byte[] b, int offset, int length)
+ {
+ return schema.createRaw(b, offset, length);
+ }
+
+ @Override
+ public ArrayBuilder createArray(int length)
+ {
+ if(schema instanceof ClassSchema) {
+ return new ClassBuilder(length, (ClassSchema)schema, this);
+ }
+ ArraySchema as = (ArraySchema)schema;
+ return new SpecificArrayBuilder(length, as.getElementType(), this);
+ }
+
+ @Override
+ public MapBuilder createMap(int length)
+ {
+ MapSchema ms = (MapSchema)schema;
+ return new SpecificMapBuilder(length, ms.getKeyType(), ms.getValueType(), this);
+ }
+}
+
+final class SpecificArrayBuilder implements ArrayBuilder {
+ private ArrayList a;
+ private SpecificObjectBuilder builder;
+ private Schema parentSchema;
+
+ SpecificArrayBuilder(int length, Schema elementSchema, SpecificObjectBuilder builder)
+ {
+ this.a = new ArrayList(length);
+ this.builder = builder;
+ this.parentSchema = builder.swapSchema(elementSchema);
+ }
+
+ public void add(Object element)
+ {
+ a.add(element);
+ }
+
+ public Object finish()
+ {
+ builder.swapSchema(parentSchema);
+ return a;
+ }
+}
+
+final class SpecificMapBuilder implements MapBuilder {
+ private HashMap m;
+ private Object key;
+ private Schema keySchema;
+ private Schema valueSchema;
+ private SpecificObjectBuilder builder;
+ private Schema parentSchema;
+
+ SpecificMapBuilder(int length, Schema keySchema, Schema valueSchema, SpecificObjectBuilder builder)
+ {
+ this.m = new HashMap(length);
+ this.keySchema = keySchema;
+ this.valueSchema = valueSchema;
+ this.builder = builder;
+ this.parentSchema = builder.swapSchema(keySchema);
+ }
+
+ @Override
+ public void putKey(Object key)
+ {
+ this.key = key;
+ this.builder.setSchema(valueSchema);
+ }
+
+ @Override
+ public void putValue(Object value)
+ {
+ m.put(this.key, value);
+ this.key = null;
+ this.builder.setSchema(keySchema);
+ }
+
+ @Override
+ public Object finish()
+ {
+ builder.swapSchema(parentSchema);
+ return m;
+ }
+}
+
+final class ClassBuilder implements ArrayBuilder {
+ private Object object;
+ private int index;
+ private List<? extends FieldSchema> fields;
+ private SpecificObjectBuilder builder;
+ private Schema parentSchema;
+
+ ClassBuilder(int length, ClassSchema schema, SpecificObjectBuilder builder)
+ {
+ this.object = schema.newInstance();
+ this.index = 0;
+ this.fields = schema.getFields();
+ this.builder = builder;
+ this.parentSchema = builder.swapSchema(fields.get(0).getType());
+ // FIXME check length
+ }
+
+ @Override
+ public void add(Object element)
+ {
+ FieldSchema f = fields.get(index++); // FIXME check fields.size
+ f.setFieldValue(object, element); // XXX FIXME debug
+ if(fields.size() > index) {
+ builder.setSchema( fields.get(index).getType() );
+ } else {
+ builder.setSchema( null );
+ // FIXME: builder.setSchema(new InvalidFieldSchema);
+ }
+ }
+
+ @Override
+ public Object finish()
+ {
+ builder.swapSchema(parentSchema);
+ return object;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/impl/UnpackerImpl.java b/java-plan2/src/org/msgpack/impl/UnpackerImpl.java
new file mode 100644
index 0000000..9f88072
--- /dev/null
+++ b/java-plan2/src/org/msgpack/impl/UnpackerImpl.java
@@ -0,0 +1,395 @@
+package org.msgpack.impl;
+
+import java.nio.ByteBuffer;
+//import java.math.BigInteger;
+import org.msgpack.UnpackException;
+
+public class UnpackerImpl {
+ static final int CS_HEADER = 0x00;
+ static final int CS_FLOAT = 0x0a;
+ static final int CS_DOUBLE = 0x0b;
+ static final int CS_UINT_8 = 0x0c;
+ static final int CS_UINT_16 = 0x0d;
+ static final int CS_UINT_32 = 0x0e;
+ static final int CS_UINT_64 = 0x0f;
+ static final int CS_INT_8 = 0x10;
+ static final int CS_INT_16 = 0x11;
+ static final int CS_INT_32 = 0x12;
+ static final int CS_INT_64 = 0x13;
+ static final int CS_RAW_16 = 0x1a;
+ static final int CS_RAW_32 = 0x1b;
+ static final int CS_ARRAY_16 = 0x1c;
+ static final int CS_ARRAY_32 = 0x1d;
+ static final int CS_MAP_16 = 0x1e;
+ static final int CS_MAP_32 = 0x1f;
+ static final int ACS_RAW_VALUE = 0x20;
+ static final int CT_ARRAY_ITEM = 0x00;
+ static final int CT_MAP_KEY = 0x01;
+ static final int CT_MAP_VALUE = 0x02;
+
+ static final int MAX_STACK_SIZE = 16;
+
+ protected int cs = CS_HEADER;
+ protected int trail = 0;
+ protected int top = -1;
+ protected boolean finished = false;
+ protected Object data = null;
+ protected int[] stack_ct = new int[MAX_STACK_SIZE];
+ protected int[] stack_count = new int[MAX_STACK_SIZE];
+ protected Object[] stack_obj = new Object[MAX_STACK_SIZE];
+ protected ByteBuffer castBuffer = ByteBuffer.allocate(8);
+ protected ObjectBuilder builder;
+
+ protected UnpackerImpl(ObjectBuilder builder)
+ {
+ this.builder = builder;
+ }
+
+ protected void setBuilder(ObjectBuilder builder)
+ {
+ this.builder = builder;
+ }
+
+ protected Object getData()
+ {
+ return data;
+ }
+
+ protected boolean isFinished()
+ {
+ return finished;
+ }
+
+ protected void reset()
+ {
+ for(int i=0; i <= top; ++top) {
+ stack_ct[top] = 0;
+ stack_count[top] = 0;
+ stack_obj[top] = null;
+ }
+ cs = CS_HEADER;
+ trail = 0;
+ top = -1;
+ finished = false;
+ data = null;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected int execute(byte[] src, int off, int length) throws UnpackException
+ {
+ if(off >= length) { return off; }
+
+ int limit = length;
+ int i = off;
+ int count;
+
+ Object obj = null;
+
+ _out: do {
+ _header_again: {
+ //System.out.println("while i:"+i+" limit:"+limit);
+
+ int b = src[i];
+
+ _push: {
+ _fixed_trail_again:
+ if(cs == CS_HEADER) {
+
+ if((b & 0x80) == 0) { // Positive Fixnum
+ //System.out.println("positive fixnum "+b);
+ obj = builder.createByte((byte)b);
+ break _push;
+ }
+
+ if((b & 0xe0) == 0xe0) { // Negative Fixnum
+ //System.out.println("negative fixnum "+b);
+ obj = builder.createByte((byte)b);
+ break _push;
+ }
+
+ if((b & 0xe0) == 0xa0) { // FixRaw
+ trail = b & 0x1f;
+ if(trail == 0) {
+ obj = builder.createRaw(new byte[0], 0, 0);
+ break _push;
+ }
+ cs = ACS_RAW_VALUE;
+ break _fixed_trail_again;
+ }
+
+ if((b & 0xf0) == 0x90) { // FixArray
+ if(top >= MAX_STACK_SIZE) {
+ throw new UnpackException("parse error", UnpackException.PARSE_ERROR);
+ }
+ count = b & 0x0f;
+ ++top;
+ stack_obj[top] = builder.createArray(count);
+ stack_ct[top] = CT_ARRAY_ITEM;
+ stack_count[top] = count;
+ //System.out.println("fixarray count:"+count);
+ break _header_again;
+ }
+
+ if((b & 0xf0) == 0x80) { // FixMap
+ if(top >= MAX_STACK_SIZE) {
+ throw new UnpackException("parse error", UnpackException.PARSE_ERROR);
+ }
+ count = b & 0x0f;
+ ++top;
+ stack_obj[top] = builder.createMap(count);
+ stack_ct[top] = CT_MAP_KEY;
+ stack_count[top] = count;
+ //System.out.println("fixmap count:"+count);
+ break _header_again;
+ }
+
+ switch(b & 0xff) { // FIXME
+ case 0xc0: // nil
+ obj = builder.createNil();
+ break _push;
+ case 0xc2: // false
+ obj = builder.createBoolean(false);
+ break _push;
+ case 0xc3: // true
+ obj = builder.createBoolean(true);
+ break _push;
+ case 0xca: // float
+ case 0xcb: // double
+ case 0xcc: // unsigned int 8
+ case 0xcd: // unsigned int 16
+ case 0xce: // unsigned int 32
+ case 0xcf: // unsigned int 64
+ case 0xd0: // signed int 8
+ case 0xd1: // signed int 16
+ case 0xd2: // signed int 32
+ case 0xd3: // signed int 64
+ trail = 1 << (b & 0x03);
+ cs = b & 0x1f;
+ //System.out.println("a trail "+trail+" cs:"+cs);
+ break _fixed_trail_again;
+ case 0xda: // raw 16
+ case 0xdb: // raw 32
+ case 0xdc: // array 16
+ case 0xdd: // array 32
+ case 0xde: // map 16
+ case 0xdf: // map 32
+ trail = 2 << (b & 0x01);
+ cs = b & 0x1f;
+ //System.out.println("b trail "+trail+" cs:"+cs);
+ break _fixed_trail_again;
+ default:
+ //System.out.println("unknown b "+(b&0xff));
+ throw new UnpackException("parse error", UnpackException.PARSE_ERROR);
+ }
+
+ } // _fixed_trail_again
+
+ do {
+ _fixed_trail_again: {
+
+ if(limit - i <= trail) { break _out; }
+ int n = i + 1;
+ i += trail;
+
+ switch(cs) {
+ case CS_FLOAT:
+ castBuffer.rewind();
+ castBuffer.put(src, n, 4);
+ obj = builder.createFloat( castBuffer.getFloat(0) );
+ //System.out.println("float "+obj);
+ break _push;
+ case CS_DOUBLE:
+ castBuffer.rewind();
+ castBuffer.put(src, n, 8);
+ obj = builder.createDouble( castBuffer.getDouble(0) );
+ //System.out.println("double "+obj);
+ break _push;
+ case CS_UINT_8:
+ //System.out.println(n);
+ //System.out.println(src[n]);
+ //System.out.println(src[n+1]);
+ //System.out.println(src[n-1]);
+ obj = builder.createShort( (short)((src[n]) & 0xff) );
+ //System.out.println("uint8 "+obj);
+ break _push;
+ case CS_UINT_16:
+ //System.out.println(src[n]);
+ //System.out.println(src[n+1]);
+ castBuffer.rewind();
+ castBuffer.put(src, n, 2);
+ obj = builder.createInt( ((int)castBuffer.getShort(0)) & 0xffff );
+ //System.out.println("uint 16 "+obj);
+ break _push;
+ case CS_UINT_32:
+ castBuffer.rewind();
+ castBuffer.put(src, n, 4);
+ obj = builder.createLong( ((long)castBuffer.getInt(0)) & 0xffffffffL );
+ //System.out.println("uint 32 "+obj);
+ break _push;
+ case CS_UINT_64:
+ castBuffer.rewind();
+ castBuffer.put(src, n, 8);
+ {
+ long o = castBuffer.getLong(0);
+ if(o < 0) {
+ // FIXME
+ //obj = GenericBigInteger.valueOf(o & 0x7fffffffL).setBit(31);
+ } else {
+ obj = builder.createLong( o );
+ }
+ }
+ throw new UnpackException("uint 64 bigger than 0x7fffffff is not supported", UnpackException.PARSE_ERROR);
+ case CS_INT_8:
+ obj = builder.createByte( src[n] );
+ break _push;
+ case CS_INT_16:
+ castBuffer.rewind();
+ castBuffer.put(src, n, 2);
+ obj = builder.createShort( castBuffer.getShort(0) );
+ break _push;
+ case CS_INT_32:
+ castBuffer.rewind();
+ castBuffer.put(src, n, 4);
+ obj = builder.createInt( castBuffer.getInt(0) );
+ break _push;
+ case CS_INT_64:
+ castBuffer.rewind();
+ castBuffer.put(src, n, 8);
+ obj = builder.createLong( castBuffer.getLong(0) );
+ break _push;
+ case CS_RAW_16:
+ castBuffer.rewind();
+ castBuffer.put(src, n, 2);
+ trail = ((int)castBuffer.getShort(0)) & 0xffff;
+ if(trail == 0) {
+ obj = builder.createRaw(new byte[0], 0, 0);
+ break _push;
+ }
+ cs = ACS_RAW_VALUE;
+ break _fixed_trail_again;
+ case CS_RAW_32:
+ castBuffer.rewind();
+ castBuffer.put(src, n, 4);
+ // FIXME overflow check
+ trail = castBuffer.getInt(0) & 0x7fffffff;
+ if(trail == 0) {
+ obj = builder.createRaw(new byte[0], 0, 0);
+ break _push;
+ }
+ cs = ACS_RAW_VALUE;
+ case ACS_RAW_VALUE:
+ obj = builder.createRaw(src, n, trail);
+ break _push;
+ case CS_ARRAY_16:
+ if(top >= MAX_STACK_SIZE) {
+ throw new UnpackException("parse error", UnpackException.PARSE_ERROR);
+ }
+ castBuffer.rewind();
+ castBuffer.put(src, n, 2);
+ count = ((int)castBuffer.getShort(0)) & 0xffff;
+ ++top;
+ stack_obj[top] = builder.createArray(count);
+ stack_ct[top] = CT_ARRAY_ITEM;
+ stack_count[top] = count;
+ break _header_again;
+ case CS_ARRAY_32:
+ if(top >= MAX_STACK_SIZE) {
+ throw new UnpackException("parse error", UnpackException.PARSE_ERROR);
+ }
+ castBuffer.rewind();
+ castBuffer.put(src, n, 4);
+ // FIXME overflow check
+ count = castBuffer.getInt(0) & 0x7fffffff;
+ ++top;
+ stack_obj[top] = builder.createArray(count);
+ stack_ct[top] = CT_ARRAY_ITEM;
+ stack_count[top] = count;
+ break _header_again;
+ case CS_MAP_16:
+ if(top >= MAX_STACK_SIZE) {
+ throw new UnpackException("parse error", UnpackException.PARSE_ERROR);
+ }
+ castBuffer.rewind();
+ castBuffer.put(src, n, 2);
+ count = ((int)castBuffer.getShort(0)) & 0xffff;
+ ++top;
+ stack_obj[top] = builder.createMap(count);
+ stack_ct[top] = CT_MAP_KEY;
+ stack_count[top] = count;
+ //System.out.println("fixmap count:"+count);
+ break _header_again;
+ case CS_MAP_32:
+ if(top >= MAX_STACK_SIZE) {
+ throw new UnpackException("parse error", UnpackException.PARSE_ERROR);
+ }
+ castBuffer.rewind();
+ castBuffer.put(src, n, 4);
+ // FIXME overflow check
+ count = castBuffer.getInt(0) & 0x7fffffff;
+ ++top;
+ stack_obj[top] = builder.createMap(count);
+ stack_ct[top] = CT_MAP_KEY;
+ stack_count[top] = count;
+ //System.out.println("fixmap count:"+count);
+ break _header_again;
+ default:
+ throw new UnpackException("parse error", UnpackException.PARSE_ERROR);
+ }
+
+ } // _fixed_trail_again
+ } while(true);
+ } // _push
+
+ do {
+ _push: {
+ //System.out.println("push top:"+top);
+ if(top == -1) {
+ ++i;
+ data = obj;
+ finished = true;
+ break _out;
+ }
+
+ switch(stack_ct[top]) {
+ case CT_ARRAY_ITEM:
+ //System.out.println("array item "+obj);
+ ((ArrayBuilder)stack_obj[top]).add(obj);
+ if(--stack_count[top] == 0) {
+ obj = ((ArrayBuilder)stack_obj[top]).finish();
+ stack_obj[top] = null;
+ --top;
+ break _push;
+ }
+ break _header_again;
+ case CT_MAP_KEY:
+ //System.out.println("map key:"+top+" "+obj);
+ MapBuilder mb = (MapBuilder)stack_obj[top];
+ mb.putKey(obj);
+ stack_ct[top] = CT_MAP_VALUE;
+ break _header_again;
+ case CT_MAP_VALUE:
+ //System.out.println("map value:"+top+" "+obj);
+ ((MapBuilder)stack_obj[top]).putValue(obj);
+ if(--stack_count[top] == 0) {
+ obj = ((MapBuilder)stack_obj[top]).finish();
+ stack_obj[top] = null;
+ --top;
+ break _push;
+ }
+ stack_ct[top] = CT_MAP_KEY;
+ break _header_again;
+ default:
+ throw new UnpackException("parse error", UnpackException.PARSE_ERROR);
+ }
+ } // _push
+ } while(true);
+
+ } // _header_again
+ cs = CS_HEADER;
+ ++i;
+ } while(i < limit); // _out
+
+ return i;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/ArraySchema.java b/java-plan2/src/org/msgpack/schema/ArraySchema.java
new file mode 100644
index 0000000..4b05190
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/ArraySchema.java
@@ -0,0 +1,61 @@
+package org.msgpack.schema;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.io.IOException;
+import org.msgpack.*;
+
+public class ArraySchema extends Schema {
+ private Schema elementType;
+
+ public ArraySchema(Schema elementType)
+ {
+ super("array");
+ this.elementType = elementType;
+ }
+
+ public Schema getElementType()
+ {
+ return elementType;
+ }
+
+ @Override
+ public String getFullName()
+ {
+ return "ArrayList<"+elementType.getFullName()+">";
+ }
+
+ @Override
+ public String getExpression()
+ {
+ return "(array "+elementType.getExpression()+")";
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void pack(Packer pk, Object obj) throws IOException
+ {
+ if(obj == null) {
+ pk.packNil();
+ return;
+ }
+ List<Object> d = (List<Object>)obj;
+ pk.packArray(d.size());
+ for(Object e : d) {
+ elementType.pack(pk, e);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object convert(GenericObject obj)
+ {
+ List<GenericObject> d = obj.asArray();
+ List<Object> g = new ArrayList<Object>();
+ for(GenericObject o : d) {
+ g.add( elementType.convert(o) );
+ }
+ return g;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/ClassGenerator.java b/java-plan2/src/org/msgpack/schema/ClassGenerator.java
new file mode 100644
index 0000000..25a9620
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/ClassGenerator.java
@@ -0,0 +1,230 @@
+package org.msgpack.schema;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.io.IOException;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.Writer;
+
+public class ClassGenerator {
+ private ClassSchema schema;
+ private Writer writer;
+ private int indent;
+
+ private ClassGenerator(Writer writer)
+ {
+ this.writer = writer;
+ this.indent = 0;
+ }
+
+ public static void write(Schema schema, Writer dest) throws IOException
+ {
+ if(!(schema instanceof ClassSchema)) {
+ throw new RuntimeException("schema is not class schema");
+ }
+ ClassSchema cs = (ClassSchema)schema;
+ new ClassGenerator(dest).run(cs);
+ }
+
+ private void run(ClassSchema cs) throws IOException
+ {
+ List<ClassSchema> subclasses = new ArrayList<ClassSchema>();
+ for(FieldSchema f : cs.getFields()) {
+ findSubclassSchema(subclasses, f.getType());
+ }
+
+ for(ClassSchema sub : subclasses) {
+ sub.setNamespace(cs.getNamespace());
+ sub.setImports(cs.getImports());
+ }
+
+ this.schema = cs;
+
+ writeHeader();
+
+ writeClass();
+
+ for(ClassSchema sub : subclasses) {
+ this.schema = sub;
+ writeSubclass();
+ }
+
+ writeFooter();
+
+ this.schema = null;
+ writer.flush();
+ }
+
+ private void findSubclassSchema(List<ClassSchema> dst, Schema s)
+ {
+ if(s instanceof ClassSchema) {
+ ClassSchema cs = (ClassSchema)s;
+ if(!dst.contains(cs)) { dst.add(cs); }
+ for(FieldSchema f : cs.getFields()) {
+ findSubclassSchema(dst, f.getType());
+ }
+ } else if(s instanceof ArraySchema) {
+ ArraySchema as = (ArraySchema)s;
+ findSubclassSchema(dst, as.getElementType());
+ } else if(s instanceof MapSchema) {
+ MapSchema as = (MapSchema)s;
+ findSubclassSchema(dst, as.getKeyType());
+ findSubclassSchema(dst, as.getValueType());
+ }
+ }
+
+ private void writeHeader() throws IOException
+ {
+ if(schema.getNamespace() != null) {
+ line("package "+schema.getNamespace()+";");
+ line();
+ }
+ line("import java.util.*;");
+ line("import java.io.*;");
+ line("import org.msgpack.*;");
+ line("import org.msgpack.schema.*;");
+ }
+
+ private void writeFooter() throws IOException
+ {
+ }
+
+ private void writeClass() throws IOException
+ {
+ line();
+ line("public final class "+schema.getName()+" implements MessagePackable, MessageConvertable");
+ line("{");
+ pushIndent();
+ writeSchema();
+ writeMemberVariables();
+ writeMemberFunctions();
+ popIndent();
+ line("}");
+ }
+
+ private void writeSubclass() throws IOException
+ {
+ line();
+ line("final class "+schema.getName()+" implements MessagePackable, MessageConvertable");
+ line("{");
+ pushIndent();
+ writeSchema();
+ writeMemberVariables();
+ writeMemberFunctions();
+ popIndent();
+ line("}");
+ }
+
+ private void writeSchema() throws IOException
+ {
+ line("private static final ClassSchema _SCHEMA = (ClassSchema)Schema.load(\""+schema.getExpression()+"\");");
+ line("public static ClassSchema getSchema() { return _SCHEMA; }");
+ }
+
+ private void writeMemberVariables() throws IOException
+ {
+ line();
+ for(FieldSchema f : schema.getFields()) {
+ line("public "+f.getType().getFullName()+" "+f.getName()+";");
+ }
+ }
+
+ private void writeMemberFunctions() throws IOException
+ {
+ // void messagePack(Packer pk)
+ // boolean equals(Object obj)
+ // int hashCode()
+ // void set(int _index, Object _value)
+ // Object get(int _index);
+ // getXxx()
+ // setXxx(Xxx xxx)
+ writeConstructors();
+ writeAccessors();
+ writePackFunction();
+ writeConvertFunction();
+ }
+
+ private void writeConstructors() throws IOException
+ {
+ line();
+ line("public "+schema.getName()+"() { }");
+ }
+
+ private void writeAccessors() throws IOException
+ {
+ // FIXME
+ //line();
+ //for(FieldSchema f : schema.getFields()) {
+ // line("");
+ //}
+ }
+
+ private void writePackFunction() throws IOException
+ {
+ line();
+ line("@Override");
+ line("public void messagePack(Packer pk) throws IOException");
+ line("{");
+ pushIndent();
+ line("List<? extends FieldSchema> _f = _SCHEMA.getFields();");
+ line("pk.packArray("+schema.getFields().size()+");");
+ int i = 0;
+ for(FieldSchema f : schema.getFields()) {
+ line("_f.get("+i+").getType().pack(pk, "+f.getName()+");");
+ ++i;
+ }
+ popIndent();
+ line("}");
+ }
+
+ private void writeConvertFunction() throws IOException
+ {
+ line();
+ line("@Override");
+ line("@SuppressWarnings(\"unchecked\")");
+ line("public void messageConvert(GenericObject obj)");
+ line("{");
+ pushIndent();
+ line("List<GenericObject> _l = obj.asArray();");
+ line("List<? extends FieldSchema> _f = _SCHEMA.getFields();");
+ int i = 0;
+ for(FieldSchema f : schema.getFields()) {
+ line("if(_l.size() <= "+i+") { return; } "+f.getName()+" = ("+f.getType().getFullName()+")_f.get("+i+").getType().convert(_l.get("+i+"));");
+ ++i;
+ }
+ popIndent();
+ line("}");
+ line();
+ line("public static "+schema.getName()+" convert(GenericObject obj)");
+ line("{");
+ pushIndent();
+ line("return ("+schema.getName()+")_SCHEMA.convert(obj);");
+ popIndent();
+ line("}");
+ }
+
+ private void line(String str) throws IOException
+ {
+ for(int i=0; i < indent; ++i) {
+ writer.write("\t");
+ }
+ writer.write(str+"\n");
+ }
+
+ private void line() throws IOException
+ {
+ writer.write("\n");
+ }
+
+ private void pushIndent()
+ {
+ indent += 1;
+ }
+
+ private void popIndent()
+ {
+ indent -= 1;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/ClassSchema.java b/java-plan2/src/org/msgpack/schema/ClassSchema.java
new file mode 100644
index 0000000..3343fca
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/ClassSchema.java
@@ -0,0 +1,39 @@
+package org.msgpack.schema;
+
+import java.util.List;
+
+public abstract class ClassSchema extends Schema {
+ protected String namespace;
+ protected List<String> imports;
+
+ public ClassSchema(String name, String namespace, List<String> imports)
+ {
+ super(name);
+ this.namespace = namespace;
+ this.imports = imports;
+ }
+
+ public String getNamespace()
+ {
+ return namespace;
+ }
+
+ public List<String> getImports()
+ {
+ return imports;
+ }
+
+ void setNamespace(String namespace)
+ {
+ this.namespace = namespace;
+ }
+
+ void setImports(List<String> imports)
+ {
+ this.imports = imports;
+ }
+
+ public abstract List<? extends FieldSchema> getFields();
+ public abstract Object newInstance();
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/FieldSchema.java b/java-plan2/src/org/msgpack/schema/FieldSchema.java
new file mode 100644
index 0000000..31a132c
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/FieldSchema.java
@@ -0,0 +1,31 @@
+package org.msgpack.schema;
+
+public abstract class FieldSchema {
+ private String name;
+ private Schema type;
+
+ public FieldSchema(String name, Schema type)
+ {
+ this.name = name;
+ this.type = type;
+ }
+
+ public String getName()
+ {
+ return this.name;
+ }
+
+ public Schema getType()
+ {
+ return type;
+ }
+
+ public String getExpression()
+ {
+ return "(field "+name+" "+type.getExpression()+")";
+ }
+
+ public abstract Object getFieldValue(Object obj);
+ public abstract void setFieldValue(Object obj, Object value);
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/GenericClassSchema.java b/java-plan2/src/org/msgpack/schema/GenericClassSchema.java
new file mode 100644
index 0000000..f1e2b44
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/GenericClassSchema.java
@@ -0,0 +1,89 @@
+package org.msgpack.schema;
+
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import org.msgpack.*;
+
+
+public class GenericClassSchema extends ClassSchema {
+ private List<GenericFieldSchema> fields;
+
+ private String fqdn;
+ private Constructor constructorCache;
+
+ public GenericClassSchema(String name, List<GenericFieldSchema> fields, String namespace, List<String> imports)
+ {
+ super(name, namespace, imports);
+ this.fields = fields;
+ if(namespace == null) {
+ this.fqdn = name;
+ } else {
+ this.fqdn = namespace+"."+name;
+ }
+ }
+
+ //@Override
+ //public String getFullName()
+ //{
+ // if(namespace == null) {
+ // return getName();
+ // } else {
+ // return namespace+"."+getName();
+ // }
+ //}
+
+ public List<? extends FieldSchema> getFields()
+ {
+ return fields;
+ }
+
+ public String getNamespace()
+ {
+ return namespace;
+ }
+
+ @Override
+ public String getExpression()
+ {
+ StringBuffer b = new StringBuffer();
+ b.append("(class ");
+ b.append(getName());
+ if(namespace != null) {
+ b.append(" (package "+namespace+")");
+ }
+ for(GenericFieldSchema f : fields) {
+ b.append(" "+f.getExpression());
+ }
+ b.append(")");
+ return b.toString();
+ }
+
+ @Override
+ public void pack(Packer pk, Object obj) throws IOException
+ {
+ if(obj == null) {
+ pk.packNil();
+ return;
+ }
+ // FIXME
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object convert(GenericObject obj)
+ {
+ // FIXME
+ return obj;
+ }
+
+ @Override
+ public Object newInstance()
+ {
+ return new HashMap(fields.size());
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/GenericFieldSchema.java b/java-plan2/src/org/msgpack/schema/GenericFieldSchema.java
new file mode 100644
index 0000000..507ee18
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/GenericFieldSchema.java
@@ -0,0 +1,25 @@
+package org.msgpack.schema;
+
+import java.util.Map;
+import java.lang.reflect.Field;
+
+public final class GenericFieldSchema extends FieldSchema {
+ public GenericFieldSchema(String name, Schema type)
+ {
+ super(name, type);
+ }
+
+ @Override
+ public Object getFieldValue(Object obj)
+ {
+ return ((Map)obj).get(getName());
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void setFieldValue(Object obj, Object value)
+ {
+ ((Map)obj).put(getName(), value);
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/IntSchema.java b/java-plan2/src/org/msgpack/schema/IntSchema.java
new file mode 100644
index 0000000..69771c3
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/IntSchema.java
@@ -0,0 +1,58 @@
+package org.msgpack.schema;
+
+import java.io.IOException;
+import org.msgpack.*;
+
+public class IntSchema extends Schema {
+ public IntSchema()
+ {
+ super("Integer");
+ }
+
+ @Override
+ public String getExpression()
+ {
+ return "int";
+ }
+
+ @Override
+ public void pack(Packer pk, Object obj) throws IOException
+ {
+ if(obj == null) {
+ pk.packNil();
+ return;
+ }
+ pk.packInt((Integer)obj);
+ }
+
+ @Override
+ public Object convert(GenericObject obj)
+ {
+ return obj.asInt();
+ }
+
+ @Override
+ public Object createByte(byte v)
+ {
+ return (int)v;
+ }
+
+ @Override
+ public Object createShort(short v)
+ {
+ return (int)v;
+ }
+
+ @Override
+ public Object createInt(int v)
+ {
+ return (int)v;
+ }
+
+ @Override
+ public Object createLong(long v)
+ {
+ return (int)v;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/LongSchema.java b/java-plan2/src/org/msgpack/schema/LongSchema.java
new file mode 100644
index 0000000..0ba3057
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/LongSchema.java
@@ -0,0 +1,58 @@
+package org.msgpack.schema;
+
+import java.io.IOException;
+import org.msgpack.*;
+
+public class LongSchema extends Schema {
+ public LongSchema()
+ {
+ super("Long");
+ }
+
+ @Override
+ public String getExpression()
+ {
+ return "long";
+ }
+
+ @Override
+ public void pack(Packer pk, Object obj) throws IOException
+ {
+ if(obj == null) {
+ pk.packNil();
+ return;
+ }
+ pk.packLong((Long)obj);
+ }
+
+ @Override
+ public Object convert(GenericObject obj)
+ {
+ return obj.asLong();
+ }
+
+ @Override
+ public Object createByte(byte v)
+ {
+ return (long)v;
+ }
+
+ @Override
+ public Object createShort(short v)
+ {
+ return (long)v;
+ }
+
+ @Override
+ public Object createInt(int v)
+ {
+ return (long)v;
+ }
+
+ @Override
+ public Object createLong(long v)
+ {
+ return (long)v;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/MapSchema.java b/java-plan2/src/org/msgpack/schema/MapSchema.java
new file mode 100644
index 0000000..e72cf63
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/MapSchema.java
@@ -0,0 +1,70 @@
+package org.msgpack.schema;
+
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.io.IOException;
+import org.msgpack.*;
+
+public class MapSchema extends Schema {
+ private Schema keyType;
+ private Schema valueType;
+
+ public MapSchema(Schema keyType, Schema valueType)
+ {
+ super("map");
+ this.keyType = keyType;
+ this.valueType = valueType;
+ }
+
+ public Schema getKeyType()
+ {
+ return keyType;
+ }
+
+ public Schema getValueType()
+ {
+ return valueType;
+ }
+
+ @Override
+ public String getFullName()
+ {
+ return "HashList<"+keyType.getFullName()+", "+valueType.getFullName()+">";
+ }
+
+ @Override
+ public String getExpression()
+ {
+ return "(map "+keyType.getExpression()+" "+valueType.getExpression()+")";
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void pack(Packer pk, Object obj) throws IOException
+ {
+ if(obj == null) {
+ pk.packNil();
+ return;
+ }
+ Map<Object,Object> d = (Map<Object,Object>)obj;
+ pk.packMap(d.size());
+ for(Map.Entry<Object,Object> e : d.entrySet()) {
+ keyType.pack(pk, e.getKey());
+ valueType.pack(pk, e.getValue());
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object convert(GenericObject obj)
+ {
+ Map<GenericObject,GenericObject> d = obj.asMap();
+ Map<Object,Object> g = new HashMap<Object,Object>();
+ for(Map.Entry<GenericObject,GenericObject> e : d.entrySet()) {
+ g.put(keyType.convert(e.getKey()), valueType.convert(e.getValue()));
+ }
+ return g;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/ObjectSchema.java b/java-plan2/src/org/msgpack/schema/ObjectSchema.java
new file mode 100644
index 0000000..a9f30f5
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/ObjectSchema.java
@@ -0,0 +1,33 @@
+package org.msgpack.schema;
+
+import java.io.IOException;
+import org.msgpack.*;
+
+public class ObjectSchema extends Schema {
+ public ObjectSchema()
+ {
+ super("object");
+ }
+
+ public String getFullName()
+ {
+ return "GenericObject";
+ }
+
+ @Override
+ public void pack(Packer pk, Object obj) throws IOException
+ {
+ if(obj == null) {
+ pk.packNil();
+ return;
+ }
+ pk.pack(obj);
+ }
+
+ @Override
+ public Object convert(GenericObject obj)
+ {
+ return obj;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/PrimitiveSchema.java b/java-plan2/src/org/msgpack/schema/PrimitiveSchema.java
new file mode 100644
index 0000000..023d81b
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/PrimitiveSchema.java
@@ -0,0 +1,21 @@
+package org.msgpack.schema;
+
+public abstract class PrimitiveSchema extends Schema {
+ public static enum PrimitiveType {
+ BYTE,
+ SHORT,
+ INT,
+ LONG,
+ FLOAT,
+ DOUBLE,
+ }
+
+ public final PrimitiveType type;
+
+ protected PrimitiveSchema(String name, PrimitiveType type)
+ {
+ super(name);
+ this.type = type;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/RawSchema.java b/java-plan2/src/org/msgpack/schema/RawSchema.java
new file mode 100644
index 0000000..847ad29
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/RawSchema.java
@@ -0,0 +1,44 @@
+package org.msgpack.schema;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import org.msgpack.*;
+
+public class RawSchema extends Schema {
+ public RawSchema()
+ {
+ super("raw");
+ }
+
+ public String getFullName()
+ {
+ return "byte[]";
+ }
+
+ @Override
+ public void pack(Packer pk, Object obj) throws IOException
+ {
+ if(obj == null) {
+ pk.packNil();
+ return;
+ }
+ byte[] d = (byte[])obj;
+ pk.packRaw(d.length);
+ pk.packRawBody(d);
+ }
+
+ @Override
+ public Object convert(GenericObject obj)
+ {
+ return obj.asBytes();
+ }
+
+ @Override
+ public Object createRaw(byte[] b, int offset, int length)
+ {
+ byte[] d = new byte[length];
+ System.arraycopy(b, offset, d, 0, length);
+ return d;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/SSchemaParser.java b/java-plan2/src/org/msgpack/schema/SSchemaParser.java
new file mode 100644
index 0000000..bfe912f
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/SSchemaParser.java
@@ -0,0 +1,246 @@
+package org.msgpack.schema;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+// FIXME exception class
+
+class SSchemaParser {
+ public static Schema parse(String source)
+ {
+ return new SSchemaParser(false).run(source);
+ }
+
+ public static Schema load(String source)
+ {
+ return new SSchemaParser(true).run(source);
+ }
+
+ private static abstract class SExp {
+ boolean isAtom() { return false; }
+ public String getAtom() { return null; }
+
+ boolean isTuple() { return false; }
+ public SExp getTuple(int i) { return null; }
+ public int size() { return 0; }
+ public boolean empty() { return size() == 0; }
+ Iterator<SExp> iterator(int offset) { return null; }
+ }
+
+ private static class SAtom extends SExp {
+ private String atom;
+
+ SAtom(String atom) { this.atom = atom; }
+
+ boolean isAtom() { return true; }
+ public String getAtom() { return atom; }
+
+ public String toString() { return atom; }
+ }
+
+ private static class STuple extends SExp {
+ private List<SExp> tuple;
+
+ STuple() { this.tuple = new ArrayList<SExp>(); }
+
+ public void add(SExp e) { tuple.add(e); }
+
+ boolean isTuple() { return true; }
+ public SExp getTuple(int i) { return tuple.get(i); }
+ public int size() { return tuple.size(); }
+
+ Iterator<SExp> iterator(int skip) {
+ Iterator<SExp> i = tuple.iterator();
+ for(int s=0; s < skip; ++s) { i.next(); }
+ return i;
+ }
+
+ public String toString() {
+ if(tuple.isEmpty()) { return "()"; }
+ Iterator<SExp> i = tuple.iterator();
+ StringBuffer o = new StringBuffer();
+ o.append("(").append(i.next());
+ while(i.hasNext()) { o.append(" ").append(i.next()); }
+ o.append(")");
+ return o.toString();
+ }
+ }
+
+ boolean specificClass;
+
+ private SSchemaParser(boolean specificClass)
+ {
+ this.specificClass = specificClass;
+ }
+
+ private static Pattern pattern = Pattern.compile(
+ "(?:\\s+)|([\\(\\)]|[\\d\\w\\.]+)");
+
+ private Schema run(String source)
+ {
+ Matcher m = pattern.matcher(source);
+
+ Stack<STuple> stack = new Stack<STuple>();
+ String token;
+
+ while(true) {
+ while(true) {
+ if(!m.find()) { throw new RuntimeException("unexpected end of file"); }
+ token = m.group(1);
+ if(token != null) { break; }
+ }
+
+ if(token.equals("(")) {
+ stack.push(new STuple());
+ } else if(token.equals(")")) {
+ STuple top = stack.pop();
+ if(stack.empty()) {
+ stack.push(top);
+ break;
+ }
+ stack.peek().add(top);
+ } else {
+ if(stack.empty()) {
+ throw new RuntimeException("unexpected token '"+token+"'");
+ }
+ stack.peek().add(new SAtom(token));
+ }
+ }
+
+ while(true) {
+ if(!m.find()) { break; }
+ token = m.group(1);
+ if(token != null) { throw new RuntimeException("unexpected token '"+token+"'"); }
+ }
+
+ return readType( stack.pop() );
+ }
+
+ private Schema readType(SExp exp)
+ {
+ if(exp.isAtom()) {
+ String type = exp.getAtom();
+ // FIXME
+ if(type.equals("string")) {
+ return new StringSchema();
+ } else if(type.equals("raw")) {
+ return new RawSchema();
+ } else if(type.equals("short")) {
+ return new ShortSchema();
+ } else if(type.equals("int")) {
+ return new IntSchema();
+ } else if(type.equals("long")) {
+ return new LongSchema();
+ } else if(type.equals("object")) {
+ return new ObjectSchema();
+ } else {
+ throw new RuntimeException("byte, short, int, long, float, double, raw, string or object is expected but got '"+type+"': "+exp);
+ }
+ } else {
+ String type = exp.getTuple(0).getAtom();
+ if(type.equals("class")) {
+ return parseClass(exp);
+ } else if(type.equals("array")) {
+ return parseArray(exp);
+ } else if(type.equals("map")) {
+ return parseMap(exp);
+ } else {
+ throw new RuntimeException("class, array or map is expected but got '"+type+"': "+exp);
+ }
+ }
+ }
+
+ private ClassSchema parseClass(SExp exp)
+ {
+ if(exp.size() < 3 || !exp.getTuple(1).isAtom()) {
+ throw new RuntimeException("class is (class NAME CLASS_BODY): "+exp);
+ }
+
+ String namespace = null;
+ List<String> imports = new ArrayList<String>();
+ String name = exp.getTuple(1).getAtom();
+ List fields = new ArrayList();
+
+ for(Iterator<SExp> i=exp.iterator(2); i.hasNext();) {
+ SExp subexp = i.next();
+ if(!subexp.isTuple() || subexp.empty() || !subexp.getTuple(0).isAtom()) {
+ throw new RuntimeException("field, package or import is expected: "+subexp);
+ }
+ String type = subexp.getTuple(0).getAtom();
+ if(type.equals("field")) {
+ fields.add( parseField(subexp) );
+ } else if(type.equals("package")) {
+ if(namespace != null) {
+ throw new RuntimeException("duplicated package definition: "+subexp);
+ }
+ namespace = parseNamespace(subexp);
+ } else if(type.equals("import")) {
+ imports.add( parseImport(subexp) );
+ } else {
+ throw new RuntimeException("field, package or import is expected but got '"+type+"': "+subexp);
+ }
+ }
+
+ if(specificClass) {
+ return new SpecificClassSchema(name, fields, namespace, imports);
+ } else {
+ return new GenericClassSchema(name, fields, namespace, imports);
+ }
+ }
+
+ private ArraySchema parseArray(SExp exp)
+ {
+ if(exp.size() != 2) {
+ throw new RuntimeException("array is (array ELEMENT_TYPE): "+exp);
+ }
+ Schema elementType = readType(exp.getTuple(1));
+ return new ArraySchema(elementType);
+ }
+
+ private MapSchema parseMap(SExp exp)
+ {
+ if(exp.size() != 3 || !exp.getTuple(1).isAtom()) {
+ throw new RuntimeException("map is (map KEY_TYPE VALUE_TYPE): "+exp);
+ }
+ Schema keyType = readType(exp.getTuple(1));
+ Schema valueType = readType(exp.getTuple(2));
+ return new MapSchema(keyType, valueType);
+ }
+
+ private String parseNamespace(SExp exp)
+ {
+ if(exp.size() != 2 || !exp.getTuple(1).isAtom()) {
+ throw new RuntimeException("package is (package NAME): "+exp);
+ }
+ String name = exp.getTuple(1).getAtom();
+ return name;
+ }
+
+ private String parseImport(SExp exp)
+ {
+ if(exp.size() != 2 || !exp.getTuple(1).isAtom()) {
+ throw new RuntimeException("import is (import NAME): "+exp);
+ }
+ String name = exp.getTuple(1).getAtom();
+ return name;
+ }
+
+ private FieldSchema parseField(SExp exp)
+ {
+ if(exp.size() != 3 || !exp.getTuple(1).isAtom()) {
+ throw new RuntimeException("field is (field NAME TYPE): "+exp);
+ }
+ String name = exp.getTuple(1).getAtom();
+ Schema type = readType(exp.getTuple(2));
+ if(specificClass) {
+ return new SpecificFieldSchema(name, type);
+ } else {
+ return new GenericFieldSchema(name, type);
+ }
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/Schema.java b/java-plan2/src/org/msgpack/schema/Schema.java
new file mode 100644
index 0000000..15b7e72
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/Schema.java
@@ -0,0 +1,93 @@
+package org.msgpack.schema;
+
+import java.io.IOException;
+import org.msgpack.impl.*;
+import org.msgpack.Packer;
+import org.msgpack.GenericObject;
+
+public abstract class Schema {
+ private String expression;
+ private String name;
+
+ public Schema(String name)
+ {
+ this.expression = expression;
+ this.name = name;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String getFullName()
+ {
+ return name;
+ }
+
+ public String getExpression()
+ {
+ return name;
+ }
+
+ public static Schema parse(String source)
+ {
+ return SSchemaParser.parse(source);
+ }
+
+ public static Schema load(String source)
+ {
+ return SSchemaParser.load(source);
+ }
+
+ public abstract void pack(Packer pk, Object obj) throws IOException;
+ public abstract Object convert(GenericObject obj);
+ //public abstract Object convertGeneric(GenericObject obj);
+
+
+ public Object createNil()
+ {
+ return null;
+ }
+
+ public Object createBoolean(boolean v)
+ {
+ throw new RuntimeException("type error");
+ }
+
+ public Object createByte(byte v)
+ {
+ throw new RuntimeException("type error");
+ }
+
+ public Object createShort(short v)
+ {
+ throw new RuntimeException("type error");
+ }
+
+ public Object createInt(int v)
+ {
+ throw new RuntimeException("type error");
+ }
+
+ public Object createLong(long v)
+ {
+ throw new RuntimeException("type error");
+ }
+
+ public Object createFloat(float v)
+ {
+ throw new RuntimeException("type error");
+ }
+
+ public Object createDouble(double v)
+ {
+ throw new RuntimeException("type error");
+ }
+
+ public Object createRaw(byte[] b, int offset, int length)
+ {
+ throw new RuntimeException("type error");
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/ShortSchema.java b/java-plan2/src/org/msgpack/schema/ShortSchema.java
new file mode 100644
index 0000000..aa95f51
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/ShortSchema.java
@@ -0,0 +1,46 @@
+package org.msgpack.schema;
+
+import java.io.IOException;
+import org.msgpack.*;
+
+public class ShortSchema extends Schema {
+ public ShortSchema()
+ {
+ super("Short");
+ }
+
+ @Override
+ public String getExpression()
+ {
+ return "short";
+ }
+
+ @Override
+ public void pack(Packer pk, Object obj) throws IOException
+ {
+ if(obj == null) {
+ pk.packNil();
+ return;
+ }
+ pk.packShort((Short)obj);
+ }
+
+ @Override
+ public Object convert(GenericObject obj)
+ {
+ return obj.asShort();
+ }
+
+ @Override
+ public Object createByte(byte v)
+ {
+ return (int)v;
+ }
+
+ @Override
+ public Object createShort(short v)
+ {
+ return (int)v;
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/SpecificClassSchema.java b/java-plan2/src/org/msgpack/schema/SpecificClassSchema.java
new file mode 100644
index 0000000..75c474a
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/SpecificClassSchema.java
@@ -0,0 +1,156 @@
+package org.msgpack.schema;
+
+import java.util.List;
+import java.util.Map;
+import java.io.IOException;
+import java.util.Iterator;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import org.msgpack.*;
+
+public class SpecificClassSchema extends ClassSchema {
+ private List<SpecificFieldSchema> fields;
+ private String namespace;
+ private List<String> imports;
+
+ private String fqdn;
+ private Constructor constructorCache;
+
+ public SpecificClassSchema(String name, List<SpecificFieldSchema> fields, String namespace, List<String> imports)
+ {
+ super(name, namespace, imports);
+ this.fields = fields;
+ if(namespace == null) {
+ this.fqdn = name;
+ } else {
+ this.fqdn = namespace+"."+name;
+ }
+ }
+
+ //@Override
+ //public String getFullName()
+ //{
+ // if(namespace == null) {
+ // return getName();
+ // } else {
+ // return namespace+"."+getName();
+ // }
+ //}
+
+ public List<? extends FieldSchema> getFields()
+ {
+ return fields;
+ }
+
+ @Override
+ public String getExpression()
+ {
+ StringBuffer b = new StringBuffer();
+ b.append("(class ");
+ b.append(getName());
+ if(namespace != null) {
+ b.append(" (package "+namespace+")");
+ }
+ for(SpecificFieldSchema f : fields) {
+ b.append(" "+f.getExpression());
+ }
+ b.append(")");
+ return b.toString();
+ }
+
+ @Override
+ public void pack(Packer pk, Object obj) throws IOException
+ {
+ if(obj == null) {
+ pk.packNil();
+ return;
+ }
+
+ if(constructorCache == null) {
+ cacheConstructor();
+ }
+
+ pk.packArray(fields.size());
+ for(SpecificFieldSchema f : fields) {
+ f.getType().pack(pk, f.getFieldValue(obj));
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object convert(GenericObject obj)
+ {
+ if(constructorCache == null) {
+ cacheConstructor();
+ }
+
+ List<GenericObject> d = obj.asArray();
+
+ try {
+ Object g = constructorCache.newInstance((Object[])null);
+
+ Iterator<GenericObject> vi = d.iterator();
+ Iterator<SpecificFieldSchema> fi = fields.iterator();
+ while(fi.hasNext() && vi.hasNext()) {
+ SpecificFieldSchema f = fi.next();
+ GenericObject v = vi.next();
+ f.setFieldValue(g, f.getType().convert(v));
+ }
+ // leave it as uninitialized
+ //while(fi.hasNext()) {
+ // SpecificFieldSchema f = fi.next();
+ // g.put(f.getName(), null);
+ //}
+
+ return g;
+
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException("can't instantiate "+fqdn+": "+e.getMessage());
+ } catch (InstantiationException e) {
+ throw new RuntimeException("can't instantiate "+fqdn+": "+e.getMessage());
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("can't instantiate "+fqdn+": "+e.getMessage());
+ }
+ }
+
+ private void cacheConstructor()
+ {
+ try {
+ Class c = Class.forName(fqdn);
+ int index = 0;
+ for(SpecificFieldSchema f : fields) {
+ f.cacheField(c, index++);
+ }
+ constructorCache = c.getDeclaredConstructor((Class[])null);
+ constructorCache.setAccessible(true);
+ } catch(ClassNotFoundException e) {
+ throw new RuntimeException("class not found: "+fqdn);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("class not found: "+fqdn+": "+e.getMessage());
+ }
+ }
+
+ @Override
+ public Object newInstance()
+ {
+ if(constructorCache == null) {
+ cacheConstructor();
+ }
+ try {
+ return constructorCache.newInstance((Object[])null);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException("can't instantiate "+fqdn+": "+e.getMessage());
+ } catch (InstantiationException e) {
+ throw new RuntimeException("can't instantiate "+fqdn+": "+e.getMessage());
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("can't instantiate "+fqdn+": "+e.getMessage());
+ }
+ }
+
+ public boolean equals(SpecificClassSchema o)
+ {
+ return (namespace != null ? namespace.equals(o.getNamespace()) : o.getNamespace() == null) &&
+ getName().equals(o.getName());
+ }
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/SpecificFieldSchema.java b/java-plan2/src/org/msgpack/schema/SpecificFieldSchema.java
new file mode 100644
index 0000000..297df27
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/SpecificFieldSchema.java
@@ -0,0 +1,78 @@
+package org.msgpack.schema;
+
+import java.util.Map;
+import java.util.Arrays;
+import java.lang.reflect.Field;
+import org.msgpack.*;
+
+public class SpecificFieldSchema extends FieldSchema {
+ public Field fieldCache;
+ private int index;
+
+ public SpecificFieldSchema(String name, Schema type)
+ {
+ super(name, type);
+ this.index = -1;
+ }
+
+ @Override
+ public Object getFieldValue(Object obj)
+ {
+ if(index >= 0) {
+ return ((MessageMergeable)obj).getField(index);
+ }
+
+ try {
+ return fieldCache.get(obj);
+ } catch(IllegalArgumentException e) {
+ throw new RuntimeException("can't get value from '"+getName()+"' field of '"+obj.getClass().getName()+"' class: "+e.getMessage());
+ } catch(IllegalAccessException e) {
+ throw new RuntimeException("can't get value from '"+getName()+"' field of '"+obj.getClass().getName()+"' class: "+e.getMessage());
+ }
+ }
+
+ @Override
+ public void setFieldValue(Object obj, Object value)
+ {
+ if(index >= 0) {
+ ((MessageMergeable)obj).setField(index, value);
+ return;
+ }
+
+ try {
+ fieldCache.set(obj, value);
+ } catch(IllegalArgumentException e) {
+ throw new RuntimeException("can't set value into '"+getName()+"' field of '"+obj.getClass().getName()+"' class: "+e.getMessage());
+ } catch(IllegalAccessException e) {
+ throw new RuntimeException("can't set value into '"+getName()+"' field of '"+obj.getClass().getName()+"' class: "+e.getMessage());
+ }
+ }
+
+ void cacheField(Class c, int index)
+ {
+ for(Class i : c.getInterfaces()) {
+ if(i.equals(MessageMergeable.class)) {
+ this.index = index;
+ return;
+ }
+ }
+
+ try {
+ fieldCache = c.getDeclaredField(getName());
+ if(!fieldCache.isAccessible()) {
+ fieldCache.setAccessible(true);
+ }
+ } catch(NoSuchFieldException e) {
+ throw new RuntimeException("can't get '"+getName()+"' field of '"+c.getName()+"' class: "+e.getMessage());
+ } catch(SecurityException e) {
+ throw new RuntimeException("can't get '"+getName()+"' field of '"+c.getName()+"' class: "+e.getMessage());
+ }
+ }
+
+ //public void setFieldInt(Object obj, int value)
+ //{
+ // if(type instanceof PrimitiveSchema) {
+ // }
+ //}
+}
+
diff --git a/java-plan2/src/org/msgpack/schema/StringSchema.java b/java-plan2/src/org/msgpack/schema/StringSchema.java
new file mode 100644
index 0000000..fc6855b
--- /dev/null
+++ b/java-plan2/src/org/msgpack/schema/StringSchema.java
@@ -0,0 +1,48 @@
+package org.msgpack.schema;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import org.msgpack.*;
+
+public class StringSchema extends Schema {
+ public StringSchema()
+ {
+ super("string");
+ }
+
+ public String getFullName()
+ {
+ return "String";
+ }
+
+ @Override
+ public void pack(Packer pk, Object obj) throws IOException
+ {
+ if(obj == null) {
+ pk.packNil();
+ return;
+ }
+ String s = (String)obj;
+ byte[] d = s.getBytes("UTF-8");
+ pk.packRaw(d.length);
+ pk.packRawBody(d);
+ }
+
+ @Override
+ public Object convert(GenericObject obj)
+ {
+ return obj.asString();
+ }
+
+ @Override
+ public Object createRaw(byte[] b, int offset, int length)
+ {
+ try {
+ return new String(b, offset, length, "UTF-8"); // XXX FIXME debug
+ } catch (Exception e) {
+ // FIXME
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+}
+