import MessagePack for Java implementation plan 2

This commit is contained in:
frsyuki 2009-11-12 01:10:36 +09:00
parent 93a95725fc
commit e39e1d4f60
48 changed files with 3676 additions and 0 deletions

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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");
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,7 @@
package org.msgpack;
public interface MessageConvertable
{
public void messageConvert(GenericObject obj);
}

View file

@ -0,0 +1,7 @@
package org.msgpack;
public interface MessageMergeable {
public void setField(int index, Object value);
public Object getField(int index);
}

View file

@ -0,0 +1,10 @@
package org.msgpack;
public class MessagePackException extends RuntimeException
{
public MessagePackException(String message)
{
super(message);
}
}

View file

@ -0,0 +1,9 @@
package org.msgpack;
import java.io.IOException;
public interface MessagePackable
{
public void messagePack(Packer pk) throws IOException;
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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;
}
*/
}

View file

@ -0,0 +1,7 @@
package org.msgpack.impl;
public interface ArrayBuilder {
public void add(Object element);
public Object finish();
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -0,0 +1,8 @@
package org.msgpack.impl;
public interface MapBuilder {
public void putKey(Object key);
public void putValue(Object value);
public Object finish();
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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());
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}
}

View file

@ -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");
}
}

View file

@ -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;
}
}

View file

@ -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());
}
}

View file

@ -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) {
// }
//}
}

View file

@ -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());
}
}
}