java: javadoc

This commit is contained in:
frsyuki 2010-05-20 17:32:15 +09:00
parent c2525bcc05
commit ec8c19b1f0
2 changed files with 276 additions and 23 deletions

View File

@ -367,7 +367,7 @@ abstract class BufferedUnpackerImpl extends UnpackerImpl {
} }
} }
final byte[] unpackRawBody(int length) throws IOException, MessageTypeException { final byte[] unpackRawBody(int length) throws IOException {
more(length); more(length);
byte[] bytes = new byte[length]; byte[] bytes = new byte[length];
System.arraycopy(buffer, offset, bytes, 0, length); System.arraycopy(buffer, offset, bytes, 0, length);
@ -388,12 +388,12 @@ abstract class BufferedUnpackerImpl extends UnpackerImpl {
return s; return s;
} }
final Object unpackObject() throws IOException, MessageTypeException { final Object unpackObject() throws IOException {
// FIXME save state, restore state // FIXME save state, restore state
UnpackResult result = new UnpackResult(); UnpackResult result = new UnpackResult();
if(!next(result)) { if(!next(result)) {
super.reset(); super.reset();
throw new MessageTypeException(); throw new UnpackException("insufficient buffer");
} }
return result.getData(); return result.getData();
} }

View File

@ -23,6 +23,82 @@ import java.io.IOException;
import java.util.Iterator; import java.util.Iterator;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/**
* Deserializer class that includes Buffered API, Unbuffered API,
* Schema API and Direct Conversion API.
*
* Buffered API uses the internal buffer of the Unpacker.
* Following code uses Buffered API with an input stream:
* <pre>
* // create an unpacker with input stream
* Unpacker pac = new Unpacker(System.in);
*
* // take a object out using next() method, or ...
* UnpackResult result = pac.next();
*
* // use an iterator.
* for(Object obj : pac) {
* // use MessageConvertable interface to convert the
* // the generic object to the specific type.
* }
* </pre>
*
* Following code doesn't use the input stream and feeds buffer
* using {@link feed(byte[])} method. This is useful to use
* special stream like zlib or event-driven I/O library.
* <pre>
* // create an unpacker without input stream
* Unpacker pac = new Unpacker();
*
* // feed buffer to the internal buffer.
* pac.feed(input_bytes);
*
* // use next() method or iterators.
* for(Object obj : pac) {
* // ...
* }
* </pre>
*
* The combination of {@link reserveBuffer()}, {@link getBuffer()},
* {@link getBufferOffset()}, {@link getBufferCapacity()} and
* {@link bufferConsumed()} is useful to omit copying.
* <pre>
* // create an unpacker without input stream
* Unpacker pac = new Unpacker();
*
* // reserve internal buffer at least 1024 bytes.
* pac.reserveBuffer(1024);
*
* // feed buffer to the internal buffer upto pac.getBufferCapacity() bytes.
* System.in.read(pac.getBuffer(), pac.getBufferOffset(), pac.getBufferCapacity());
*
* // use next() method or iterators.
* for(Object obj : pac) {
* // ...
* }
* </pre>
*
* Unbuffered API doesn't initialize the internal buffer.
* You can manage the buffer manually.
* <pre>
* // create an unpacker with input stream
* Unpacker pac = new Unpacker(System.in);
*
* // manage the buffer manually.
* byte[] buffer = new byte[1024];
* int filled = System.in.read(buffer);
* int offset = 0;
*
* // deserialize objects using execute() method.
* int nextOffset = pac.execute(buffer, offset, filled);
*
* // take out object if deserialized object is ready.
* if(pac.isFinished()) {
* Object obj = pac.getData();
* // ...
* }
* </pre>
*/
public class Unpacker implements Iterable<Object> { public class Unpacker implements Iterable<Object> {
// buffer: // buffer:
@ -59,38 +135,86 @@ public class Unpacker implements Iterable<Object> {
final BufferedUnpackerMixin impl = new BufferedUnpackerMixin(); final BufferedUnpackerMixin impl = new BufferedUnpackerMixin();
/**
* Calls {@link Unpacker(DEFAULT_BUFFER_SIZE)}
*/
public Unpacker() { public Unpacker() {
this(DEFAULT_BUFFER_SIZE); this(DEFAULT_BUFFER_SIZE);
} }
/**
* Calls {@link Unpacker(null, bufferReserveSize)}
*/
public Unpacker(int bufferReserveSize) { public Unpacker(int bufferReserveSize) {
this(null, bufferReserveSize); this(null, bufferReserveSize);
} }
/**
* Calls {@link Unpacker(stream, DEFAULT_BUFFER_SIZE)}
*/
public Unpacker(InputStream stream) { public Unpacker(InputStream stream) {
this(stream, DEFAULT_BUFFER_SIZE); this(stream, DEFAULT_BUFFER_SIZE);
} }
/**
* Constructs the unpacker.
* The stream is used to fill the buffer when more buffer is required by {@link next()} or {@link UnpackIterator#hasNext()} method.
* @param stream input stream to fill the buffer
* @param bufferReserveSize threshold size to expand the size of buffer
*/
public Unpacker(InputStream stream, int bufferReserveSize) { public Unpacker(InputStream stream, int bufferReserveSize) {
this.parsed = 0; this.parsed = 0;
this.bufferReserveSize = bufferReserveSize/2; this.bufferReserveSize = bufferReserveSize/2;
this.stream = stream; this.stream = stream;
} }
/**
* Sets schema to convert deserialized object into specific type.
* Default schema is {@link GenericSchema} that leaves objects for generic type. Use {@link MessageConvertable#messageConvert(Object)} method to convert the generic object.
* @param s schem to use
*/
public Unpacker useSchema(Schema s) { public Unpacker useSchema(Schema s) {
impl.setSchema(s); impl.setSchema(s);
return this; return this;
} }
/**
* Gets the input stream.
* @return the input stream. it may be null.
*/
public InputStream getStream() { public InputStream getStream() {
return this.stream; return this.stream;
} }
/**
* Sets the input stream.
* @param stream the input stream to set.
*/
public void setStream(InputStream stream) { public void setStream(InputStream stream) {
this.stream = stream; this.stream = stream;
} }
/**
* Fills the buffer with the specified buffer.
*/
public void feed(byte[] buffer) {
feed(buffer, 0, buffer.length);
}
/**
* Fills the buffer with the specified buffer.
*/
public void feed(byte[] buffer, int offset, int length) {
reserveBuffer(length);
System.arraycopy(buffer, offset, impl.buffer, impl.offset, length);
bufferConsumed(length);
}
/**
* Fills the buffer with the specified buffer.
*/
public void feed(ByteBuffer buffer) { public void feed(ByteBuffer buffer) {
int length = buffer.remaining(); int length = buffer.remaining();
if (length == 0) return; if (length == 0) return;
@ -99,45 +223,62 @@ public class Unpacker implements Iterable<Object> {
bufferConsumed(length); bufferConsumed(length);
} }
public void feed(byte[] buffer) { /**
feed(buffer, 0, buffer.length); * Swaps the internal buffer with the specified buffer.
} * This method doesn't copy the buffer and the its contents will be rewritten by {@link fill()} or {@link feed(byte[])} method.
*/
public void feed(byte[] buffer, int offset, int length) {
reserveBuffer(length);
System.arraycopy(buffer, offset, impl.buffer, impl.offset, length);
bufferConsumed(length);
}
public void wrap(byte[] buffer) { public void wrap(byte[] buffer) {
wrap(buffer, 0, buffer.length); wrap(buffer, 0, buffer.length);
} }
/**
* Swaps the internal buffer with the specified buffer.
* This method doesn't copy the buffer and the its contents will be rewritten by {@link fill()} or {@link feed(byte[])} method.
*/
public void wrap(byte[] buffer, int offset, int length) { public void wrap(byte[] buffer, int offset, int length) {
impl.buffer = buffer; impl.buffer = buffer;
impl.offset = offset; impl.offset = offset;
impl.filled = length; impl.filled = length;
} }
/**
* Fills the internal using the input stream.
* @return false if the stream is null or stream.read returns <= 0.
*/
public boolean fill() throws IOException { public boolean fill() throws IOException {
return impl.fill(); return impl.fill();
} }
/**
* Returns the iterator that calls {@link next()} method repeatedly.
*/
public Iterator<Object> iterator() { public Iterator<Object> iterator() {
return new UnpackIterator(this); return new UnpackIterator(this);
} }
/**
* Deserializes one object and returns it.
* @return {@link UnpackResult#isFinished()} returns false if the buffer is insufficient to deserialize one object.
*/
public UnpackResult next() throws IOException, UnpackException { public UnpackResult next() throws IOException, UnpackException {
UnpackResult result = new UnpackResult(); UnpackResult result = new UnpackResult();
impl.next(result); impl.next(result);
return result; return result;
} }
/**
* Deserializes one object and returns it.
* @return false if the buffer is insufficient to deserialize one object.
*/
public boolean next(UnpackResult result) throws IOException, UnpackException { public boolean next(UnpackResult result) throws IOException, UnpackException {
return impl.next(result); return impl.next(result);
} }
/**
* Reserve free space of the internal buffer at least specified size and expands {@link getBufferCapacity()}.
*/
public void reserveBuffer(int require) { public void reserveBuffer(int require) {
if(impl.buffer == null) { if(impl.buffer == null) {
int nextSize = (bufferReserveSize < require) ? require : bufferReserveSize; int nextSize = (bufferReserveSize < require) ? require : bufferReserveSize;
@ -163,22 +304,40 @@ public class Unpacker implements Iterable<Object> {
impl.offset = 0; impl.offset = 0;
} }
/**
* Returns the internal buffer.
*/
public byte[] getBuffer() { public byte[] getBuffer() {
return impl.buffer; return impl.buffer;
} }
public int getBufferOffset() { /**
return impl.filled; * Returns the size of free space of the internal buffer.
} */
public int getBufferCapacity() { public int getBufferCapacity() {
return impl.buffer.length - impl.filled; return impl.buffer.length - impl.filled;
} }
/**
* Returns the offset of free space in the internal buffer.
*/
public int getBufferOffset() {
return impl.filled;
}
/**
* Moves front the offset of the free space in the internal buffer.
* Call this method after fill the buffer manually using {@link reserveBuffer()}, {@link getBuffer()}, {@link getBufferOffset()} and {@link getBufferCapacity()} methods.
*/
public void bufferConsumed(int size) { public void bufferConsumed(int size) {
impl.filled += size; impl.filled += size;
} }
/**
* Deserializes one object upto the offset of the internal buffer.
* Call {@link reset()} method before calling this method again.
* @return true if one object is deserialized. Use {@link getData()} to get the deserialized object.
*/
public boolean execute() throws UnpackException { public boolean execute() throws UnpackException {
int noffset = impl.execute(impl.buffer, impl.offset, impl.filled); int noffset = impl.execute(impl.buffer, impl.offset, impl.filled);
if(noffset <= impl.offset) { if(noffset <= impl.offset) {
@ -190,10 +349,24 @@ public class Unpacker implements Iterable<Object> {
} }
/**
* Deserializes one object over the specified buffer.
* This method doesn't use the internal buffer.
* Use {@link isFinished()} method to known a object is ready to get.
* Call {@link reset()} method before calling this method again.
* @return offset position that is parsed.
*/
public int execute(byte[] buffer) throws UnpackException { public int execute(byte[] buffer) throws UnpackException {
return execute(buffer, 0, buffer.length); return execute(buffer, 0, buffer.length);
} }
/**
* Deserializes one object over the specified buffer.
* This method doesn't use the internal buffer.
* Use {@link isFinished()} method to known a object is ready to get.
* Call {@link reset()} method before calling this method again.
* @return offset position that is parsed.
*/
public int execute(byte[] buffer, int offset, int length) throws UnpackException { public int execute(byte[] buffer, int offset, int length) throws UnpackException {
int noffset = impl.execute(buffer, offset + impl.offset, length); int noffset = impl.execute(buffer, offset + impl.offset, length);
impl.offset = noffset - offset; impl.offset = noffset - offset;
@ -203,14 +376,23 @@ public class Unpacker implements Iterable<Object> {
return noffset; return noffset;
} }
public boolean isFinished() { /**
return impl.isFinished(); * Gets the object deserialized by {@link execute(byte[])} method.
} */
public Object getData() { public Object getData() {
return impl.getData(); return impl.getData();
} }
/**
* Returns true if an object is ready to get with {@link getData()} method.
*/
public boolean isFinished() {
return impl.isFinished();
}
/**
* Resets the internal state of the unpacker.
*/
public void reset() { public void reset() {
impl.reset(); impl.reset();
} }
@ -236,59 +418,130 @@ public class Unpacker implements Iterable<Object> {
} }
/**
* Gets one {@code byte} value from the buffer.
* This method calls {@link fill()} method if needed.
* @throws MessageTypeException the first value of the buffer is not a {@code byte}.
*/
public byte unpackByte() throws IOException, MessageTypeException { public byte unpackByte() throws IOException, MessageTypeException {
return impl.unpackByte(); return impl.unpackByte();
} }
/**
* Gets one {@code short} value from the buffer.
* This method calls {@link fill()} method if needed.
* @throws MessageTypeException the first value of the buffer is not a {@code short}.
*/
public short unpackShort() throws IOException, MessageTypeException { public short unpackShort() throws IOException, MessageTypeException {
return impl.unpackShort(); return impl.unpackShort();
} }
/**
* Gets one {@code int} value from the buffer.
* This method calls {@link fill()} method if needed.
* @throws MessageTypeException the first value of the buffer is not a {@code int}.
*/
public int unpackInt() throws IOException, MessageTypeException { public int unpackInt() throws IOException, MessageTypeException {
return impl.unpackInt(); return impl.unpackInt();
} }
/**
* Gets one {@code long} value from the buffer.
* This method calls {@link fill()} method if needed.
* @throws MessageTypeException the first value of the buffer is not a {@code long}.
*/
public long unpackLong() throws IOException, MessageTypeException { public long unpackLong() throws IOException, MessageTypeException {
return impl.unpackLong(); return impl.unpackLong();
} }
/**
* Gets one {@code float} value from the buffer.
* This method calls {@link fill()} method if needed.
* @throws MessageTypeException the first value of the buffer is not a {@code float}.
*/
public float unpackFloat() throws IOException, MessageTypeException { public float unpackFloat() throws IOException, MessageTypeException {
return impl.unpackFloat(); return impl.unpackFloat();
} }
/**
* Gets one {@code double} value from the buffer.
* This method calls {@link fill()} method if needed.
* @throws MessageTypeException the first value of the buffer is not a {@code double}.
*/
public double unpackDouble() throws IOException, MessageTypeException { public double unpackDouble() throws IOException, MessageTypeException {
return impl.unpackDouble(); return impl.unpackDouble();
} }
/**
* Gets one {@code null} value from the buffer.
* This method calls {@link fill()} method if needed.
* @throws MessageTypeException the first value of the buffer is not a {@code null}.
*/
public Object unpackNull() throws IOException, MessageTypeException { public Object unpackNull() throws IOException, MessageTypeException {
return impl.unpackNull(); return impl.unpackNull();
} }
/**
* Gets one {@code boolean} value from the buffer.
* This method calls {@link fill()} method if needed.
* @throws MessageTypeException the first value of the buffer is not a {@code boolean}.
*/
public boolean unpackBoolean() throws IOException, MessageTypeException { public boolean unpackBoolean() throws IOException, MessageTypeException {
return impl.unpackBoolean(); return impl.unpackBoolean();
} }
/**
* Gets one array header from the buffer.
* This method calls {@link fill()} method if needed.
* @return the length of the map. There are {@code retval} objects to get.
* @throws MessageTypeException the first value of the buffer is not a array.
*/
public int unpackArray() throws IOException, MessageTypeException { public int unpackArray() throws IOException, MessageTypeException {
return impl.unpackArray(); return impl.unpackArray();
} }
/**
* Gets one map header from the buffer.
* This method calls {@link fill()} method if needed.
* @return the length of the map. There are {@code retval * 2} objects to get.
* @throws MessageTypeException the first value of the buffer is not a map.
*/
public int unpackMap() throws IOException, MessageTypeException { public int unpackMap() throws IOException, MessageTypeException {
return impl.unpackMap(); return impl.unpackMap();
} }
/**
* Gets one raw header from the buffer.
* This method calls {@link fill()} method if needed.
* @return the length of the raw bytes. There are {@code retval} bytes to get.
* @throws MessageTypeException the first value of the buffer is not a raw bytes.
*/
public int unpackRaw() throws IOException, MessageTypeException { public int unpackRaw() throws IOException, MessageTypeException {
return impl.unpackRaw(); return impl.unpackRaw();
} }
public byte[] unpackRawBody(int length) throws IOException, MessageTypeException { /**
* Gets one raw header from the buffer.
* This method calls {@link fill()} method if needed.
*/
public byte[] unpackRawBody(int length) throws IOException {
return impl.unpackRawBody(length); return impl.unpackRawBody(length);
} }
/**
* Gets one {@code String} value from the buffer.
* This method calls {@link fill()} method if needed.
* @throws MessageTypeException the first value of the buffer is not a {@code String}.
*/
final public String unpackString() throws IOException, MessageTypeException { final public String unpackString() throws IOException, MessageTypeException {
return impl.unpackString(); return impl.unpackString();
} }
final public Object unpackObject() throws IOException, MessageTypeException { /**
* Gets one {@code Object} value from the buffer.
* This method calls {@link fill()} method if needed.
*/
final public Object unpackObject() throws IOException {
return impl.unpackObject(); return impl.unpackObject();
} }
} }