Mercurial > hg > Members > sugi > MessagePack-java
view src/main/java/org/msgpack/util/json/JSONPacker.java @ 0:cb825acd883a
first commit
author | sugi |
---|---|
date | Sat, 18 Oct 2014 15:06:15 +0900 |
parents | |
children |
line wrap: on
line source
// // MessagePack for Java // // Copyright (C) 2009 - 2013 FURUHASHI Sadayuki // // Licensed 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.msgpack.util.json; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.math.BigInteger; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CodingErrorAction; import org.msgpack.io.Output; import org.msgpack.io.StreamOutput; import org.msgpack.MessagePack; import org.msgpack.MessageTypeException; import org.msgpack.packer.Packer; import org.msgpack.packer.AbstractPacker; import org.msgpack.packer.PackerStack; public class JSONPacker extends AbstractPacker { private static final byte[] NULL = new byte[] { 0x6e, 0x75, 0x6c, 0x6c }; private static final byte[] TRUE = new byte[] { 0x74, 0x72, 0x75, 0x65 }; private static final byte[] FALSE = new byte[] { 0x66, 0x61, 0x6c, 0x73, 0x65 }; private static final byte COMMA = 0x2c; private static final byte COLON = 0x3a; private static final byte QUOTE = 0x22; private static final byte LEFT_BR = 0x5b; private static final byte RIGHT_BR = 0x5d; private static final byte LEFT_WN = 0x7b; private static final byte RIGHT_WN = 0x7d; private static final byte BACKSLASH = 0x5c; private static final byte ZERO = 0x30; private static final int FLAG_FIRST_ELEMENT = 0x01; private static final int FLAG_MAP_KEY = 0x02; private static final int FLAG_MAP_VALUE = 0x04; // private static final int FLAG_MAP = FLAG_MAP_KEY | FLAG_MAP_VALUE; protected final Output out; private int[] flags; private PackerStack stack = new PackerStack(); private CharsetDecoder decoder; public JSONPacker(OutputStream stream) { this(new MessagePack(), stream); } public JSONPacker(MessagePack msgpack, OutputStream stream) { this(msgpack, new StreamOutput(stream)); } protected JSONPacker(MessagePack msgpack, Output out) { super(msgpack); this.out = out; this.stack = new PackerStack(); this.flags = new int[PackerStack.MAX_STACK_SIZE]; this.decoder = Charset.forName("UTF-8").newDecoder() .onMalformedInput(CodingErrorAction.REPORT) .onUnmappableCharacter(CodingErrorAction.REPORT); } @Override protected void writeBoolean(boolean v) throws IOException { beginElement(); if (v) { out.write(TRUE, 0, TRUE.length); } else { out.write(FALSE, 0, FALSE.length); } endElement(); } @Override protected void writeByte(byte v) throws IOException { beginElement(); byte[] b = Byte.toString(v).getBytes(); // TODO optimize out.write(b, 0, b.length); endElement(); } @Override protected void writeShort(short v) throws IOException { beginElement(); byte[] b = Short.toString(v).getBytes(); // TODO optimize out.write(b, 0, b.length); endElement(); } @Override protected void writeInt(int v) throws IOException { beginElement(); byte[] b = Integer.toString(v).getBytes(); // TODO optimize out.write(b, 0, b.length); endElement(); } @Override protected void writeLong(long v) throws IOException { beginElement(); byte[] b = Long.toString(v).getBytes(); // TODO optimize out.write(b, 0, b.length); endElement(); } @Override protected void writeBigInteger(BigInteger v) throws IOException { beginElement(); byte[] b = v.toString().getBytes(); // TODO optimize out.write(b, 0, b.length); endElement(); } @Override protected void writeFloat(float v) throws IOException { beginElement(); Float r = v; if (r.isInfinite() || r.isNaN()) { throw new IOException( "JSONPacker doesn't support NaN and infinite float value"); } byte[] b = Float.toString(v).getBytes(); // TODO optimize out.write(b, 0, b.length); endElement(); } @Override protected void writeDouble(double v) throws IOException { beginElement(); Double r = v; if (r.isInfinite() || r.isNaN()) { throw new IOException( "JSONPacker doesn't support NaN and infinite float value"); } byte[] b = Double.toString(v).getBytes(); // TODO optimize out.write(b, 0, b.length); endElement(); } @Override protected void writeByteArray(byte[] b, int off, int len) throws IOException { beginStringElement(); out.writeByte(QUOTE); escape(out, b, off, len); out.writeByte(QUOTE); endElement(); } @Override protected void writeByteBuffer(ByteBuffer bb) throws IOException { beginStringElement(); out.writeByte(QUOTE); int pos = bb.position(); try { escape(out, bb); } finally { bb.position(pos); } out.writeByte(QUOTE); endElement(); } @Override protected void writeString(String s) throws IOException { beginStringElement(); out.writeByte(QUOTE); escape(out, s); out.writeByte(QUOTE); endElement(); } @Override public Packer writeNil() throws IOException { beginElement(); out.write(NULL, 0, NULL.length); endElement(); return this; } @Override public Packer writeArrayBegin(int size) throws IOException { beginElement(); out.writeByte(LEFT_BR); endElement(); stack.pushArray(size); flags[stack.getDepth()] = FLAG_FIRST_ELEMENT; return this; } @Override public Packer writeArrayEnd(boolean check) throws IOException { if (!stack.topIsArray()) { throw new MessageTypeException( "writeArrayEnd() is called but writeArrayBegin() is not called"); } int remain = stack.getTopCount(); if (remain > 0) { if (check) { throw new MessageTypeException( "writeArrayEnd(check=true) is called but the array is not end: " + remain); } for (int i = 0; i < remain; i++) { writeNil(); } } stack.pop(); out.writeByte(RIGHT_BR); return this; } @Override public Packer writeMapBegin(int size) throws IOException { beginElement(); out.writeByte(LEFT_WN); endElement(); stack.pushMap(size); flags[stack.getDepth()] = FLAG_FIRST_ELEMENT | FLAG_MAP_KEY; return this; } @Override public Packer writeMapEnd(boolean check) throws IOException { if (!stack.topIsMap()) { throw new MessageTypeException( "writeMapEnd() is called but writeMapBegin() is not called"); } int remain = stack.getTopCount(); if (remain > 0) { if (check) { throw new MessageTypeException( "writeMapEnd(check=true) is called but the map is not end: " + remain); } for (int i = 0; i < remain; i++) { writeNil(); } } stack.pop(); out.writeByte(RIGHT_WN); return this; } @Override public void flush() throws IOException { out.flush(); } @Override public void close() throws IOException { out.close(); } public void reset() { stack.clear(); } private void beginElement() throws IOException { int flag = flags[stack.getDepth()]; if ((flag & FLAG_MAP_KEY) != 0) { throw new IOException("Key of a map must be a string in JSON"); } beginStringElement(); } private void beginStringElement() throws IOException { int flag = flags[stack.getDepth()]; if ((flag & FLAG_MAP_VALUE) != 0) { out.writeByte(COLON); } else if (stack.getDepth() > 0 && (flag & FLAG_FIRST_ELEMENT) == 0) { out.writeByte(COMMA); } } private void endElement() throws IOException { int flag = flags[stack.getDepth()]; if ((flag & FLAG_MAP_KEY) != 0) { flag &= ~FLAG_MAP_KEY; flag |= FLAG_MAP_VALUE; } else if ((flag & FLAG_MAP_VALUE) != 0) { flag &= ~FLAG_MAP_VALUE; flag |= FLAG_MAP_KEY; } flag &= ~FLAG_FIRST_ELEMENT; flags[stack.getDepth()] = flag; stack.reduceCount(); } private void escape(Output out, byte[] b, int off, int len) throws IOException { escape(out, ByteBuffer.wrap(b, off, len)); } private void escape(Output out, ByteBuffer bb) throws IOException { // TODO optimize String str = decoder.decode(bb).toString(); escape(out, str); } private final static int[] ESCAPE_TABLE; private final static byte[] HEX_TABLE; static { ESCAPE_TABLE = new int[128]; for (int i = 0; i < 0x20; ++i) { // control char ESCAPE_TABLE[i] = -1; } ESCAPE_TABLE['"'] = '"'; ESCAPE_TABLE['\\'] = '\\'; ESCAPE_TABLE[0x08] = 'b'; ESCAPE_TABLE[0x09] = 't'; ESCAPE_TABLE[0x0C] = 'f'; ESCAPE_TABLE[0x0A] = 'n'; ESCAPE_TABLE[0x0D] = 'r'; char[] hex = "0123456789ABCDEF".toCharArray(); HEX_TABLE = new byte[hex.length]; for (int i = 0; i < hex.length; ++i) { HEX_TABLE[i] = (byte) hex[i]; } } private static void escape(Output out, String s) throws IOException { byte[] tmp = new byte[] { (byte) '\\', (byte) 'u', 0, 0, 0, 0 }; char[] chars = s.toCharArray(); for (int i = 0; i < chars.length; i++) { int ch = chars[i]; if (ch <= 0x7f) { int e = ESCAPE_TABLE[ch]; if (e == 0) { // ascii char tmp[2] = (byte) ch; out.write(tmp, 2, 1); } else if (e > 0) { // popular control char tmp[2] = BACKSLASH; tmp[3] = (byte) e; out.write(tmp, 2, 2); } else { // control char uXXXXXX tmp[2] = ZERO; tmp[3] = ZERO; tmp[4] = HEX_TABLE[ch >> 4]; tmp[5] = HEX_TABLE[ch & 0x0f]; out.write(tmp, 0, 6); } } else if (ch <= 0x7ff) { // 2-bytes char tmp[2] = (byte) (0xc0 | (ch >> 6)); tmp[3] = (byte) (0x80 | (ch & 0x3f)); out.write(tmp, 2, 2); } else if (ch >= 0xd800 && ch <= 0xdfff) { // surrogates tmp[2] = HEX_TABLE[(ch >> 12) & 0x0f]; tmp[3] = HEX_TABLE[(ch >> 8) & 0x0f]; tmp[4] = HEX_TABLE[(ch >> 4) & 0x0f]; tmp[5] = HEX_TABLE[ch & 0x0f]; out.write(tmp, 0, 6); } else { // 3-bytes char tmp[2] = (byte) (0xe0 | (ch >> 12)); tmp[3] = (byte) (0x80 | ((ch >> 6) & 0x3f)); tmp[4] = (byte) (0x80 | (ch & 0x3f)); out.write(tmp, 2, 3); } } } }