http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackInputStream.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackInputStream.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackInputStream.java new file mode 100644 index 0000000..918af13 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackInputStream.java @@ -0,0 +1,482 @@ +/*************************************************************************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + ***************************************************************************************************************************/ +package org.apache.juneau.msgpack; + +import static org.apache.juneau.msgpack.DataType.*; + +import java.io.*; +import org.apache.juneau.internal.*; + +/** + * Specialized input stream for parsing MessagePack streams. + * <p> + * <b>Note: This class is not intended for external use.</b> + * + * @author James Bognar ([email protected]) + */ +public final class MsgPackInputStream extends InputStream { + + private final InputStream is; + private DataType currentDataType; + private long length; + private int lastByte; + private int extType; + int pos = 0; + + // Data type quick-lookup table. + private static final DataType[] TYPES = new DataType[] { + /*0x0?*/ INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT, + /*0x1?*/ INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT, + /*0x2?*/ INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT, + /*0x3?*/ INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT, + /*0x4?*/ INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT, + /*0x5?*/ INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT, + /*0x6?*/ INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT, + /*0x7?*/ INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT, + /*0x8?*/ MAP,MAP,MAP,MAP,MAP,MAP,MAP,MAP,MAP,MAP,MAP,MAP,MAP,MAP,MAP,MAP, + /*0x9?*/ ARRAY,ARRAY,ARRAY,ARRAY,ARRAY,ARRAY,ARRAY,ARRAY,ARRAY,ARRAY,ARRAY,ARRAY,ARRAY,ARRAY,ARRAY,ARRAY, + /*0xA?*/ STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING, + /*0xB?*/ STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING,STRING, + /*0xC?*/ NULL, INVALID, BOOLEAN, BOOLEAN, BIN, BIN, BIN, EXT, EXT, EXT, FLOAT, DOUBLE, INT, INT, LONG, LONG, + /*0xD?*/ INT, INT, INT, LONG, EXT, EXT, EXT, EXT, EXT, STRING, STRING, STRING, ARRAY, ARRAY, MAP, MAP, + /*0xE?*/ INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT, + /*0xF?*/ INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT,INT + }; + + /** + * Constructor. + * @param is The input stream being wrapped. + */ + protected MsgPackInputStream(InputStream is) { + this.is = is; + } + + @Override /* InputStream */ + public int read() throws IOException { + int i = is.read(); + if (i > 0) + pos++; + return i; + } + + /** + * Reads the data type flag from the stream. + * This is the byte that indicates what kind of data follows. + */ + DataType readDataType() throws IOException { + int i = read(); + if (i == -1) + throw new IOException("Unexpected end of file found at position " + pos); + currentDataType = TYPES[i]; + switch (currentDataType) { + case NULL: + case FLOAT: { + length = 4; + break; + } + case DOUBLE: { + length = 8; + break; + } + case BOOLEAN: { + lastByte = i; + break; + } + case INT: { + // positive fixnum stores 7-bit positive integer + // +--------+ + // |0XXXXXXX| + // +--------+ + // + // negative fixnum stores 5-bit negative integer + // +--------+ + // |111YYYYY| + // +--------+ + // + // * 0XXXXXXX is 8-bit unsigned integer + // * 111YYYYY is 8-bit signed integer + // + // uint 8 stores a 8-bit unsigned integer + // +--------+--------+ + // | 0xcc |ZZZZZZZZ| + // +--------+--------+ + // + // uint 16 stores a 16-bit big-endian unsigned integer + // +--------+--------+--------+ + // | 0xcd |ZZZZZZZZ|ZZZZZZZZ| + // +--------+--------+--------+ + // + // uint 32 stores a 32-bit big-endian unsigned integer + // +--------+--------+--------+--------+--------+ + // | 0xce |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| + // +--------+--------+--------+--------+--------+ + // + // uint 64 stores a 64-bit big-endian unsigned integer + // +--------+--------+--------+--------+--------+--------+--------+--------+--------+ + // | 0xcf |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| + // +--------+--------+--------+--------+--------+--------+--------+--------+--------+ + // + // int 8 stores a 8-bit signed integer + // +--------+--------+ + // | 0xd0 |ZZZZZZZZ| + // +--------+--------+ + // + // int 16 stores a 16-bit big-endian signed integer + // +--------+--------+--------+ + // | 0xd1 |ZZZZZZZZ|ZZZZZZZZ| + // +--------+--------+--------+ + // + // int 32 stores a 32-bit big-endian signed integer + // +--------+--------+--------+--------+--------+ + // | 0xd2 |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| + // +--------+--------+--------+--------+--------+ + // + // int 64 stores a 64-bit big-endian signed integer + // +--------+--------+--------+--------+--------+--------+--------+--------+--------+ + // | 0xd3 |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| + // +--------+--------+--------+--------+--------+--------+--------+--------+--------+ + lastByte = i; + if (i <= POSFIXINT_U) + length = 0; + else if (i >= NEGFIXINT_L) + length = -1; + else if (i == INT8 || i == UINT8) + length = 1; + else if (i == INT16 || i == UINT16) + length = 2; + else if (i == INT32) + length = 4; + else + length = 0; + break; + } + case LONG: { + if (i == UINT32) + length = 4; + else if (i == INT64 || i == UINT64) + length = 8; + else + length = 0; + break; + } + case STRING:{ + // fixstr stores a byte array whose length is upto 31 bytes: + // +--------+========+ + // |101XXXXX| data | + // +--------+========+ + // + // str 8 stores a byte array whose length is upto (2^8)-1 bytes: + // +--------+--------+========+ + // | 0xd9 |YYYYYYYY| data | + // +--------+--------+========+ + // + // str 16 stores a byte array whose length is upto (2^16)-1 bytes: + // +--------+--------+--------+========+ + // | 0xda |ZZZZZZZZ|ZZZZZZZZ| data | + // +--------+--------+--------+========+ + // + // str 32 stores a byte array whose length is upto (2^32)-1 bytes: + // +--------+--------+--------+--------+--------+========+ + // | 0xdb |AAAAAAAA|AAAAAAAA|AAAAAAAA|AAAAAAAA| data | + // +--------+--------+--------+--------+--------+========+ + // + // where + // * XXXXX is a 5-bit unsigned integer which represents N + // * YYYYYYYY is a 8-bit unsigned integer which represents N + // * ZZZZZZZZ_ZZZZZZZZ is a 16-bit big-endian unsigned integer which represents N + // * AAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA is a 32-bit big-endian unsigned integer which represents N + // * N is the length of data + if (i <= FIXSTR_U) + length = i & 0x1F; + else if (i == STR8) + length = readUInt1(); + else if (i == STR16) + length = readUInt2(); + else + length = readUInt4(); + break; + } + case ARRAY: { + // fixarray stores an array whose length is upto 15 elements: + // +--------+~~~~~~~~~~~~~~~~~+ + // |1001XXXX| N objects | + // +--------+~~~~~~~~~~~~~~~~~+ + // + // array 16 stores an array whose length is upto (2^16)-1 elements: + // +--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // | 0xdc |YYYYYYYY|YYYYYYYY| N objects | + // +--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // + // array 32 stores an array whose length is upto (2^32)-1 elements: + // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // | 0xdd |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| N objects | + // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // + // where + // * XXXX is a 4-bit unsigned integer which represents N + // * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N + // * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a 32-bit big-endian unsigned integer which represents N + // N is the size of a array + if (i <= FIXARRAY_U) + length = i & 0x0F; + else if (i == ARRAY16) + length = readUInt2(); + else + length = readUInt4(); + break; + } + case BIN:{ + // bin 8 stores a byte array whose length is upto (2^8)-1 bytes: + // +--------+--------+========+ + // | 0xc4 |XXXXXXXX| data | + // +--------+--------+========+ + // + // bin 16 stores a byte array whose length is upto (2^16)-1 bytes: + // +--------+--------+--------+========+ + // | 0xc5 |YYYYYYYY|YYYYYYYY| data | + // +--------+--------+--------+========+ + // + // bin 32 stores a byte array whose length is upto (2^32)-1 bytes: + // +--------+--------+--------+--------+--------+========+ + // | 0xc6 |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| data | + // +--------+--------+--------+--------+--------+========+ + // + // where + // * XXXXXXXX is a 8-bit unsigned integer which represents N + // * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N + // * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a 32-bit big-endian unsigned integer which represents N + // * N is the length of data + if (i == BIN8) + length = readUInt1(); + else if (i == BIN16) + length = readUInt2(); + else + length = readUInt4(); + break; + } + case EXT:{ + // fixext 1 stores an integer and a byte array whose length is 1 byte + // +--------+--------+--------+ + // | 0xd4 | type | data | + // +--------+--------+--------+ + // + // fixext 2 stores an integer and a byte array whose length is 2 bytes + // +--------+--------+--------+--------+ + // | 0xd5 | type | data | + // +--------+--------+--------+--------+ + // + // fixext 4 stores an integer and a byte array whose length is 4 bytes + // +--------+--------+--------+--------+--------+--------+ + // | 0xd6 | type | data | + // +--------+--------+--------+--------+--------+--------+ + // + // fixext 8 stores an integer and a byte array whose length is 8 bytes + // +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+ + // | 0xd7 | type | data | + // +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+ + // + // fixext 16 stores an integer and a byte array whose length is 16 bytes + // +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+ + // | 0xd8 | type | data + // +--------+--------+--------+--------+--------+--------+--------+--------+--------+--------+ + // +--------+--------+--------+--------+--------+--------+--------+--------+ + // data (cont.) | + // +--------+--------+--------+--------+--------+--------+--------+--------+ + // + // ext 8 stores an integer and a byte array whose length is upto (2^8)-1 bytes: + // +--------+--------+--------+========+ + // | 0xc7 |XXXXXXXX| type | data | + // +--------+--------+--------+========+ + // + // ext 16 stores an integer and a byte array whose length is upto (2^16)-1 bytes: + // +--------+--------+--------+--------+========+ + // | 0xc8 |YYYYYYYY|YYYYYYYY| type | data | + // +--------+--------+--------+--------+========+ + // + // ext 32 stores an integer and a byte array whose length is upto (2^32)-1 bytes: + // +--------+--------+--------+--------+--------+--------+========+ + // | 0xc9 |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| type | data | + // +--------+--------+--------+--------+--------+--------+========+ + // + // where + // * XXXXXXXX is a 8-bit unsigned integer which represents N + // * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N + // * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a big-endian 32-bit unsigned integer which represents N + // * N is a length of data + // * type is a signed 8-bit signed integer + // * type < 0 is reserved for future extension including 2-byte type information + if (i == FIXEXT1) + length = 1; + else if (i == FIXEXT2) + length = 2; + else if (i == FIXEXT4) + length = 4; + else if (i == FIXEXT8) + length = 8; + else if (i == FIXEXT16) + length = 16; + else if (i == EXT8) + length = readUInt1(); + else if (i == EXT16) + length = readUInt2(); + else if (i == EXT32) + length = readUInt4(); + extType = is.read(); + + break; + } + case MAP:{ + // fixmap stores a map whose length is upto 15 elements + // +--------+~~~~~~~~~~~~~~~~~+ + // |1000XXXX| N*2 objects | + // +--------+~~~~~~~~~~~~~~~~~+ + // + // map 16 stores a map whose length is upto (2^16)-1 elements + // +--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // | 0xde |YYYYYYYY|YYYYYYYY| N*2 objects | + // +--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // + // map 32 stores a map whose length is upto (2^32)-1 elements + // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // | 0xdf |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| N*2 objects | + // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // + // where + // * XXXX is a 4-bit unsigned integer which represents N + // * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N + // * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a 32-bit big-endian unsigned integer which represents N + // * N is the size of a map + // * odd elements in objects are keys of a map + // * the next element of a key is its associated value + if (i <= FIXMAP_U) + length = i & 0x0F; + else if (i == MAP16) + length = readUInt2(); + else + length = readUInt4(); + break; + } + default: + throw new IOException("Invalid flag 0xC1 detected in stream."); + } + return currentDataType; + } + + /** + * Returns the length value for the field. + * For ints/floats/bins/strings, this is the number of bytes that the field takes up (minus the data-type flag). + * For arrays, it's the number of array entries. + * For maps, it's the number of map entries. + */ + long readLength() { + return length; + } + + /** + * Read a boolean from the stream. + */ + boolean readBoolean() { + return lastByte == TRUE; + } + + /** + * Read a string from the stream. + */ + String readString() throws IOException { + return new String(readBinary(), IOUtils.UTF8); + } + + /** + * Read a binary field from the stream. + */ + byte[] readBinary() throws IOException { + byte[] b = new byte[(int)length]; + is.read(b); + return b; + } + + /** + * Read an integer from the stream. + */ + int readInt() throws IOException { + if (length == 0) + return lastByte; + if (length == 1) + return is.read(); + if (length == 2) + return (is.read() << 8) | is.read(); + int i = is.read(); i <<= 8; i |= is.read(); i <<= 8; i |= is.read(); i <<= 8; i |= is.read(); + return i; + } + + /** + * Read a float from the stream. + */ + float readFloat() throws IOException { + return Float.intBitsToFloat(readInt()); + } + + /** + * Read a double from the stream. + */ + double readDouble() throws IOException { + return Double.longBitsToDouble(readLong()); + } + + /** + * Read 64-bit long from the stream. + */ + long readLong() throws IOException { + if (length == 4) + return readUInt4(); + long l = is.read(); l <<= 8; l |= is.read(); l <<= 8; l |= is.read(); l <<= 8; l |= is.read(); l <<= 8; l |= is.read(); l <<= 8; l |= is.read(); l <<= 8; l |= is.read(); l <<= 8; l |= is.read(); + return l; + } + + /** + * Return the extended-format type. + * Currently not used. + */ + int getExtType() { + return extType; + } + + /** + * Read one byte from the stream. + */ + private int readUInt1() throws IOException { + return is.read(); + } + + /** + * Read two bytes from the stream. + */ + private int readUInt2() throws IOException { + return (is.read() << 8) | is.read(); + } + + /** + * Read four bytes from the stream. + */ + private long readUInt4() throws IOException { + long l = is.read(); l <<= 8; l |= is.read(); l <<= 8; l |= is.read(); l <<= 8; l |= is.read(); + return l; + } + + /** + * Return the current read position in the stream (i.e. number of bytes we've read so far). + */ + int getPosition() { + return pos; + } +}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackOutputStream.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackOutputStream.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackOutputStream.java new file mode 100644 index 0000000..3ffed66 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackOutputStream.java @@ -0,0 +1,322 @@ +/*************************************************************************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + ***************************************************************************************************************************/ +package org.apache.juneau.msgpack; + +import static org.apache.juneau.msgpack.DataType.*; + +import java.io.*; +import java.math.*; +import java.util.concurrent.atomic.*; + +/** + * Specialized output stream for serializing MessagePack streams. + * <p> + * <b>Note: This class is not intended for external use.</b> + * + * @author James Bognar ([email protected]) + */ +public final class MsgPackOutputStream extends OutputStream { + + private final OutputStream os; + + /** + * Constructor. + * @param os The output stream being wrapped. + */ + protected MsgPackOutputStream(OutputStream os) { + this.os = os; + } + + @Override /* OutputStream */ + public void write(int b) throws IOException { + os.write(b); + } + + /** + * Same as {@link #write(int)}. + */ + final MsgPackOutputStream append(byte b) throws IOException { + os.write(b); + return this; + } + + /** + * Same as {@link #write(byte[])}. + */ + final MsgPackOutputStream append(byte[] b) throws IOException { + os.write(b); + return this; + } + + /** + * Appends one byte to the stream. + */ + final MsgPackOutputStream append1(int i) throws IOException { + os.write(i); + return this; + } + + /** + * Appends two bytes to the stream. + */ + final MsgPackOutputStream append2(int i) throws IOException { + return append1(i>>8).append1(i); + } + + /** + * Appends four bytes to the stream. + */ + final MsgPackOutputStream append4(int i) throws IOException { + return append1(i>>24).append1(i>>16).append1(i>>8).append1(i); + } + + /** + * Appends eight bytes to the stream. + */ + final MsgPackOutputStream append8(long l) throws IOException { + return append1((int)(l>>56)).append1((int)(l>>48)).append1((int)(l>>40)).append1((int)(l>>32)).append1((int)(l>>24)).append1((int)(l>>16)).append1((int)(l>>8)).append1((int)(l)); + } + + /** + * Appends a NULL flag to the stream. + */ + final MsgPackOutputStream appendNull() throws IOException { + return append1(NIL); + } + + /** + * Appends a boolean to the stream. + */ + final MsgPackOutputStream appendBoolean(boolean b) throws IOException { + return append1(b ? TRUE : FALSE); + } + + /** + * Appends an integer to the stream. + */ + final MsgPackOutputStream appendInt(int i) throws IOException { + // POSFIXINT_L = 0x00, // pos fixint 0xxxxxxx 0x00 - 0x7f + // POSFIXINT_U = 0x7F, + // UINT8 = 0xCC, // uint 8 11001100 0xcc + // UINT16 = 0xCD, // uint 16 11001101 0xcd + // UINT32 = 0xCE, // uint 32 11001110 0xce + // UINT64 = 0xCF, // uint 64 11001111 0xcf + // INT8 = 0xD0, // int 8 11010000 0xd0 + // INT16 = 0xD1, // int 16 11010001 0xd1 + // INT32 = 0xD2, // int 32 11010010 0xd2 + // INT64 = 0xD3, // int 64 11010011 0xd3 + // NEGFIXINT_L = 0xE0, // neg fixint 111xxxxx 0xe0 - 0xff + // NEGFIXINT_U = 0xFF; + if (i >= 0) { + if (i < (1<<7)) + return append1(i); + if (i < (1<<15)) + return append1(INT16).append2(i); + return append1(INT32).append4(i); + } + if (i > -(1<<6)) + return append((byte)(0xE0 | -i)); + if (i > -(1<<7)) + return append1(INT8).append1(i); + if (i > -(1<<15)) + return append1(INT16).append2(i); + return append1(INT32).append4(i); + } + + final long L2X31 = ((long)(1<<30))*2; + + /** + * Appends a long to the stream. + */ + final MsgPackOutputStream appendLong(long l) throws IOException { + if (l < L2X31 && l > -(L2X31)) + return appendInt((int)l); + return append1(INT64).append8(l); + } + + /** + * Appends a generic Number to the stream. + */ + final MsgPackOutputStream appendNumber(Number n) throws IOException { + Class<?> c = n.getClass(); + if (c == Integer.class || c == Short.class || c == Byte.class || c == AtomicInteger.class) + return appendInt(n.intValue()); + if (c == Long.class || c == AtomicLong.class) + return appendLong(n.longValue()); + if (c == Float.class) + return appendFloat(n.floatValue()); + if (c == Double.class) + return appendDouble(n.doubleValue()); + if (c == BigInteger.class) + return appendLong(n.longValue()); + if (c == BigDecimal.class) + return appendDouble(n.doubleValue()); + return appendInt(0); + } + + /** + * Appends a float to the stream. + */ + final MsgPackOutputStream appendFloat(float f) throws IOException { + // FLOAT32 = 0xCA, // float 32 11001010 0xca + return append1(FLOAT32).append4(Float.floatToIntBits(f)); + + } + + /** + * Appends a double to the stream. + */ + final MsgPackOutputStream appendDouble(double d) throws IOException { + // FLOAT64 = 0xCB, // float 64 11001011 0xcb + return append1(FLOAT64).append8(Double.doubleToLongBits(d)); + } + + /** + * Appends a string to the stream. + */ + final MsgPackOutputStream appendString(CharSequence cs) throws IOException { + + // fixstr stores a byte array whose length is upto 31 bytes: + // +--------+========+ + // |101XXXXX| data | + // +--------+========+ + // + // str 8 stores a byte array whose length is upto (2^8)-1 bytes: + // +--------+--------+========+ + // | 0xd9 |YYYYYYYY| data | + // +--------+--------+========+ + // + // str 16 stores a byte array whose length is upto (2^16)-1 bytes: + // +--------+--------+--------+========+ + // | 0xda |ZZZZZZZZ|ZZZZZZZZ| data | + // +--------+--------+--------+========+ + // + // str 32 stores a byte array whose length is upto (2^32)-1 bytes: + // +--------+--------+--------+--------+--------+========+ + // | 0xdb |AAAAAAAA|AAAAAAAA|AAAAAAAA|AAAAAAAA| data | + // +--------+--------+--------+--------+--------+========+ + // where + // * XXXXX is a 5-bit unsigned integer which represents N + // * YYYYYYYY is a 8-bit unsigned integer which represents N + // * ZZZZZZZZ_ZZZZZZZZ is a 16-bit big-endian unsigned integer which represents N + // * AAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA is a 32-bit big-endian unsigned integer which represents N + // * N is the length of data + + byte[] b = cs.toString().getBytes("UTF-8"); + if (b.length < 32) + return append1(0xA0 + b.length).append(b); + if (b.length < (1<<8)) + return append1(STR8).append1(b.length).append(b); + if (b.length < (1<<16)) + return append1(STR16).append2(b.length).append(b); + return append1(STR32).append4(b.length).append(b); + } + + /** + * Appends a binary field to the stream. + */ + final MsgPackOutputStream appendBinary(byte[] b) throws IOException { + // bin 8 stores a byte array whose length is upto (2^8)-1 bytes: + // +--------+--------+========+ + // | 0xc4 |XXXXXXXX| data | + // +--------+--------+========+ + // + // bin 16 stores a byte array whose length is upto (2^16)-1 bytes: + // +--------+--------+--------+========+ + // | 0xc5 |YYYYYYYY|YYYYYYYY| data | + // +--------+--------+--------+========+ + // + // bin 32 stores a byte array whose length is upto (2^32)-1 bytes: + // +--------+--------+--------+--------+--------+========+ + // | 0xc6 |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| data | + // +--------+--------+--------+--------+--------+========+ + // + // where + // * XXXXXXXX is a 8-bit unsigned integer which represents N + // * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N + // * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a 32-bit big-endian unsigned integer which represents N + // * N is the length of data + + if (b.length < (1<<8)) + return append1(BIN8).append1(b.length).append(b); + if (b.length < (1<<16)) + return append1(BIN16).append2(b.length).append(b); + return append1(BIN32).append4(b.length).append(b); + } + + /** + * Appends an array data type flag to the stream. + */ + final MsgPackOutputStream startArray(int size) throws IOException { + // fixarray stores an array whose length is upto 15 elements: + // +--------+~~~~~~~~~~~~~~~~~+ + // |1001XXXX| N objects | + // +--------+~~~~~~~~~~~~~~~~~+ + // + // array 16 stores an array whose length is upto (2^16)-1 elements: + // +--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // | 0xdc |YYYYYYYY|YYYYYYYY| N objects | + // +--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // + // array 32 stores an array whose length is upto (2^32)-1 elements: + // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // | 0xdd |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| N objects | + // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // + // where + // * XXXX is a 4-bit unsigned integer which represents N + // * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N + // * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a 32-bit big-endian unsigned integer which represents N + // N is the size of a array + + if (size < 16) + return append1(0x90 + size); + if (size < (1<<16)) + return append1(ARRAY16).append2(size); + return append1(ARRAY32).append4(size); + } + + /** + * Appends a map data type flag to the stream. + */ + final MsgPackOutputStream startMap(int size) throws IOException { + // fixmap stores a map whose length is upto 15 elements + // +--------+~~~~~~~~~~~~~~~~~+ + // |1000XXXX| N*2 objects | + // +--------+~~~~~~~~~~~~~~~~~+ + // + // map 16 stores a map whose length is upto (2^16)-1 elements + // +--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // | 0xde |YYYYYYYY|YYYYYYYY| N*2 objects | + // +--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // + // map 32 stores a map whose length is upto (2^32)-1 elements + // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // | 0xdf |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ| N*2 objects | + // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+ + // + // where + // * XXXX is a 4-bit unsigned integer which represents N + // * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N + // * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a 32-bit big-endian unsigned integer which represents N + // * N is the size of a map + // * odd elements in objects are keys of a map + // * the next element of a key is its associated value + + if (size < 16) + return append1(0x80 + size); + if (size < (1<<16)) + return append1(MAP16).append2(size); + return append1(MAP32).append4(size); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java new file mode 100644 index 0000000..ec62363 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java @@ -0,0 +1,261 @@ +/*************************************************************************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + ***************************************************************************************************************************/ +package org.apache.juneau.msgpack; + +import static org.apache.juneau.msgpack.DataType.*; + +import java.lang.reflect.*; +import java.util.*; + +import org.apache.juneau.*; +import org.apache.juneau.annotation.*; +import org.apache.juneau.parser.*; +import org.apache.juneau.transform.*; + +/** + * Parses a MessagePack stream into a POJO model. + * + * + * <h6 class='topic'>Media types</h6> + * <p> + * Handles <code>Content-Type</code> types: <code>octal/msgpack</code> + * + * + * <h6 class='topic'>Configurable properties</h6> + * <p> + * This class has the following properties associated with it: + * <ul> + * <li>{@link MsgPackParserContext} + * </ul> + * + * + * @author James Bognar ([email protected]) + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +@Consumes({"octal/msgpack"}) +public final class MsgPackParser extends InputStreamParser { + + /** Default parser, all default settings.*/ + public static final MsgPackParser DEFAULT = new MsgPackParser().lock(); + + /** + * Workhorse method. + */ + private <T> T parseAnything(MsgPackParserSession session, ClassMeta<T> nt, MsgPackInputStream is, Object outer) throws Exception { + + BeanContext bc = session.getBeanContext(); + if (nt == null) + nt = (ClassMeta<T>)object(); + PojoTransform<T,Object> transform = (PojoTransform<T,Object>)nt.getPojoTransform(); + ClassMeta<?> ft = nt.getTransformedClassMeta(); + session.setCurrentClass(ft); + + Object o = null; + DataType dt = is.readDataType(); + int length = (int)is.readLength(); + + if (dt != DataType.NULL) { + if (dt == BOOLEAN) + o = is.readBoolean(); + else if (dt == INT) + o = is.readInt(); + else if (dt == LONG) + o = is.readLong(); + else if (dt == FLOAT) + o = is.readFloat(); + else if (dt == DOUBLE) + o = is.readDouble(); + else if (dt == STRING) + o = session.trim(is.readString()); + else if (dt == BIN) + o = is.readBinary(); + else if (dt == ARRAY && ft.isObject()) { + ObjectList ol = new ObjectList(bc); + for (int i = 0; i < length; i++) + ol.add(parseAnything(session, object(), is, outer)); + o = ol; + } else if (dt == MAP && ft.isObject()) { + ObjectMap om = new ObjectMap(bc); + for (int i = 0; i < length; i++) + om.put(parseAnything(session, string(), is, outer), parseAnything(session, object(), is, om)); + o = om.cast(); + } + + if (ft.isObject()) { + // Do nothing. + } else if (ft.isBoolean() || ft.isCharSequence() || ft.isChar() || ft.isNumber()) { + o = bc.convertToType(o, ft); + } else if (ft.isMap()) { + if (dt == MAP) { + Map m = (ft.canCreateNewInstance(outer) ? (Map)ft.newInstance(outer) : new ObjectMap(bc)); + for (int i = 0; i < length; i++) { + Object key = parseAnything(session, ft.getKeyType(), is, outer); + ClassMeta<?> vt = ft.getValueType(); + Object value = parseAnything(session, vt, is, m); + setName(vt, value, key); + m.put(key, value); + } + o = m; + } else { + throw new ParseException(session, "Invalid data type {0} encountered for parse type {1}", dt, ft); + } + } else if (ft.canCreateNewInstanceFromObjectMap(outer)) { + ObjectMap m = new ObjectMap(bc); + for (int i = 0; i < length; i++) + m.put(parseAnything(session, string(), is, outer), parseAnything(session, object(), is, m)); + o = ft.newInstanceFromObjectMap(outer, m); + } else if (ft.canCreateNewBean(outer)) { + if (dt == MAP) { + BeanMap m = bc.newBeanMap(outer, ft.getInnerClass()); + for (int i = 0; i < length; i++) { + String pName = parseAnything(session, string(), is, m.getBean(false)); + BeanPropertyMeta<?> bpm = m.getPropertyMeta(pName); + if (bpm == null) { + if (pName.equals("_class")) + parseAnything(session, bc.string(), is, null); + else + onUnknownProperty(session, pName, m, 0, is.getPosition()); + } else { + ClassMeta<?> cm = bpm.getClassMeta(); + Object value = parseAnything(session, cm, is, m.getBean(false)); + setName(cm, value, pName); + bpm.set(m, value); + } + } + o = m.getBean(); + } else { + throw new ParseException(session, "Invalid data type {0} encountered for parse type {1}", dt, ft); + } + } else if (ft.canCreateNewInstanceFromString(outer) && dt == STRING) { + o = ft.newInstanceFromString(outer, o == null ? "" : o.toString()); + } else if (ft.canCreateNewInstanceFromNumber(outer) && dt.isOneOf(INT, LONG, FLOAT, DOUBLE)) { + o = ft.newInstanceFromNumber(outer, (Number)o); + } else if (ft.isCollection()) { + if (dt == MAP) { + ObjectMap m = new ObjectMap(bc); + for (int i = 0; i < length; i++) + m.put(parseAnything(session, string(), is, outer), parseAnything(session, object(), is, m)); + o = m.cast(); + } else if (dt == ARRAY) { + Collection l = (ft.canCreateNewInstance(outer) ? (Collection)ft.newInstance() : new ObjectList(bc)); + for (int i = 0; i < length; i++) + l.add(parseAnything(session, ft.getElementType(), is, l)); + o = l; + } else { + throw new ParseException(session, "Invalid data type {0} encountered for parse type {1}", dt, ft); + } + } else if (ft.isArray()) { + if (dt == MAP) { + ObjectMap m = new ObjectMap(bc); + for (int i = 0; i < length; i++) + m.put(parseAnything(session, string(), is, outer), parseAnything(session, object(), is, m)); + o = m.cast(); + } else if (dt == ARRAY) { + Collection l = (ft.isCollection() && ft.canCreateNewInstance(outer) ? (Collection)ft.newInstance() : new ObjectList(bc)); + for (int i = 0; i < length; i++) + l.add(parseAnything(session, ft.getElementType(), is, l)); + o = bc.toArray(ft, l); + } else { + throw new ParseException(session, "Invalid data type {0} encountered for parse type {1}", dt, ft); + } + } else if (dt == MAP) { + ObjectMap m = new ObjectMap(bc); + for (int i = 0; i < length; i++) + m.put(parseAnything(session, string(), is, outer), parseAnything(session, object(), is, m)); + if (m.containsKey("_class")) + o = m.cast(); + else + throw new ParseException(session, "Class ''{0}'' could not be instantiated. Reason: ''{1}''", ft.getInnerClass().getName(), ft.getNotABeanReason()); + } else { + throw new ParseException(session, "Invalid data type {0} encountered for parse type {1}", dt, ft); + } + } + + if (transform != null && o != null) + o = transform.normalize(o, nt); + + if (outer != null) + setParent(nt, o, outer); + + return (T)o; + } + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Parser */ + public MsgPackParserSession createSession(Object input, ObjectMap op, Method javaMethod, Object outer) { + return new MsgPackParserSession(getContext(MsgPackParserContext.class), getBeanContext(), input, op, javaMethod, outer); + } + + @Override /* Parser */ + protected <T> T doParse(ParserSession session, ClassMeta<T> type) throws Exception { + MsgPackParserSession s = (MsgPackParserSession)session; + type = s.getBeanContext().normalizeClassMeta(type); + MsgPackInputStream is = s.getInputStream(); + T o = parseAnything(s, type, is, s.getOuter()); + return o; + } + + @Override /* Parser */ + public MsgPackParser setProperty(String property, Object value) throws LockedException { + super.setProperty(property, value); + return this; + } + + @Override /* CoreApi */ + public MsgPackParser setProperties(ObjectMap properties) throws LockedException { + super.setProperties(properties); + return this; + } + + @Override /* CoreApi */ + public MsgPackParser addNotBeanClasses(Class<?>...classes) throws LockedException { + super.addNotBeanClasses(classes); + return this; + } + + @Override /* CoreApi */ + public MsgPackParser addTransforms(Class<?>...classes) throws LockedException { + super.addTransforms(classes); + return this; + } + + @Override /* CoreApi */ + public <T> MsgPackParser addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException { + super.addImplClass(interfaceClass, implClass); + return this; + } + + @Override /* CoreApi */ + public MsgPackParser setClassLoader(ClassLoader classLoader) throws LockedException { + super.setClassLoader(classLoader); + return this; + } + + @Override /* Lockable */ + public MsgPackParser lock() { + super.lock(); + return this; + } + + @Override /* Lockable */ + public MsgPackParser clone() { + try { + return (MsgPackParser)super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); // Shouldn't happen + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserContext.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserContext.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserContext.java new file mode 100644 index 0000000..2ed5522 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserContext.java @@ -0,0 +1,49 @@ +/*************************************************************************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + ***************************************************************************************************************************/ +package org.apache.juneau.msgpack; + +import org.apache.juneau.*; +import org.apache.juneau.parser.*; + +/** + * Configurable properties on the {@link MsgPackParser} class. + * <p> + * Context properties are set by calling {@link ContextFactory#setProperty(String, Object)} on the context factory + * returned {@link CoreApi#getContextFactory()}. + * <p> + * The following convenience methods are also provided for setting context properties: + * <ul> + * <li>{@link MsgPackParser#setProperty(String,Object)} + * <li>{@link MsgPackParser#setProperties(ObjectMap)} + * <li>{@link MsgPackParser#addNotBeanClasses(Class[])} + * <li>{@link MsgPackParser#addTransforms(Class[])} + * <li>{@link MsgPackParser#addImplClass(Class,Class)} + * </ul> + * <p> + * See {@link ContextFactory} for more information about context properties. + * + * @author James Bognar ([email protected]) + */ +public final class MsgPackParserContext extends ParserContext { + + /** + * Constructor. + * <p> + * Typically only called from {@link ContextFactory#getContext(Class)}. + * + * @param cf The factory that created this context. + */ + public MsgPackParserContext(ContextFactory cf) { + super(cf); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java new file mode 100644 index 0000000..4f5057c --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java @@ -0,0 +1,70 @@ +/*************************************************************************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + ***************************************************************************************************************************/ +package org.apache.juneau.msgpack; + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +import org.apache.juneau.*; +import org.apache.juneau.parser.*; + +/** + * Session object that lives for the duration of a single use of {@link MsgPackParser}. + * <p> + * This class is NOT thread safe. It is meant to be discarded after one-time use. + * + * @author James Bognar ([email protected]) + */ +public final class MsgPackParserSession extends ParserSession { + + private MsgPackInputStream inputStream; + + /** + * Create a new session using properties specified in the context. + * + * @param ctx The context creating this session object. + * The context contains all the configuration settings for this object. + * @param beanContext The bean context being used. + * @param input The input. Can be any of the following types: + * <ul> + * <li><jk>null</jk> + * <li>{@link Reader} + * <li>{@link CharSequence} + * <li>{@link InputStream} containing UTF-8 encoded text. + * <li>{@link File} containing system encoded text. + * </ul> + * @param op The override properties. + * These override any context properties defined in the context. + * @param javaMethod The java method that called this parser, usually the method in a REST servlet. + * @param outer The outer object for instantiating top-level non-static inner classes. + */ + public MsgPackParserSession(MsgPackParserContext ctx, BeanContext beanContext, Object input, ObjectMap op, Method javaMethod, Object outer) { + super(ctx, beanContext, input, op, javaMethod, outer); + } + + @Override /* ParserSession */ + public MsgPackInputStream getInputStream() throws ParseException { + if (inputStream == null) + inputStream = new MsgPackInputStream(super.getInputStream()); + return inputStream; + } + + @Override /* ParserSession */ + public Map<String,Object> getLastLocation() { + Map<String,Object> m = super.getLastLocation(); + if (inputStream != null) + m.put("position", inputStream.getPosition()); + return m; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java new file mode 100644 index 0000000..76aa138 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java @@ -0,0 +1,280 @@ +/*************************************************************************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + ***************************************************************************************************************************/ +package org.apache.juneau.msgpack; + +import java.lang.reflect.*; +import java.util.*; + +import org.apache.juneau.*; +import org.apache.juneau.annotation.*; +import org.apache.juneau.serializer.*; +import org.apache.juneau.transform.*; + +/** + * Serializes POJO models to MessagePack. + * + * + * <h6 class='topic'>Media types</h6> + * <p> + * Handles <code>Accept</code> types: <code>octal/msgpack</code> + * <p> + * Produces <code>Content-Type</code> types: <code>octal/msgpack</code> + * + * <h6 class='topic'>Configurable properties</h6> + * <p> + * This class has the following properties associated with it: + * <ul> + * <li>{@link MsgPackSerializerContext} + * <li>{@link SerializerContext} + * <li>{@link BeanContext} + * </ul> + * + * @author James Bognar ([email protected]) + */ +@Produces({"octal/msgpack"}) +public class MsgPackSerializer extends OutputStreamSerializer { + + /** Default serializer, all default settings.*/ + public static final MsgPackSerializer DEFAULT = new MsgPackSerializer().lock(); + + /** + * Workhorse method. Determines the type of object, and then calls the + * appropriate type-specific serialization method. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + MsgPackOutputStream serializeAnything(MsgPackSerializerSession session, MsgPackOutputStream out, Object o, ClassMeta<?> eType, String attrName, BeanPropertyMeta pMeta) throws Exception { + BeanContext bc = session.getBeanContext(); + + if (o == null) + return out.appendNull(); + + if (eType == null) + eType = object(); + + boolean addClassAttr; // Add "_class" attribute to element? + ClassMeta<?> aType; // The actual type + ClassMeta<?> gType; // The generic type + + aType = session.push(attrName, o, eType); + boolean isRecursion = aType == null; + + // Handle recursion + if (aType == null) { + o = null; + aType = object(); + } + + gType = aType.getTransformedClassMeta(); + addClassAttr = (session.isAddClassAttrs() && ! eType.equals(aType)); + + // Transform if necessary + PojoTransform transform = aType.getPojoTransform(); // The transform + if (transform != null) { + o = transform.transform(o); + + // If the transform's getTransformedClass() method returns Object, we need to figure out + // the actual type now. + if (gType.isObject()) + gType = bc.getClassMetaForObject(o); + } + + // '\0' characters are considered null. + if (o == null || (gType.isChar() && ((Character)o).charValue() == 0)) + out.appendNull(); + else if (gType.isBoolean()) + out.appendBoolean((Boolean)o); + else if (gType.isNumber()) + out.appendNumber((Number)o); + else if (gType.hasToObjectMapMethod()) + serializeMap(session, out, gType.toObjectMap(o), gType); + else if (gType.isBean()) + serializeBeanMap(session, out, bc.forBean(o), addClassAttr); + else if (gType.isUri() || (pMeta != null && (pMeta.isUri() || pMeta.isBeanUri()))) + out.appendString(session.resolveUri(o.toString())); + else if (gType.isMap()) { + if (o instanceof BeanMap) + serializeBeanMap(session, out, (BeanMap)o, addClassAttr); + else + serializeMap(session, out, (Map)o, eType); + } + else if (gType.isCollection()) { + if (addClassAttr) + serializeCollectionMap(session, out, (Collection)o, gType); + else + serializeCollection(session, out, (Collection) o, eType); + } + else if (gType.isArray()) { + if (addClassAttr) + serializeCollectionMap(session, out, toList(gType.getInnerClass(), o), gType); + else + serializeCollection(session, out, toList(gType.getInnerClass(), o), eType); + } else + out.appendString(session.toString(o)); + + if (! isRecursion) + session.pop(); + return out; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void serializeMap(MsgPackSerializerSession session, MsgPackOutputStream out, Map m, ClassMeta<?> type) throws Exception { + + ClassMeta<?> keyType = type.getKeyType(), valueType = type.getValueType(); + + m = session.sort(m); + + // The map size may change as we're iterating over it, so + // grab a snapshot of the entries in a separate list. + List<SimpleMapEntry> entries = new ArrayList<SimpleMapEntry>(m.size()); + for (Map.Entry e : (Set<Map.Entry>)m.entrySet()) + entries.add(new SimpleMapEntry(e.getKey(), e.getValue())); + + out.startMap(entries.size()); + + for (SimpleMapEntry e : entries) { + Object value = e.value; + Object key = session.generalize(e.key, keyType); + + serializeAnything(session, out, key, keyType, null, null); + serializeAnything(session, out, value, valueType, null, null); + } + } + + @SuppressWarnings({ "rawtypes" }) + private void serializeCollectionMap(MsgPackSerializerSession session, MsgPackOutputStream out, Collection o, ClassMeta<?> type) throws Exception { + + out.startMap(2); + serializeAnything(session, out, "_class", null, null, null); + serializeAnything(session, out, type.getInnerClass().getName(), null, null, null); + serializeAnything(session, out, "items", null, null, null); + serializeCollection(session, out, o, type); + } + + @SuppressWarnings({ "rawtypes" }) + private void serializeBeanMap(MsgPackSerializerSession session, MsgPackOutputStream out, final BeanMap<?> m, boolean addClassAttr) throws Exception { + + List<BeanPropertyValue> values = m.getValues(addClassAttr, session.isTrimNulls()); + + int size = values.size(); + for (BeanPropertyValue p : values) + if (p.getThrown() != null) + size--; + out.startMap(size); + + for (BeanPropertyValue p : values) { + BeanPropertyMeta pMeta = p.getMeta(); + String key = p.getName(); + Object value = p.getValue(); + Throwable t = p.getThrown(); + if (t != null) + session.addBeanGetterWarning(pMeta, t); + else { + serializeAnything(session, out, key, null, null, null); + serializeAnything(session, out, value, pMeta == null ? session.getBeanContext().string() : pMeta.getClassMeta(), key, pMeta); + } + } + } + + private static class SimpleMapEntry { + final Object key; + final Object value; + + private SimpleMapEntry(Object key, Object value) { + this.key = key; + this.value = value; + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private void serializeCollection(MsgPackSerializerSession session, MsgPackOutputStream out, Collection c, ClassMeta<?> type) throws Exception { + + ClassMeta<?> elementType = type.getElementType(); + List<Object> l = new ArrayList<Object>(c.size()); + + c = session.sort(c); + l.addAll(c); + + out.startArray(l.size()); + + for (Object o : l) + serializeAnything(session, out, o, elementType, "<iterator>", null); + } + + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Serializer */ + public MsgPackSerializerSession createSession(Object output, ObjectMap properties, Method javaMethod) { + return new MsgPackSerializerSession(getContext(MsgPackSerializerContext.class), getBeanContext(), output, properties, javaMethod); + } + + @Override /* Serializer */ + protected void doSerialize(SerializerSession session, Object o) throws Exception { + MsgPackSerializerSession s = (MsgPackSerializerSession)session; + serializeAnything(s, s.getOutputStream(), o, null, "root", null); + } + + @Override /* CoreApi */ + public MsgPackSerializer setProperty(String property, Object value) throws LockedException { + super.setProperty(property, value); + return this; + } + + @Override /* CoreApi */ + public MsgPackSerializer setProperties(ObjectMap properties) throws LockedException { + super.setProperties(properties); + return this; + } + + @Override /* CoreApi */ + public MsgPackSerializer addNotBeanClasses(Class<?>...classes) throws LockedException { + super.addNotBeanClasses(classes); + return this; + } + + @Override /* CoreApi */ + public MsgPackSerializer addTransforms(Class<?>...classes) throws LockedException { + super.addTransforms(classes); + return this; + } + + @Override /* CoreApi */ + public <T> MsgPackSerializer addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException { + super.addImplClass(interfaceClass, implClass); + return this; + } + + @Override /* CoreApi */ + public MsgPackSerializer setClassLoader(ClassLoader classLoader) throws LockedException { + super.setClassLoader(classLoader); + return this; + } + + @Override /* Lockable */ + public MsgPackSerializer lock() { + super.lock(); + return this; + } + + @Override /* Lockable */ + public MsgPackSerializer clone() { + try { + MsgPackSerializer c = (MsgPackSerializer)super.clone(); + return c; + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); // Shouldn't happen + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerContext.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerContext.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerContext.java new file mode 100644 index 0000000..49638b2 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerContext.java @@ -0,0 +1,49 @@ +/*************************************************************************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + ***************************************************************************************************************************/ +package org.apache.juneau.msgpack; + +import org.apache.juneau.*; +import org.apache.juneau.serializer.*; + +/** + * Configurable properties on the {@link MsgPackSerializer} class. + * <p> + * Context properties are set by calling {@link ContextFactory#setProperty(String, Object)} on the context factory + * returned {@link CoreApi#getContextFactory()}. + * <p> + * The following convenience methods are also provided for setting context properties: + * <ul> + * <li>{@link MsgPackSerializer#setProperty(String,Object)} + * <li>{@link MsgPackSerializer#setProperties(ObjectMap)} + * <li>{@link MsgPackSerializer#addNotBeanClasses(Class[])} + * <li>{@link MsgPackSerializer#addTransforms(Class[])} + * <li>{@link MsgPackSerializer#addImplClass(Class,Class)} + * </ul> + * <p> + * See {@link ContextFactory} for more information about context properties. + * + * @author James Bognar ([email protected]) + */ +public final class MsgPackSerializerContext extends SerializerContext { + + /** + * Constructor. + * <p> + * Typically only called from {@link ContextFactory#getContext(Class)}. + * + * @param cf The factory that created this context. + */ + public MsgPackSerializerContext(ContextFactory cf) { + super(cf); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java new file mode 100644 index 0000000..a1709d9 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerSession.java @@ -0,0 +1,52 @@ +/*************************************************************************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + ***************************************************************************************************************************/ +package org.apache.juneau.msgpack; + +import java.lang.reflect.*; + +import org.apache.juneau.*; +import org.apache.juneau.json.*; +import org.apache.juneau.serializer.*; + +/** + * Session object that lives for the duration of a single use of {@link MsgPackSerializer}. + * <p> + * This class is NOT thread safe. It is meant to be discarded after one-time use. + * + * @author James Bognar ([email protected]) + */ +public final class MsgPackSerializerSession extends SerializerSession { + + /** + * Create a new session using properties specified in the context. + * + * @param ctx The context creating this session object. + * The context contains all the configuration settings for this object. + * @param beanContext The bean context being used. + * @param output The output object. See {@link JsonSerializerSession#getOutputStream()} for valid class types. + * @param op The override properties. + * These override any context properties defined in the context. + * @param javaMethod The java method that called this parser, usually the method in a REST servlet. + */ + protected MsgPackSerializerSession(MsgPackSerializerContext ctx, BeanContext beanContext, Object output, ObjectMap op, Method javaMethod) { + super(ctx, beanContext, output, op, javaMethod); + } + + @Override + public MsgPackOutputStream getOutputStream() throws Exception { + Object output = getOutput(); + if (output instanceof MsgPackOutputStream) + return (MsgPackOutputStream)output; + return new MsgPackOutputStream(super.getOutputStream()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/msgpack/package.html ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/package.html b/juneau-core/src/main/java/org/apache/juneau/msgpack/package.html new file mode 100644 index 0000000..d37c0ac --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/package.html @@ -0,0 +1,63 @@ +<!DOCTYPE HTML> +<!-- +/*************************************************************************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + ***************************************************************************************************************************/ + --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + /* For viewing in Page Designer */ + @IMPORT url("../../../../../../javadoc.css"); + + /* For viewing in REST interface */ + @IMPORT url("../htdocs/javadoc.css"); + body { + margin: 20px; + } + </style> + <script> + /* Replace all @code and @link tags. */ + window.onload = function() { + document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>'); + document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>'); + } + </script> +</head> +<body> +<p>JSON serialization and parsing support</p> +<script> + function toggle(x) { + var div = x.nextSibling; + while (div != null && div.nodeType != 1) + div = div.nextSibling; + if (div != null) { + var d = div.style.display; + if (d == 'block' || d == '') { + div.style.display = 'none'; + x.className += " closed"; + } else { + div.style.display = 'block'; + x.className = x.className.replace(/(?:^|\s)closed(?!\S)/g , '' ); + } + } + } +</script> + +<a id='TOC'></a><h5 class='toc'>Table of Contents</h5> +<ol class='toc'> +</ol> + +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/package.html ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/package.html b/juneau-core/src/main/java/org/apache/juneau/package.html new file mode 100644 index 0000000..8b3d585 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/package.html @@ -0,0 +1,217 @@ +<!DOCTYPE HTML> +<!-- +/*************************************************************************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + ***************************************************************************************************************************/ + --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + /* For viewing in Page Designer */ + @IMPORT url("../../../../../javadoc.css"); + + /* For viewing in REST interface */ + @IMPORT url("../htdocs/javadoc.css"); + body { + margin: 20px; + } + </style> + <script> + /* Replace all @code and @link tags. */ + window.onload = function() { + document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>'); + document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>'); + } + </script> +</head> +<body> +<p>Base toolkit for serializers, parsers, and bean contexts</p> + +<script> + function toggle(x) { + var div = x.nextSibling; + while (div != null && div.nodeType != 1) + div = div.nextSibling; + if (div != null) { + var d = div.style.display; + if (d == 'block' || d == '') { + div.style.display = 'none'; + x.className += " closed"; + } else { + div.style.display = 'block'; + x.className = x.className.replace(/(?:^|\s)closed(?!\S)/g , '' ); + } + } + } +</script> + +<a id='TOC'></a><h5 class='toc'>Table of Contents</h5> +<ol class='toc'> + <li><p><a class='doclink' href='#BeanContext_Api'>Bean Context API</a></p> + <ol> + <li><p><a class='doclink' href='#BeanMap'>The BeanMap class</a></p> + <li><p><a class='doclink' href='#BeanContext'>The BeanContext class</a></p> + <li><p><a class='doclink' href='#Bean'>Bean annotations</a></p> + </ol> + <li><p><a class='doclink' href='#ObjectMap_ObjectList'>ObjectMap and ObjectList APIs</a></p> + <li><p><a class='doclink' href='#PojoCategories'>POJO Categories</a></p> +</ol> + +<!-- ======================================================================================================== --> +<a id="BeanContext_Api"></a> +<h2 class='topic' onclick='toggle(this)'>1 - Bean Context API</h2> +<div class='topic'> + <p> + The {@link org.apache.juneau.BeanContext} class is the core class in the Juneau architecture. It serves multiple functions... + </p> + <ul class='normal'> + <li>It provides the ability to create instances of {@link org.apache.juneau.BeanMap BeanMaps}. + <li>It serves as a repository for {@link org.apache.juneau.transform.Transform Transforms}, which are used to tailor how beans and non-beans are handled. + <li>It's used by all built-in {@link org.apache.juneau.serializer.Serializer Serializers} and {@link org.apache.juneau.parser.Parser Parsers} for working with POJOs in a consistent way. + </ul> + + <!-- ======================================================================================================== --> + <a id="BeanMap"></a> + <h3 class='topic' onclick='toggle(this)'>1.1 - The BeanMap class</h3> + <div class='topic'> + <p> + The {@link org.apache.juneau.BeanMap} class allows you to access the properties of a bean through the familiar {@code Map} interface. + So, for example, you can use the {@code Map.get(key)} method to retrieve a property value in leu of it's getter method, and the {@code Map.put(key, value)} method to set a property value in leu of it's setter method. + </p> + <p> + The serialization and parsing of beans in Juneau is accomplished by wrapping Java beans inside instances of the class {@code BeanMap}. + </p> + <p> + <b>Note:</b> Instances of {@link org.apache.juneau.BeanMap} objects are always retrieved through the {@link org.apache.juneau.BeanContext} class. You cannot instantiate {@code BeanMaps} directly since the rules for defining what constitutes a bean depend on various settings in the bean context. + </p> + <p> + In general, the performance on using the {@link org.apache.juneau.BeanMap} class to access properties is equivalent to using reflection directly. + </p> + <p> + See the {@link org.apache.juneau.BeanMap} javadoc for more information. + </p> + </div> + + <!-- ======================================================================================================== --> + <a id="BeanContext"></a> + <h3 class='topic' onclick='toggle(this)'>1.2 - The BeanContext class</h3> + <div class='topic'> + <p> + The {@link org.apache.juneau.BeanContext} class is the workhorse class used to wrap Java beans inside {@link org.apache.juneau.BeanMap BeanMaps}. + There are several options provided on the {@link org.apache.juneau.BeanContext} class to tailor the definition of a bean. + </p> + <p> + The following is a very simple example of how to wrap a bean inside a {@link org.apache.juneau.BeanMap} wrapper and use the wrapper interface to get and set property values on the bean. + In this case, we're using the DEFAULT bean context. + </p> + <p class='bcode'> + <jc>// A sample pseudo bean class.</jc> + <jk>public class</jk> Person { + <jk>public</jk> String getName(); + <jk>public void</jk> setName(String name); + <jk>public int</jk> getAge(); + <jk>public void</jk> setAge(<jk>int</jk> age); + } + + <jc>// Get an instance of a bean context. + // In this case, just use the default bean context.</jc> + BeanContext beanContext = BeanContext.<jsf>DEFAULT</jsf>; + + <jc>// Create an instance of our bean and wrap it in a bean map.</jc> + Person p = <jk>new</jk> Person(); + BeanMap<Person> m = beanContext.forBean(p); + + <jc>// Set some properties on the bean.</jc> + m.put(<js>"name"</js>, <js>"John Smith"</js>); + m.put(<js>"age"</js>, 21); + + <jc>// Print out bean properties.</jc> + System.out.println(m.get(<js>"name"</js>)); <jc>// Prints "John Smith"</jc> + System.out.println(p.getName()); <jc>// Prints "John Smith"</jc> + System.out.println(m.get(<js>"age"</js>)); <jc>// Prints 21</jc> + System.out.println(p.getAge()); <jc>// Prints 21</jc> + + <jc>// The bean context class can also create instances of bean maps.</jc> + m = beanContext.newBeanMap(Person.<jk>class</jk>); + p = m.getBean(); <jc>// Get the new wrapped bean.</jc> + + <jc>// The bean context class can also create instances of beans.</jc> + p = beanContext.newBean(Person.<jk>class</jk>); + </p> + <p> + There are 3 ways to get an instance of a {@link org.apache.juneau.BeanContext}: + </p> + <p class='bcode'> + <jc>// Use one of the default bean contexts.</jc> + BeanContext beanContext = BeanContext.<jsf>DEFAULT</jsf>; + + <jc>// Create a context from scratch with your own settings.</jc> + beanContext = <jk>new</jk> BeanContext().addTransforms(DateTransform.ISO8601DT.<jk>class</jk>); + + <jc>// Clone and modify an existing context.</jc> + beanContext = BeanContext.<jsf>DEFAULT</jsf>.clone().addTransforms(DateTransform.ISO8601DT.<jk>class</jk>); + </p> + <p> + The {@link org.apache.juneau.BeanContext} class is a highly-customizable class. + See the {@link org.apache.juneau.BeanContext} javadoc for more information. + </p> + </div> + + <!-- ======================================================================================================== --> + <a id="Bean"></a> + <h3 class='topic' onclick='toggle(this)'>1.3 - Bean annotations</h3> + <div class='topic'> + <p> + Juneau provides the following annotations that can be used to fine-tune what properties are associated with beans: + </p> + <ul class='normal'> + <li>{@link org.apache.juneau.annotation.Bean} - Fine-tune properties associated with beans. + <li>{@link org.apache.juneau.annotation.BeanProperty} - Fine-tune bean properties (fields / getters / setters). + <li>{@link org.apache.juneau.annotation.BeanConstructor} - Define read-only bean properties that can only be set through constructor arguments. + <li>{@link org.apache.juneau.annotation.BeanIgnore} - Prevent bean classes/methods/fields from being interpreted as bean constructs. + </ul> + <p> + These annotations always override the settings defined in the {@link org.apache.juneau.BeanContext} class. + </p> + <p> + For example, the following bean class will only have one property associated with it, <js>"name"</js>, since it's the only one listed in the list of properties. + </p> + <p class='bcode'> + <jc>// Bean with only one 'name' property</jc> + <ja>@Bean</ja>(properties={<js>"name"</js>}) + <jk>public class</jk> Person { + <jk>public</jk> String getName(); + <jk>public void</jk> setName(String name); + <jk>public int</jk> getAge(); + <jk>public void</jk> setAge(<jk>int</jk> age); + } + </p> + <p> + When this bean is serialized using one of the {@link org.apache.juneau.serializer.Serializer Serializers}, the age property will be ignored. + </p> + <p> + Using the <ja>@Bean</ja> and <ja>@BeanProperty</ja> annotations, it's also possible to include non-standard properties (for example, getters or setters with non-standard names), or override the names of properties (for example, {@code "Name"} or {@code "fullName"} instead of {@code "name"}). + </p> + <p> + It should be noted that the {@link org.apache.juneau.transform.BeanTransform} class can also be used to exclude properties from beans. + However, only the annotations can be used to include non-standard properties or override property names. + </p> + <p> + See the {@link org.apache.juneau.annotation.Bean}, {@link org.apache.juneau.annotation.BeanProperty}, {@link org.apache.juneau.annotation.BeanConstructor}, and {@link org.apache.juneau.annotation.BeanIgnore} javadocs for more information. + </p> + </div> +</div> + +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/parser/InputStreamParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/InputStreamParser.java b/juneau-core/src/main/java/org/apache/juneau/parser/InputStreamParser.java new file mode 100644 index 0000000..ac8237c --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/parser/InputStreamParser.java @@ -0,0 +1,45 @@ +/*************************************************************************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + ***************************************************************************************************************************/ +package org.apache.juneau.parser; + +import org.apache.juneau.annotation.*; + +/** + * Subclass of {@link Parser} for byte-based parsers. + * + * + * <h6 class='topic'>Description</h6> + * <p> + * This class is typically the parent class of all byte-based parsers. + * It has 1 abstract method to implement... + * <ul> + * <li><code>parse(InputStream, ClassMeta, ParserContext)</code> + * </ul> + * + * + * <h6 class='topic'>@Consumes annotation</h6> + * <p> + * The media types that this parser can handle is specified through the {@link Consumes @Consumes} annotation. + * <p> + * However, the media types can also be specified programmatically by overriding the {@link #getMediaTypes()} method. + * + * + * @author James Bognar ([email protected]) + */ +public abstract class InputStreamParser extends Parser { + + @Override /* Parser */ + public boolean isReaderParser() { + return false; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/parser/ParseException.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/ParseException.java b/juneau-core/src/main/java/org/apache/juneau/parser/ParseException.java new file mode 100644 index 0000000..08e2508 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/parser/ParseException.java @@ -0,0 +1,105 @@ +/*************************************************************************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + ***************************************************************************************************************************/ +package org.apache.juneau.parser; + +import java.text.*; +import java.util.*; + +import org.apache.juneau.*; +import org.apache.juneau.json.*; + +/** + * Exception that indicates invalid syntax encountered during parsing. + * + * @author James Bognar ([email protected]) + */ +public final class ParseException extends FormattedException { + + private static final long serialVersionUID = 1L; + + /** + * Constructor. + * + * @param session The parser session to extract information from. + * @param message The exception message containing {@link MessageFormat}-style arguments. + * @param args Message arguments. + */ + public ParseException(ParserSession session, String message, Object...args) { + super(getMessage(session, message, args)); + } + + /** + * Constructor. + * + * @param message The exception message containing {@link MessageFormat}-style arguments. + * @param args Message arguments. + */ + public ParseException(String message, Object...args) { + super(getMessage(null, message, args)); + } + + /** + * Constructor. + * + * @param session The parser session to extract information from. + * @param causedBy The inner exception. + */ + public ParseException(ParserSession session, Exception causedBy) { + super(causedBy, getMessage(session, causedBy.getMessage())); + } + + /** + * Constructor. + * + * @param causedBy The inner exception. + */ + public ParseException(Exception causedBy) { + super(causedBy, getMessage(null, causedBy.getMessage())); + } + + private static String getMessage(ParserSession session, String msg, Object... args) { + if (args.length != 0) + msg = MessageFormat.format(msg, args); + if (session != null) { + Map<String,Object> m = session.getLastLocation(); + if (m != null && ! m.isEmpty()) + msg = "Parse exception occurred at " + JsonSerializer.DEFAULT_LAX.toString(m) + ". " + msg; + } + return msg; + } + + /** + * Returns the highest-level <code>ParseException</code> in the stack trace. + * Useful for JUnit testing of error conditions. + * + * @return The root parse exception, or this exception if there isn't one. + */ + public ParseException getRootCause() { + ParseException t = this; + while (! (t.getCause() == null || ! (t.getCause() instanceof ParseException))) + t = (ParseException)t.getCause(); + return t; + } + + /** + * Sets the inner cause for this exception. + * + * @param cause The inner cause. + * @return This object (for method chaining). + */ + @Override /* Throwable */ + public synchronized ParseException initCause(Throwable cause) { + super.initCause(cause); + return this; + } +}
