diff options
| author | frsyuki <frsyuki@users.sourceforge.jp> | 2009-11-12 01:10:36 +0900 |
|---|---|---|
| committer | frsyuki <frsyuki@users.sourceforge.jp> | 2009-11-12 01:10:36 +0900 |
| commit | e39e1d4f602b0202b830f8e672e2116bdb8b9f34 (patch) | |
| tree | 30ff5419760ae2c2550ff759ae8e345b204cb435 /java-plan2/src | |
| parent | 93a95725fc45d7d0047578ecdc5b2ae4e900970f (diff) | |
| download | msgpack-python-e39e1d4f602b0202b830f8e672e2116bdb8b9f34.tar.gz | |
import MessagePack for Java implementation plan 2
Diffstat (limited to 'java-plan2/src')
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()); + } + } +} + |
