Java tutorial
package byps; /* USE THIS FILE ACCORDING TO THE COPYRIGHT RULES IN LICENSE.TXT WHICH IS PART OF THE SOURCE CODE PACKAGE */ import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.StringTokenizer; import java.util.TimeZone; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class BBufferJson extends BBuffer { public BBufferJson(ByteBuffer buf) { super(BBinaryModel.MEDIUM, buf); if (buf != null) { buf.order(ByteOrder.BIG_ENDIAN); } } public ByteBuffer getBuffer() { return this.buf; } public void clear() { this.buf.clear(); addComma = false; } private boolean addComma = false; public void beginObject() { ensureRemaining(2); if (addComma) buf.put((byte) ','); buf.put((byte) '{'); addComma = false; } public void endObject() { ensureRemaining(1); buf.put((byte) '}'); addComma = true; } public void beginArray() { ensureRemaining(2); if (addComma) buf.put((byte) ','); buf.put((byte) '['); addComma = false; } public void endArray() { ensureRemaining(1); buf.put((byte) ']'); addComma = true; } public void putRef(int id) { beginObject(); putInt("*i", -id); endObject(); } public void putBoolean(boolean v) { putJsonValueAscii(null, v ? "true" : "false", STRING_WITHOUT_QUOTE); } public void putByte(byte v) { putJsonValueAscii(null, Byte.toString(v), STRING_WITHOUT_QUOTE); } public void putByte(char v) { putJsonValueAscii(null, Character.toString(v), STRING_WITHOUT_QUOTE); } public void putChar(char v) { putString(null, v != 0 ? Character.toString(v) : ""); } public void putShort(short v) { putJsonValueAscii(null, Short.toString(v), STRING_WITHOUT_QUOTE); } public void putInt(int v) { putJsonValueAscii(null, Integer.toString(v), STRING_WITHOUT_QUOTE); } /** * Long values are written as strings with a "." as suffix. * @param v */ public void putLong(long v) { putJsonValueAscii(null, Long.toString(v), STRING_WITH_QUOTE | STRING_LONG_VALUE); // Long als String } public void putFloat(float v) { putJsonValueAscii(null, Float.toString(v), STRING_WITHOUT_QUOTE); } public void putDouble(double v) { putJsonValueAscii(null, Double.toString(v), STRING_WITHOUT_QUOTE); } public void putBoolean(String name, boolean v) { putJsonValueAscii(name, Boolean.toString(v), STRING_WITHOUT_QUOTE); } public void putByte(String name, byte v) { putJsonValueAscii(name, Byte.toString(v), STRING_WITHOUT_QUOTE); } public void putChar(String name, char v) { putString(name, v != 0 ? Character.toString(v) : ""); } public void putShort(String name, short v) { putJsonValueAscii(name, Short.toString(v), STRING_WITHOUT_QUOTE); } public void putInt(String name, int v) { putJsonValueAscii(name, Integer.toString(v), STRING_WITHOUT_QUOTE); } /** * Long values are written as strings with a "." as suffix. * @param name * @param v */ public void putLong(String name, long v) { putJsonValueAscii(name, Long.toString(v), STRING_WITH_QUOTE | STRING_LONG_VALUE); } public void putFloat(String name, float v) { putJsonValueAscii(name, Float.toString(v), STRING_WITHOUT_QUOTE); } public void putDouble(String name, double v) { putJsonValueAscii(name, Double.toString(v), STRING_WITHOUT_QUOTE); } // public void putBooleanIf(String name, boolean value) { // if (value) putBoolean(name, value); // } // public void putByteIf(String name, byte value) { // if (value != 0) putByte(name, value); // } // public void putCharIf(String name, char value) { // if (value != 0) putChar(name, value); // } // public void putShortIf(String name, short value) { // if (value != 0) putShort(name, value); // } // public void putIntIf(String name, int value) { // if (value != 0) putInt(name, value); // } // public void putLongIf(String name, long value){ // if (value != 0) putLong(name, value); // } // public void putFloatIf(String name, float value) { // if (value != 0) putFloat(name, value); // } // public void putDoubleIf(String name, double value) { // if (value != 0) putDouble(name, value); // } // public void putStringIf(String name, String value) { // if (value != null && value.length() != 0) putString(name, value); // } // public void putArrayByteIf(String name, byte[] value) { // if (value != null) putArrayByte(name, value); // } // public static String base64(byte[] v) { // if (v == null) return null; // return javax.xml.bind.DatatypeConverter.printBase64Binary(v); // } // // public static byte[] base64(String v) { // if (v == null) return null; // return javax.xml.bind.DatatypeConverter.parseBase64Binary(v); // } public void putArrayByte(byte[] v) { putArrayByte(null, v); } public byte[] getArrayByte() { byte[] ret = null; int i = 0; char quote = nextJsonChar(true); if (quote == '\"' || quote == '\'') { ret = new byte[10]; for (;;) { if (i + 3 >= ret.length) { byte[] w = new byte[ret.length << 1]; System.arraycopy(ret, 0, w, 0, i); ret = w; } int c1 = getBase64Byte(buf.get()); if (c1 < 0) { break; } int c2 = getBase64Byte(buf.get()); ret[i++] = (byte) ((c1 << 2) | (c2 >> 4)); int c3 = getBase64Byte(buf.get()); if (c3 < 0) { // assume == internalSkip(1); break; } ret[i++] = (byte) (((c2 & 0x0F) << 4) | (c3 >> 2)); int c4 = getBase64Byte(buf.get()); if (c4 < 0) { // assume = break; } ret[i++] = (byte) (((c3 & 0x3) << 6) | c4); } if (i != ret.length) { byte[] w = new byte[i]; System.arraycopy(ret, 0, w, 0, i); ret = w; } } else { // assume null internalSkip(3); } return ret; } private int internalBase64Length(byte[] v) { int n = 0; if (v != null) { n += ((v.length + 2) * 4) / 3 + 2; } else { n = 4; } return n; } public void putArrayByte(String name, byte[] v) { ensureRemaining(+1 + // comma internalUtf8Length(name) + 1 + // : internalBase64Length(v)); if (name != null) { if (addComma) buf.put((byte) ','); internalPutString(name, true); buf.put((byte) ':'); addComma = false; } if (v == null) { putNull(); } else { if (addComma) buf.put((byte) ','); buf.put((byte) '\"'); int i = 0; while (i < v.length) { int x = 0; int b1 = v[i++] & 0xFF; //int c = (b1 & 0xFC) >> 2; //buf.put((byte)BASE64_CHARS[c]); x |= BASE64_CHARS[(b1 & 0xFC) >> 2] << 24; //c = ((b1 & 0x03) << 4); if (i == v.length) { // buf.put((byte)BASE64_CHARS[c]); // buf.put((byte)'='); // buf.put((byte)'='); x |= (BASE64_CHARS[((b1 & 0x03) << 4)] << 16) | ('=' << 8) | '='; buf.putInt(x); break; } int b2 = v[i++] & 0xFF; // c |= ((b2 & 0xF0) >> 4); // buf.put((byte)BASE64_CHARS[c]); x |= (BASE64_CHARS[((b1 & 0x03) << 4) | ((b2 & 0xF0) >> 4)] << 16); // c = ((b2 & 0x0F) << 2); if (i == v.length) { // buf.put((byte)BASE64_CHARS[c]); // buf.put((byte)'='); x |= (BASE64_CHARS[((b2 & 0x0F) << 2)] << 8) | '='; buf.putInt(x); break; } int b3 = (v[i++] & 0xFF); // c = ((b2 & 0x0F) << 2); // c |= ((b3 & 0xC0) >> 6); //buf.put((byte)BASE64_CHARS[c]); x |= BASE64_CHARS[((b2 & 0x0F) << 2) | ((b3 & 0xC0) >> 6)] << 8; // c = (b3 & 0x3F); //buf.put((byte)BASE64_CHARS[c]); x |= BASE64_CHARS[(b3 & 0x3F)]; buf.putInt(x); } buf.put((byte) '\"'); } addComma = true; } private int getBase64Byte(int c) { c &= 0xFF; if (c >= 'A' && c <= 'Z') return c - 'A'; if (c >= 'a' && c <= 'z') return c - 'a' + 26; if (c >= '0' && c <= '9') return c - '0' + 52; if (c == '+') return 62; if (c == '/') return 63; return -1; } private final static byte[] BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" .getBytes(); private int internalUtf8Length(String s) { int n = 2; // quotes if (s != null && s.length() != 0) { // s could be \u0000\u0000... n += (s.length() * 6); } return n; } public void putString(String name, String value) { ensureRemaining(internalUtf8Length(name) + internalUtf8Length(value) + 2); if (addComma) buf.put((byte) ','); if (name != null) { internalPutString(name, true); buf.put((byte) ':'); } addComma = false; putString(value); addComma = true; } public void putJsonValueAscii(String name, String value, int flags) { ensureRemaining(internalUtf8Length(name) + internalUtf8Length(value) + 2); if (addComma) buf.put((byte) ','); if (name != null) { internalPutString(name, true); buf.put((byte) ':'); } internalPutSimpleAsciiString(value, flags); addComma = true; } public void beginElement(String s) { ensureRemaining(internalUtf8Length(s) + 2); if (addComma) buf.put((byte) ','); internalPutString(s, true); buf.put((byte) ':'); addComma = false; } public void endElement() { addComma = true; } public void putString(String s) { ensureRemaining(internalUtf8Length(s) + 1); if (addComma) buf.put((byte) ','); else addComma = true; internalPutString(s, true); } protected void internalPutString(String s, boolean quote) { if (s != null && s.length() != 0) { if (quote) buf.put((byte) '\"'); StringTokenizer stok = new StringTokenizer(s, "\t\r\n\"\'\\", true); while (stok.hasMoreTokens()) { String t = stok.nextToken(); char c = t.charAt(0); if (c == '\t') buf.putShort((short) (('\\' << 8) | 't')); else if (c == '\r') buf.putShort((short) (('\\' << 8) | 'r')); else if (c == '\n') buf.putShort((short) (('\\' << 8) | 'n')); else if (c == '\"') buf.putShort((short) (('\\' << 8) | '\"')); else if (c == '\\') buf.putShort((short) (('\\' << 8) | '\\')); else { for (int i = 0; i < t.length(); i++) { internalPutCharUtf8(t.charAt(i)); } } } if (quote) buf.put((byte) '\"'); } else { // put empty string, null values are not supported buf.putShort((short) ((('\"') << 8) | ('\"'))); } } // Crockford // private final void internalPutCharUtf8(char z) { // if (z < ' ' || (z >= '\u0080' && z < '\u00a0') || // (z >= '\u2000' && z < '\u2100')) { // String s = Integer.toHexString((int)z); // buf.putShort((short)(('\\' << 8) | 'u')); // for (int i = 4; i > s.length(); i--) buf.put((byte)'0'); // for (int i = 0; i < s.length(); i++) buf.put((byte)s.charAt(i)); // } // else { // buf.put((byte)z); // } // } private final void internalPutCharUtf8(char z) { if (z > 0x800) { int a = (z & 0x3F) | 0x80; int b = ((z >> 6) & 0x3F) | 0x80; int c = ((z >> 12) & 0xF) | 0xE0; buf.putShort((short) ((c << 8) | b)); buf.put((byte) a); } else if (z >= 0x80) { int a = (z & 0x3F) | 0x80; int b = ((z >> 6) & 0x3F) | 0xC0; buf.putShort((short) ((b << 8) | a)); } else if (z < 0x20) { buf.putInt(('\\' << 24) | ('u' << 16) | ('0' << 8) | '0'); buf.put(hexCharBytes[(z & 0xF0) >> 4]); buf.put(hexCharBytes[z & 0x0F]); } else { buf.put((byte) z); } } private final static byte hexCharBytes[] = "0123456789ABCDEF".getBytes(); /** * Write a null value. * Used to write the first element of the object table. */ public final void putNull() { ensureRemaining(5); if (addComma) buf.put((byte) ','); else addComma = true; final int n = ('n' << 24) | ('u' << 16) | ('l' << 8) | ('l'); buf.putInt(n); } private final int STRING_WITHOUT_QUOTE = 0; private final int STRING_WITH_QUOTE = 1; private final int STRING_LONG_VALUE = 2; private final void internalPutSimpleAsciiString(String s, int flags) { boolean quote = (flags & STRING_WITH_QUOTE) != 0; boolean isLongValue = (flags & STRING_LONG_VALUE) != 0; int n = s.length(); if (isLongValue) n++; if (quote) n += 2; ensureRemaining(n); if (quote) buf.put((byte) '\"'); for (int i = 0; i < s.length(); i++) { buf.put((byte) s.charAt(i)); } if (isLongValue) buf.put((byte) '.'); if (quote) buf.put((byte) '\"'); } public boolean getBoolean() { char c = nextJsonChar(true); boolean v = c == 't'; internalSkip(v ? 3 : 4); // skip <rue> or <alse> return v; } public char getChar() { String s = getString(); return s != null && s.length() != 0 ? s.charAt(0) : 0; } public short getShort() { return (short) getInt(); } public int getInt() { int v = 0; int c = 0; boolean neg = false; for (;;) { c = nextJsonChar(true); if (c >= '0' && c <= '9') { v *= 10; v += (int) (c - '0'); } else if (c == '+') { } else if (c == '-') { neg = true; } else { oneCharBack(); break; } } return neg ? -v : v; } public long getLong() { long v = 0; int c = 0; boolean neg = false; for (;;) { c = nextJsonChar(true); if (c >= '0' && c <= '9') { v *= 10; v += (int) (c - '0'); } else if (c == '\'' || c == '\"' || c == '.') { } else if (c == '+') { } else if (c == '-') { neg = true; } else { oneCharBack(); break; } } return neg ? -v : v; } public float getFloat() { return (float) getDouble(); } public double getDouble() { StringBuilder sbuf = new StringBuilder(30); for (;;) { char c = nextJsonChar(true); if ((c >= '0' && c <= '9') || (c == '.') || (c == 'e') || (c == 'E') || (c == '-') || (c == '+')) { sbuf.append(c); } else if (c == 'N') { internalSkip(2); nextJsonChar(false); // update this.lastChar return Double.NaN; } else if (c == 'I') { boolean neg = sbuf.length() != 0 && sbuf.charAt(0) == '-'; internalSkip(7); nextJsonChar(false); // update this.lastChar return neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; } else { oneCharBack(); break; } } return Double.parseDouble(sbuf.toString()); } public String getString() { StringBuilder sbuf = new StringBuilder(); char quote = nextJsonChar(true); if (quote == '\"' || quote == '\'') { boolean esc = false; for (;;) { int c = buf.get() & 0xFF; if ((c & 0xE0) == 0xE0) { int v = (c & 0xF) << 12; c = buf.get() & 0xFF; v |= (c & 0x3F) << 6; c = buf.get() & 0xFF; v |= (c & 0x3F); sbuf.append((char) v); } else if ((c & 0xC0) == 0xC0) { int v = (c & 0x1F) << 6; c = buf.get() & 0xFF; v |= (c & 0x3F); sbuf.append((char) v); } else { if (!esc && c == '\\') { esc = true; } else { if (esc && c == 't') { sbuf.append('\t'); } else if (esc && c == 'r') { sbuf.append('\r'); } else if (esc && c == 'n') { sbuf.append('\n'); } else if (esc && c == '\\') { sbuf.append('\\'); } else if (esc && c == 'u') { // \u0001 int n = buf.getInt(); StringBuilder sn = new StringBuilder(4); sn.append((char) ((n >> 24) & 0xFF)); sn.append((char) ((n >> 16) & 0xFF)); sn.append((char) ((n >> 8) & 0xFF)); sn.append((char) ((n >> 0) & 0xFF)); sbuf.append((char) Integer.parseInt(sn.toString(), 16)); } else if (c == '\"' || c == '\'') { if (!esc && c == quote) { break; } sbuf.append((char) c); } else { sbuf.append((char) c); } esc = false; } } } } else { // null buf.position(buf.position() + 3); return ""; // null-values for Strings are not supported } return sbuf.toString(); } private void internalPutDate(String name, Date date) { if (date != null) { putJsonValueAscii(name, dateFormats[0].format(date), STRING_WITH_QUOTE); } else { putJsonValueAscii(name, "null", STRING_WITHOUT_QUOTE); } } public void putDate(Date date) { internalPutDate(null, date); } public void putDate(String name, Date date) { internalPutDate(name, date); } public static Date toDate(Object value) { Date date = null; if (value != null) { String svalue = (String) value; if (svalue.length() != 0) { for (SimpleDateFormat df : dateFormats) { try { date = df.parse(svalue); break; } catch (ParseException ignored) { } } } } return date; } public Date getDate() { return toDate(getString()); } public void putTypeId(int v) { putInt("_typeId", v); } public BBufferJson flip() { buf.flip(); return this; } private void internalSkip(int size) { buf.position(buf.position() + size); } public int position() { return buf.position(); } private final void ensureRemaining(int size) { if (buf.position() + size > buf.capacity()) { int cap = Math.max(buf.capacity() << 1, buf.position() + size); ByteBuffer nbuf = ByteBuffer.allocate(cap); nbuf.order(buf.order()); buf.flip(); nbuf.put(buf); buf = nbuf; } } @Override public String toString() { return buf.toString(); } private char lastChar; public char nextJsonChar(boolean eat) { char c = 0; try { for (; buf.remaining() != 0;) { int v = eat ? buf.get() : buf.get(buf.position()); c = (char) (v & 0xFF); switch (c) { case ' ': case '\n': case '\r': case '\t': break; default: { lastChar = c; return c; } } } } catch (BufferUnderflowException e) { c = 0; } lastChar = c; return c; } public void nextExpectedJsonChar(char expectedChar, boolean eat) throws BException { char c = nextJsonChar(eat); if (c != expectedChar) { int pos = eat ? buf.position() : (buf.position() - 1); BException e = new BException(BExceptionC.CORRUPT, "Expecting character " + expectedChar + " at position " + pos); log.error(e); log.info(this.toDetailString()); throw e; } } public void oneCharBack() { int p = buf.position(); if (p != 0) { buf.position(p - 1); } } public int getLastChar() { return lastChar; } private BJsonObject parseJsonObject() throws BException { char c = nextJsonChar(true); if (c == '[') { return parseJsonArray(); } else if (c == '{') { HashMap<String, Object> map = new HashMap<String, Object>(); c = nextJsonChar(false); if (c == '}') { internalSkip(1); } else { while (c != '}') { if (c != '\"' && c != '\'' && c != ',') { BException e = new BException(BExceptionC.CORRUPT, "Unexpected character " + c + "(" + Integer.toString((int) c & 0xFFFF) + ") at position " + buf.position() + ", expecting element name or array value."); log.error(e); String s = this.toDetailString(); log.info(s); throw e; } String key = getString(); if (key == null) { BException e = new BException(BExceptionC.CORRUPT, "Expecting element name at position " + buf.position()); log.error(e); log.info(this.toDetailString()); throw e; } // key.length() == 0 fr eine Map mit key-Wert="" nextExpectedJsonChar(':', true); Object value = parseJsonValue(); map.put(key, value); c = nextJsonChar(true); } } return new BJsonObject(map); } else { return new BJsonObject(); } } public Object parseJsonValue() throws BException { Object value = null; char c = nextJsonChar(false); if (c == 0) { } else if (c == '[') { value = parseJsonArray(); } else if (c == '{') { value = parseJsonObject(); } else if (c == '\"' || c == '\'') { value = getString(); } else if ((c >= '0' && c <= '9') || (c == '-') || (c == '+') || (c == '.') || (c == 'N') || (c == 'I')) { value = getDouble(); } else if (c == 't' || c == 'f') { value = getBoolean(); } else if (c == 'n') { // null buf.position(buf.position() + 4); } else if (c == 'u') { // undefined buf.position(buf.position() + 9); } else { BException e = new BException(BExceptionC.CORRUPT, "Unexpected character " + c + " at position " + buf.position()); log.error(e); log.info(this.toString()); throw e; } return value; } private BJsonObject parseJsonArray() throws BException { ArrayList<Object> arr = new ArrayList<Object>(); nextExpectedJsonChar('[', true); char c = nextJsonChar(false); if (c == ']') { internalSkip(1); } else { while (c != ']' && c != 0) { Object value = parseJsonValue(); arr.add(value); c = nextJsonChar(true); } } return new BJsonObject(arr); } /** * Long values are written as strings with a "." as suffix. * The segments are ORed together. * E.g. longValue = "1.2.4.8." represents value 15. * @param s * @return * @throws NumberFormatException */ public static long parseLong(String s) throws NumberFormatException { long ret = 0; if (s != null && s.length() != 0) { String[] arr = s.split("\\."); for (int i = 0; i < arr.length; i++) { if (arr[i].length() == 0) continue; try { ret |= Long.parseLong(arr[i]); } catch (NumberFormatException e) { if (!s.startsWith("0x")) throw e; s = s.substring(2); ret |= Long.valueOf(s, 16); } } } return ret; } private static SimpleDateFormat[] dateFormats; static { try { dateFormats = new SimpleDateFormat[] { // JSON Date format e.g.: 2013-11-09T20:35:16.596Z new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"), new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"), new SimpleDateFormat("yyyy-MM-dd"), }; for (SimpleDateFormat df : dateFormats) { df.setTimeZone(TimeZone.getTimeZone("UTC")); } } catch (Throwable e) { System.err.println(e); } } private static Log log = LogFactory.getLog(BBufferJson.class); } //2,5 times slower for strings with 30 characters. //public void putString1(String s) { // ensureRemaining(s.length() << 1); // char[] chars = s.toCharArray(); // for(char c : chars) buf.putChar(c); //} // //Best for strings shorter 10 characters. 3 times slower for strings with 30 characters. //public void putString2(String s) { // ensureRemaining(s.length() << 1); // for(int i = 0; i < s.length(); i++) buf.putChar(s.charAt(i)); //} // //3 times slower for strings with 30 characters. Conversion to UTF-8 is faster than to UTF-16. //public void putString3(String s) { // ensureRemaining(s.length() << 1); // try { // byte[] bytes = s.getBytes(buf.order() == ByteOrder.LITTLE_ENDIAN ? "UTF-16LE" : "UTF-16BE"); // buf.put(bytes); // } catch (UnsupportedEncodingException e) { // } //} //