pax_global_header00006660000000000000000000000064131114366200014507gustar00rootroot0000000000000052 comment=7d4eb41583ea0011209ea85f674bca310972ef54 noggit-noggit-0.8/000077500000000000000000000000001311143662000141325ustar00rootroot00000000000000noggit-noggit-0.8/.gitignore000077500000000000000000000000471311143662000161260ustar00rootroot00000000000000.classpath .project .settings targetnoggit-noggit-0.8/LICENSE.txt000077500000000000000000000261351311143662000157670ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. noggit-noggit-0.8/README.md000077500000000000000000000026041311143662000154160ustar00rootroot00000000000000N o g g i t =========== Noggit is the world's fastest streaming JSON parser for Java. Features: --------- - Fast! Measured as the fastest JSON parser on char[], String input. - Streaming API (StAX/pull-parser like) for both easy and efficient parsing. - Conforms to JSON standard: http://www.ietf.org/rfc/rfc4627.txt - Conforms to JSON standard: http://rfc7159.net/rfc7159 - Memory efficiency: - Incremental parsing (Reader-based) in order to handle huge messages. - A single byte of state needed per nested object or array. - Doesn't read large objects (including primitives) into memory unless asked. - Can eliminate most copying, allowing user to provide value output buffers. - Can handle primitives of any size (does not attempt to parse numerics into a certain language primitive unless asked). - Simple serialization of objects (List, Map, etc). - Optional creation of objects (List, Map, etc) when parsing. Syntax Features (Optional): --------------------------- - Single-line comments using either # or // - Multi-line comments using C style /* comments in here */ - Single quoted strings. - Unquoted object keys. Example: {answer : 42} - Unquoted string values. Example: {first: Yonik, last: Seeley} - Allow backslash escaping of any character. - Allow trailing commas and extra commas. Example: [9,4,3,] - Handle nbsp (non-break space, \u00a0) as whitespace. noggit-noggit-0.8/pom.xml000077500000000000000000000040571311143662000154600ustar00rootroot00000000000000 4.0.0 org.noggit noggit jar 0.8 Noggit http://github.com/yonik/noggit Noggit is the world's fastest streaming JSON parser for Java. org.sonatype.oss oss-parent 7 Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt repo scm:git:git@github.com:yonik/noggit.git scm:git:git@github.com:yonik/noggit.git git@github.com:yonik/noggit.git yseeley@gmail.com Yonik Seeley https://github.com/yonik yonik UTF-8 junit junit 3.8.1 test org.apache.maven.plugins maven-compiler-plugin 3.1 1.5 1.5 org.apache.maven.plugins maven-eclipse-plugin 2.9 noggit-noggit-0.8/src/000077500000000000000000000000001311143662000147215ustar00rootroot00000000000000noggit-noggit-0.8/src/main/000077500000000000000000000000001311143662000156455ustar00rootroot00000000000000noggit-noggit-0.8/src/main/java/000077500000000000000000000000001311143662000165665ustar00rootroot00000000000000noggit-noggit-0.8/src/main/java/org/000077500000000000000000000000001311143662000173555ustar00rootroot00000000000000noggit-noggit-0.8/src/main/java/org/noggit/000077500000000000000000000000001311143662000206445ustar00rootroot00000000000000noggit-noggit-0.8/src/main/java/org/noggit/CharArr.java000077500000000000000000000202021311143662000230300ustar00rootroot00000000000000/** * 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.noggit; import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.nio.CharBuffer; // CharArr origins // V1.0 7/06/97 // V1.1 9/21/99 // V1.2 2/02/04 // Java5 features // V1.3 11/26/06 // Make safe for Java 1.4, work into Noggit // @author yonik // Java5 version could look like the following: // public class CharArr implements CharSequence, Appendable, Readable, Closeable { /** * @author yonik * @version $Id: CharArr.java 583538 2007-10-10 16:53:02Z yonik $ */ public class CharArr implements CharSequence, Appendable { protected char[] buf; protected int start; protected int end; public CharArr() { this(32); } public CharArr(int size) { buf = new char[size]; } public CharArr(char[] arr, int start, int end) { set(arr,start,end); } public void setStart(int start) { this.start = start; } public void setEnd(int end) { this.end = end; } public void set(char[] arr, int start, int end) { this.buf = arr; this.start = start; this.end = end; } public char[] getArray() { return buf; } public int getStart() { return start; } public int getEnd() { return end; } public int size() { return end-start; } @Override public int length() { return size(); } /** The capacity of the buffer when empty (getArray().size()) */ public int capacity() { return buf.length; } @Override public char charAt(int index) { return buf[start+index]; } @Override public CharArr subSequence(int start, int end) { return new CharArr(buf, this.start+start, this.start+end); } public int read() throws IOException { if (start>=end) return -1; return buf[start++]; } public int read(char cbuf[], int off, int len) { //TODO return 0; } public void unsafeWrite(char b) { buf[end++] = b; } public void unsafeWrite(int b) { unsafeWrite((char)b); } public void unsafeWrite(char b[], int off, int len) { System.arraycopy(b, off, buf, end, len); end += len; } protected void resize(int len) { char newbuf[] = new char[Math.max(buf.length << 1, len)]; System.arraycopy(buf, start, newbuf, 0, size()); buf = newbuf; } public void reserve(int num) { if (end + num > buf.length) resize(end + num); } public void write(char b) { if (end >= buf.length) { resize(end+1); } unsafeWrite(b); } public final void write(int b) { write((char)b); } public final void write(char[] b) { write(b,0,b.length); } public void write(char b[], int off, int len) { reserve(len); unsafeWrite(b, off, len); } public final void write(CharArr arr) { write(arr.buf, arr.start, arr.end-arr.start); } public final void write(String s) { write(s, 0, s.length()); } public void write(String s, int stringOffset, int len) { reserve(len); s.getChars(stringOffset, len, buf, end); end += len; } public void flush() { } public final void reset() { start = end = 0; } public void close() { } public char[] toCharArray() { char newbuf[] = new char[size()]; System.arraycopy(buf, start, newbuf, 0, size()); return newbuf; } @Override public String toString() { return new String(buf, start, size()); } public int read(CharBuffer cb) throws IOException { /*** int sz = size(); if (sz<=0) return -1; if (sz>0) cb.put(buf, start, sz); return -1; ***/ int sz = size(); if (sz>0) cb.put(buf, start, sz); start=end; while (true) { fill(); int s = size(); if (s==0) return sz==0 ? -1 : sz; sz += s; cb.put(buf, start, s); } } public int fill() throws IOException { return 0; // or -1? } //////////////// Appendable methods ///////////// @Override public final Appendable append(CharSequence csq) throws IOException { return append(csq, 0, csq.length()); } @Override public Appendable append(CharSequence csq, int start, int end) throws IOException { write(csq.subSequence(start, end).toString()); return null; } @Override public final Appendable append(char c) throws IOException { write(c); return this; } } class NullCharArr extends CharArr { public NullCharArr() { super(new char[1],0,0); } @Override public void unsafeWrite(char b) {} @Override public void unsafeWrite(char b[], int off, int len) {} @Override public void unsafeWrite(int b) {} @Override public void write(char b) {} @Override public void write(char b[], int off, int len) {} @Override public void reserve(int num) {} @Override protected void resize(int len) {} @Override public Appendable append(CharSequence csq, int start, int end) throws IOException { return this; } @Override public char charAt(int index) { return 0; } @Override public void write(String s, int stringOffset, int len) { } } // IDEA: a subclass that refills the array from a reader? class CharArrReader extends CharArr { protected final Reader in; public CharArrReader(Reader in, int size) { super(size); this.in = in; } @Override public int read() throws IOException { if (start>=end) fill(); return start>=end ? -1 : buf[start++]; } @Override public int read(CharBuffer cb) throws IOException { // empty the buffer and then read direct int sz = size(); if (sz>0) cb.put(buf,start,end); int sz2 = in.read(cb); if (sz2>=0) return sz+sz2; return sz>0 ? sz : -1; } @Override public int fill() throws IOException { if (start>=end) { reset(); } else if (start>0) { System.arraycopy(buf, start, buf, 0, size()); end=size(); start=0; } /*** // fill fully or not??? do { int sz = in.read(buf,end,buf.length-end); if (sz==-1) return; end+=sz; } while (end < buf.length); ***/ int sz = in.read(buf,end,buf.length-end); if (sz>0) end+=sz; return sz; } } class CharArrWriter extends CharArr { protected Writer sink; @Override public void flush() { try { sink.write(buf, start, end-start); } catch (IOException e) { throw new RuntimeException(e); } start = end = 0; } @Override public void write(char b) { if (end >= buf.length) { flush(); } unsafeWrite(b); } @Override public void write(char b[], int off, int len) { int space = buf.length - end; if (len < space) { unsafeWrite(b, off, len); } else if (len < buf.length) { unsafeWrite(b, off, space); flush(); unsafeWrite(b, off+space, len-space); } else { flush(); try { sink.write(b, off, len); } catch (IOException e) { throw new RuntimeException(e); } } } @Override public void write(String s, int stringOffset, int len) { int space = buf.length - end; if (len < space) { s.getChars(stringOffset, stringOffset+len, buf, end); end += len; } else if (len < buf.length) { // if the data to write is small enough, buffer it. s.getChars(stringOffset, stringOffset+space, buf, end); flush(); s.getChars(stringOffset+space, stringOffset+len, buf, 0); end = len-space; } else { flush(); // don't buffer, just write to sink try { sink.write(s, stringOffset, len); } catch (IOException e) { throw new RuntimeException(e); } } } } noggit-noggit-0.8/src/main/java/org/noggit/CharUtil.java000077500000000000000000000032341311143662000232270ustar00rootroot00000000000000/** * Copyright 2006- Yonik Seeley * * 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.noggit; /** * @author yonik * @version $Id: CharUtil.java 479919 2006-11-28 05:53:55Z yonik $ */ public class CharUtil { // belongs in number utils or charutil? public long parseLong(char[] arr, int start, int end) { long x = 0; boolean negative = arr[start] == '-'; for (int i=negative ? start+1 : start; i=0) { int c = a[a_start] - b[b_start]; if (c!=0) return c; a_start++; b_start++; } return a_len-b_len; } } noggit-noggit-0.8/src/main/java/org/noggit/JSONParser.java000077500000000000000000001106271311143662000234470ustar00rootroot00000000000000/** * Copyright 2006- Yonik Seeley * * 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.noggit; import java.io.IOException; import java.io.Reader; /** * @author yonik * @version $Id: JSONParser.java 1099557 2011-05-04 18:54:26Z yonik $ */ public class JSONParser { /** Event indicating a JSON string value, including member names of objects */ public static final int STRING=1; /** Event indicating a JSON number value which fits into a signed 64 bit integer */ public static final int LONG=2; /** Event indicating a JSON number value which has a fractional part or an exponent * and with string length <= 23 chars not including sign. This covers * all representations of normal values for Double.toString(). */ public static final int NUMBER=3; /** Event indicating a JSON number value that was not produced by toString of any * Java primitive numerics such as Double or Long. It is either * an integer outside the range of a 64 bit signed integer, or a floating * point value with a string representation of more than 23 chars. */ public static final int BIGNUMBER=4; /** Event indicating a JSON boolean */ public static final int BOOLEAN=5; /** Event indicating a JSON null */ public static final int NULL=6; /** Event indicating the start of a JSON object */ public static final int OBJECT_START=7; /** Event indicating the end of a JSON object */ public static final int OBJECT_END=8; /** Event indicating the start of a JSON array */ public static final int ARRAY_START=9; /** Event indicating the end of a JSON array */ public static final int ARRAY_END=10; /** Event indicating the end of input has been reached */ public static final int EOF=11; /** Flags to control parsing behavior */ public static final int ALLOW_COMMENTS = 1 << 0; public static final int ALLOW_SINGLE_QUOTES = 1 << 1; public static final int ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER = 1 << 2; public static final int ALLOW_UNQUOTED_KEYS = 1 << 3; public static final int ALLOW_UNQUOTED_STRING_VALUES = 1 << 4; /** ALLOW_EXTRA_COMMAS causes any number of extra commas in arrays and objects to be ignored * Note that a trailing comma in [] would be [,] (hence calling the feature "trailing" commas * is either limiting or misleading. Since trailing commas is fundamentally incompatible with any future * "fill-in-missing-values-with-null", it was decided to extend this feature to handle any * number of extra commas. */ public static final int ALLOW_EXTRA_COMMAS = 1 << 5; public static final int ALLOW_MISSING_COLON_COMMA_BEFORE_OBJECT = 1 << 6; public static final int OPTIONAL_OUTER_BRACES = 1 << 7; public static final int FLAGS_STRICT = 0; public static final int FLAGS_DEFAULT = ALLOW_COMMENTS | ALLOW_SINGLE_QUOTES | ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER | ALLOW_UNQUOTED_KEYS | ALLOW_UNQUOTED_STRING_VALUES | ALLOW_EXTRA_COMMAS; public static class ParseException extends RuntimeException { public ParseException(String msg) { super(msg); } } public static String getEventString( int e ) { switch( e ) { case STRING: return "STRING"; case LONG: return "LONG"; case NUMBER: return "NUMBER"; case BIGNUMBER: return "BIGNUMBER"; case BOOLEAN: return "BOOLEAN"; case NULL: return "NULL"; case OBJECT_START: return "OBJECT_START"; case OBJECT_END: return "OBJECT_END"; case ARRAY_START: return "ARRAY_START"; case ARRAY_END: return "ARRAY_END"; case EOF: return "EOF"; } return "Unknown: "+e; } private static final CharArr devNull = new NullCharArr(); protected int flags = FLAGS_DEFAULT; protected final char[] buf; // input buffer with JSON text in it protected int start; // current position in the buffer protected int end; // end position in the buffer (one past last valid index) protected final Reader in; // optional reader to obtain data from protected boolean eof=false; // true if the end of the stream was reached. protected long gpos; // global position = gpos + start protected int event; // last event read protected int stringTerm; // The terminator for the last string we read: single quote, double quote, or 0 for unterminated. protected boolean missingOpeningBrace = false; public JSONParser(Reader in) { this(in, new char[8192]); // 8192 matches the default buffer size of a BufferedReader so double // buffering of the data is avoided. } public JSONParser(Reader in, char[] buffer) { this.in = in; this.buf = buffer; } // idea - if someone passes us a CharArrayReader, we could // directly use that buffer as it's protected. public JSONParser(char[] data, int start, int end) { this.in = null; this.buf = data; this.start = start; this.end = end; } public JSONParser(String data) { this(data, 0, data.length()); } public JSONParser(String data, int start, int end) { this.in = null; this.start = start; this.end = end; this.buf = new char[end-start]; data.getChars(start,end,buf,0); } public int getFlags() { return flags; } public int setFlags(int flags) { int oldFlags = flags; this.flags = flags; return oldFlags; } // temporary output buffer private final CharArr out = new CharArr(64); // We need to keep some state in order to (at a minimum) know if // we should skip ',' or ':'. private byte[] stack = new byte[16]; private int ptr=0; // pointer into the stack of parser states private byte state=0; // current parser state // parser states stored in the stack private static final byte DID_OBJSTART =1; // '{' just read private static final byte DID_ARRSTART =2; // '[' just read private static final byte DID_ARRELEM =3; // array element just read private static final byte DID_MEMNAME =4; // object member name (map key) just read private static final byte DID_MEMVAL =5; // object member value (map val) just read // info about value that was just read (or is in the middle of being read) private int valstate; // push current parser state (use at start of new container) private final void push() { if (ptr >= stack.length) { // doubling here is probably overkill, but anything that needs to double more than // once (32 levels deep) is very atypical anyway. byte[] newstack = new byte[stack.length<<1]; System.arraycopy(stack,0,newstack,0,stack.length); stack = newstack; } stack[ptr++] = state; } // pop parser state (use at end of container) private final void pop() { if (--ptr<0) { throw err("Unbalanced container"); } else { state = stack[ptr]; } } protected void fill() throws IOException { if (in!=null) { gpos += end; start=0; int num = in.read(buf,0,buf.length); end = num>=0 ? num : 0; } if (start>=end) eof=true; } private void getMore() throws IOException { fill(); if (start>=end) { throw err(null); } } protected int getChar() throws IOException { if (start>=end) { fill(); if (start>=end) return -1; } return buf[start++]; } /** Returns true if the given character is considered to be whitespace. * One difference between Java's Character.isWhitespace() is that this method * considers a hard space (non-breaking space, or nbsp) to be whitespace. */ protected static final boolean isWhitespace(int ch) { return (Character.isWhitespace(ch) || ch==0x00a0); } private static final long WS_MASK=(1L<<' ')|(1L<<'\t')|(1L<<'\r')|(1L<<'\n')|(1L<<'#')|(1L<<'/')|(0x01); // set 1 bit so 0xA0 will be flagged as whitespace protected int getCharNWS() throws IOException { for (;;) { int ch = getChar(); // getCharNWS is normally called in the context of expecting certain JSON special characters // such as ":}"]," // all of these characters are below 64 (including comment chars '/' and '#', so we can make this the fast path // even w/o checking the range first. We'll only get some false-positives while using bare strings (chars "IJMc") if (((WS_MASK >> ch) & 0x01) == 0) { return ch; } else if (ch <= ' ') { // this will only be true if one of the whitespace bits was set continue; } else if (ch=='/') { getSlashComment(); } else if (ch=='#') { getNewlineComment(); } else if (!isWhitespace(ch)) { // we'll only reach here with certain bare strings, errors, or strange whitespace like 0xa0 return ch; } /*** // getCharNWS is normally called in the context of expecting certain JSON special characters // such as ":}"]," // all of these characters are below 64 (including comment chars '/' and '#', so we can make this the fast path if (ch < 64) { if (((WS_MASK >> ch) & 0x01) == 0) return ch; if (ch <= ' ') continue; // whitespace below a normal space if (ch=='/') { getSlashComment(); } else if (ch=='#') { getNewlineComment(); } } else if (!isWhitespace(ch)) { // check for higher whitespace like 0xA0 return ch; } ***/ /** older code switch (ch) { case ' ' : case '\t' : case '\r' : case '\n' : continue outer; case '#' : getNewlineComment(); continue outer; case '/' : getSlashComment(); continue outer; default: return ch; } **/ } } protected int getCharNWS(int ch) throws IOException { for (;;) { // getCharNWS is normally called in the context of expecting certain JSON special characters // such as ":}"]," // all of these characters are below 64 (including comment chars '/' and '#', so we can make this the fast path // even w/o checking the range first. We'll only get some false-positives while using bare strings (chars "IJMc") if (((WS_MASK >> ch) & 0x01) == 0) { return ch; } else if (ch <= ' ') { // this will only be true if one of the whitespace bits was set // whitespace... get new char at bottom of loop } else if (ch == '/') { getSlashComment(); } else if (ch == '#') { getNewlineComment(); } else if (!isWhitespace(ch)) { // we'll only reach here with certain bare strings, errors, or strange whitespace like 0xa0 return ch; } ch = getChar(); } } protected int getCharExpected(int expected) throws IOException { for(;;) { int ch = getChar(); if (ch==expected) return expected; if (ch==' ') continue; return getCharNWS(ch); } } protected void getNewlineComment() throws IOException { // read a # or a //, so go until newline for (;;) { int ch = getChar(); // don't worry about DOS /r/n... we'll stop on the \r and let the rest of the whitespace // eater consume the \n if (ch == '\n' || ch == '\r' || ch == -1) { return; } } } protected void getSlashComment() throws IOException { int ch = getChar(); if (ch == '/') { getNewlineComment(); return; } if (ch != '*') { throw err("Invalid comment: expected //, /*, or #"); } ch = getChar(); for (;;) { if (ch == '*') { ch = getChar(); if (ch == '/') { return; } else if (ch == '*') { // handle cases of *******/ continue; } } if (ch == -1) { return; } ch = getChar(); } } protected boolean matchBareWord(char[] arr) throws IOException { for (int i=1; i0) start--; // backup one char String chs = "char=" + ((start>=end) ? "(EOF)" : "" + buf[start]); String pos = "position=" + (gpos+start); String tot = chs + ',' + pos + getContext(); if (msg==null) { if (start>=end) msg = "Unexpected EOF"; else msg="JSON Parse Error"; } return new ParseException(msg + ": " + tot); } private String getContext() { String context = ""; if (start>=0) { context += " AFTER='" + errEscape(Math.max(start - 60, 0), start + 1) + "'"; } if (start=b) return ""; return new String(buf, a, b-a).replaceAll("\\s+"," "); } private boolean bool; // boolean value read private long lval; // long value read private int nstate; // current state while reading a number private static final int HAS_FRACTION = 0x01; // nstate flag, '.' already read private static final int HAS_EXPONENT = 0x02; // nstate flag, '[eE][+-]?[0-9]' already read /** Returns the long read... only significant if valstate==LONG after * this call. firstChar should be the first numeric digit read. */ private long readNumber(int firstChar, boolean isNeg) throws IOException { out.unsafeWrite(firstChar); // unsafe OK since we know output is big enough // We build up the number in the negative plane since it's larger (by one) than // the positive plane. long v = '0' - firstChar; // can't overflow a long in 18 decimal digits (i.e. 17 additional after the first). // we also need 22 additional to handle double so we'll handle in 2 separate loops. int i; for (i=0; i<17; i++) { int ch = getChar(); // TODO: is this switch faster as an if-then-else? switch(ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': v = v*10 - (ch-'0'); out.unsafeWrite(ch); continue; case '.': out.unsafeWrite('.'); valstate = readFrac(out,22-i); return 0; case 'e': case 'E': out.unsafeWrite(ch); nstate=0; valstate = readExp(out,22-i); return 0; default: // return the number, relying on nextEvent() to return an error // for invalid chars following the number. if (ch!=-1) --start; // push back last char if not EOF valstate = LONG; return isNeg ? v : -v; } } // after this, we could overflow a long and need to do extra checking boolean overflow = false; long maxval = isNeg ? Long.MIN_VALUE : -Long.MAX_VALUE; for (; i<22; i++) { int ch = getChar(); switch(ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (v < (0x8000000000000000L/10)) overflow=true; // can't multiply by 10 w/o overflowing v *= 10; int digit = ch - '0'; if (v < maxval + digit) overflow=true; // can't add digit w/o overflowing v -= digit; out.unsafeWrite(ch); continue; case '.': out.unsafeWrite('.'); valstate = readFrac(out,22-i); return 0; case 'e': case 'E': out.unsafeWrite(ch); nstate=0; valstate = readExp(out,22-i); return 0; default: // return the number, relying on nextEvent() to return an error // for invalid chars following the number. if (ch!=-1) --start; // push back last char if not EOF valstate = overflow ? BIGNUMBER : LONG; return isNeg ? v : -v; } } nstate=0; valstate = BIGNUMBER; return 0; } // read digits right of decimal point private int readFrac(CharArr arr, int lim) throws IOException { nstate = HAS_FRACTION; // deliberate set instead of '|' while(--lim>=0) { int ch = getChar(); if (ch>='0' && ch<='9') { arr.write(ch); } else if (ch=='e' || ch=='E') { arr.write(ch); return readExp(arr,lim); } else { if (ch!=-1) start--; // back up return NUMBER; } } return BIGNUMBER; } // call after 'e' or 'E' has been seen to read the rest of the exponent private int readExp(CharArr arr, int lim) throws IOException { nstate |= HAS_EXPONENT; int ch = getChar(); lim--; if (ch=='+' || ch=='-') { arr.write(ch); ch = getChar(); lim--; } // make sure at least one digit is read. if (ch<'0' || ch>'9') { throw err("missing exponent number"); } arr.write(ch); return readExpDigits(arr,lim); } // continuation of readExpStart private int readExpDigits(CharArr arr, int lim) throws IOException { while (--lim>=0) { int ch = getChar(); if (ch>='0' && ch<='9') { arr.write(ch); } else { if (ch!=-1) start--; // back up return NUMBER; } } return BIGNUMBER; } private void continueNumber(CharArr arr) throws IOException { if (arr != out) arr.write(out); if ((nstate & HAS_EXPONENT)!=0){ readExpDigits(arr, Integer.MAX_VALUE); return; } if (nstate != 0) { readFrac(arr, Integer.MAX_VALUE); return; } for(;;) { int ch = getChar(); if (ch>='0' && ch <='9') { arr.write(ch); } else if (ch=='.') { arr.write(ch); readFrac(arr,Integer.MAX_VALUE); return; } else if (ch=='e' || ch=='E') { arr.write(ch); readExp(arr,Integer.MAX_VALUE); return; } else { if (ch!=-1) start--; return; } } } private int hexval(int hexdig) { if (hexdig>='0' && hexdig <='9') { return hexdig-'0'; } else if (hexdig>='A' && hexdig <='F') { return hexdig+(10-'A'); } else if (hexdig>='a' && hexdig <='f') { return hexdig+(10-'a'); } throw err("invalid hex digit"); } // backslash has already been read when this is called private char readEscapedChar() throws IOException { int ch = getChar(); switch (ch) { case '"' : return '"'; case '\'' : return '\''; case '\\' : return '\\'; case '/' : return '/'; case 'n' : return '\n'; case 'r' : return '\r'; case 't' : return '\t'; case 'f' : return '\f'; case 'b' : return '\b'; case 'u' : return (char)( (hexval(getChar()) << 12) | (hexval(getChar()) << 8) | (hexval(getChar()) << 4) | (hexval(getChar()))); } if ( (flags & ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER) != 0 && ch != EOF) { return (char)ch; } throw err("Invalid character escape"); } // a dummy buffer we can use to point at other buffers private final CharArr tmp = new CharArr(null,0,0); private CharArr readStringChars() throws IOException { if (stringTerm == 0) { // "out" will already contain the first part of the bare string, so don't reset it readStringBare(out); return out; } char terminator = (char) stringTerm; int i; for (i=start; i=end) { arr.write(buf,start,middle-start); start=middle; getMore(); middle=start; } int ch = buf[middle++]; if (ch == terminator) { int len = middle-start-1; if (len>0) arr.write(buf,start,len); start=middle; return; } else if (ch=='\\') { int len = middle-start-1; if (len>0) arr.write(buf,start,len); start=middle; arr.write(readEscapedChar()); middle=start; } } } private void readStringBare(CharArr arr) throws IOException { if (arr != out) { arr.append(out); } for(;;) { int ch = getChar(); if (!isUnquotedStringChar(ch)) { if (ch == -1) break; if (ch == '\\') { arr.write(readEscapedChar()); continue; } start--; break; } if (ch == '\\') { arr.write(readEscapedChar()); continue; } arr.write(ch); } } // isName==true if this is a field name (as opposed to a value) protected void handleNonDoubleQuoteString(int ch, boolean isName) throws IOException { if (ch == '\'') { stringTerm = ch; if ((flags & ALLOW_SINGLE_QUOTES) == 0) { throw err("Single quoted strings not allowed"); } } else { if (isName && (flags & ALLOW_UNQUOTED_KEYS) == 0 || !isName && (flags & ALLOW_UNQUOTED_STRING_VALUES) == 0 || eof) { if (isName) { throw err("Expected quoted string"); } else { throw err(null); } } if (!isUnquotedStringStart(ch)) { throw err(null); } stringTerm = 0; // signal for unquoted string out.reset(); out.unsafeWrite(ch); } } private static boolean isUnquotedStringStart(int ch) { return Character.isJavaIdentifierStart(ch); } // What characters are allowed to continue an unquoted string // once we know we are in one. private static boolean isUnquotedStringChar(int ch) { return Character.isJavaIdentifierPart(ch) || ch == '.' || ch == '-' || ch == '/'; // would checking for a-z first speed up the common case? // possibly much more liberal unquoted string handling... /*** switch (ch) { case -1: case ' ': case '\t': case '\r': case '\n': case '}': case ']': case ',': case ':': case '=': // reserved for future use case '\\': // check for backslash should come after this function call return false; } return true; ***/ } /*** alternate implementation // middle is the pointer to the middle of a buffer to start scanning for a non-string // character ('"' or "/"). start<=middle=end) { getMore(); middle=start; } else { start = middle+1; // set buffer pointer to correct spot if (ch=='"') { valstate=0; return; } else if (ch=='\\') { arr.write(readEscapedChar()); if (start>=end) getMore(); middle=start; } } } } ***/ // return the next event when parser is in a neutral state (no // map separators or array element separators to read private int next(int ch) throws IOException { // TODO: try my own form of indirect jump... look up char class and index directly into handling implementation? for(;;) { switch (ch) { case ' ': // this is not the exclusive list of whitespace chars... the rest are handled in default: case '\t': case '\r': case '\n': ch = getCharNWS(); // calling getCharNWS here seems faster than letting the switch handle it break; case '"' : stringTerm = '"'; valstate = STRING; return STRING; case '\'' : if ((flags & ALLOW_SINGLE_QUOTES) == 0) { throw err("Single quoted strings not allowed"); } stringTerm = '\''; valstate = STRING; return STRING; case '{' : push(); state= DID_OBJSTART; return OBJECT_START; case '[': push(); state=DID_ARRSTART; return ARRAY_START; case '0' : out.reset(); //special case '0'? If next char isn't '.' val=0 ch=getChar(); if (ch=='.') { start--; ch='0'; readNumber('0',false); return valstate; } else if (ch>'9' || ch<'0') { out.unsafeWrite('0'); if (ch!=-1) start--; lval = 0; valstate=LONG; return LONG; } else { throw err("Leading zeros not allowed"); } case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : case '8' : case '9' : out.reset(); lval = readNumber(ch,false); return valstate; case '-' : out.reset(); out.unsafeWrite('-'); ch = getChar(); if (ch<'0' || ch>'9') throw err("expected digit after '-'"); lval = readNumber(ch,true); return valstate; case 't': // TODO: test performance of this non-branching inline version. // if ((('r'-getChar())|('u'-getChar())|('e'-getChar())) != 0) throw err(""); if (matchBareWord(JSONUtil.TRUE_CHARS)) { bool = true; valstate = BOOLEAN; return valstate; } else { valstate = STRING; return STRING; } case 'f': if (matchBareWord(JSONUtil.FALSE_CHARS)) { bool = false; valstate = BOOLEAN; return valstate; } else { valstate = STRING; return STRING; } case 'n': if (matchBareWord(JSONUtil.NULL_CHARS)) { valstate = NULL; return valstate; } else { valstate = STRING; return STRING; } case '/': getSlashComment(); ch = getChar(); break; case '#': getNewlineComment(); ch = getChar(); break; case ']': // This only happens with a trailing comma (or an error) if (state != DID_ARRELEM || (flags & ALLOW_EXTRA_COMMAS)==0) { throw err("Unexpected array closer ]"); } pop(); return event = ARRAY_END; case '}': // This only happens with a trailing comma (or an error) if (state != DID_MEMVAL || (flags & ALLOW_EXTRA_COMMAS)==0) { throw err("Unexpected object closer }"); } pop(); return event = ARRAY_END; case ',': // This only happens with input like [1,] if ((state != DID_ARRELEM && state != DID_MEMVAL) || (flags & ALLOW_EXTRA_COMMAS)==0) { throw err("Unexpected comma"); } ch = getChar(); break; case -1: if (getLevel()>0) throw err("Premature EOF"); return EOF; default: // Handle unusual unicode whitespace like no-break space (0xA0) if (isWhitespace(ch)) { ch = getChar(); // getCharNWS() would also work break; } handleNonDoubleQuoteString(ch, false); valstate = STRING; return STRING; // throw err(null); } } } @Override public String toString() { return "start="+start+",end="+end+",state="+state+"valstate="+valstate; } /** Returns the next event encountered in the JSON stream, one of *
    *
  • {@link #STRING}
  • *
  • {@link #LONG}
  • *
  • {@link #NUMBER}
  • *
  • {@link #BIGNUMBER}
  • *
  • {@link #BOOLEAN}
  • *
  • {@link #NULL}
  • *
  • {@link #OBJECT_START}
  • *
  • {@link #OBJECT_END}
  • *
  • {@link #OBJECT_END}
  • *
  • {@link #ARRAY_START}
  • *
  • {@link #ARRAY_END}
  • *
  • {@link #EOF}
  • *
*/ public int nextEvent() throws IOException { if (valstate != 0) { if (valstate == STRING) { readStringChars2(devNull, start); } else if (valstate == BIGNUMBER) { continueNumber(devNull); } valstate = 0; } int ch; outer: for(;;) { switch (state) { case 0: event = next(getChar()); if (event == STRING && (flags & OPTIONAL_OUTER_BRACES) != 0) { if (start > 0) start--; missingOpeningBrace = true; stringTerm = 0; valstate = 0; event = next('{'); } return event; case DID_OBJSTART: ch = getCharExpected('"'); if (ch == '}') { pop(); return event = OBJECT_END; } if (ch == '"') { stringTerm = ch; } else if (ch == ',' && (flags & ALLOW_EXTRA_COMMAS) != 0) { continue outer; } else { handleNonDoubleQuoteString(ch, true); } state = DID_MEMNAME; valstate = STRING; return event = STRING; case DID_MEMNAME: ch = getCharExpected(':'); if (ch != ':') { if ((ch == '{' || ch == '[') && (flags & ALLOW_MISSING_COLON_COMMA_BEFORE_OBJECT) != 0) { start--; } else { throw err("Expected key,value separator ':'"); } } state = DID_MEMVAL; // set state first because it might be pushed... return event = next(getChar()); case DID_MEMVAL: ch = getCharExpected(','); if (ch == '}') { pop(); return event = OBJECT_END; } else if (ch != ',') { if ((flags & ALLOW_EXTRA_COMMAS) != 0 && (ch == '\'' || ch == '"' || Character.isLetter(ch))) { start--; } else if (missingOpeningBrace && ch == -1 && (flags & OPTIONAL_OUTER_BRACES) != 0) { missingOpeningBrace = false; pop(); return event = OBJECT_END; } else throw err("Expected ',' or '}'"); } ch = getCharExpected('"'); if (ch == '"') { stringTerm = ch; } else if ((ch == ',' || ch== '}') && (flags & ALLOW_EXTRA_COMMAS) != 0) { if (ch==',') continue outer; pop(); return event = OBJECT_END; } else { handleNonDoubleQuoteString(ch, true); } state = DID_MEMNAME; valstate = STRING; return event = STRING; case DID_ARRSTART: ch = getCharNWS(); if (ch == ']') { pop(); return event = ARRAY_END; } state = DID_ARRELEM; // set state first, might be pushed... return event = next(ch); case DID_ARRELEM: ch = getCharExpected(','); if (ch == ',') { // state = DID_ARRELEM; // redundant return event = next(getChar()); } else if (ch == ']') { pop(); return event = ARRAY_END; } else { if ((ch == '{' || ch == '[') && (flags & ALLOW_MISSING_COLON_COMMA_BEFORE_OBJECT) != 0) { return event = next(ch); } else { throw err("Expected ',' or ']'"); } } } } // end for(;;) } public int lastEvent() { return event; } public boolean wasKey() { return state == DID_MEMNAME; } private void goTo(int what) throws IOException { if (valstate==what) { valstate=0; return; } if (valstate==0) { /*int ev = */nextEvent(); // TODO if (valstate!=what) { throw err("type mismatch"); } valstate=0; } else { throw err("type mismatch"); } } /** Returns the JSON string value, decoding any escaped characters. */ public String getString() throws IOException { return getStringChars().toString(); } /** Returns the characters of a JSON string value, decoding any escaped characters. *

The underlying buffer of the returned CharArr should *not* be * modified as it may be shared with the input buffer. *

The returned CharArr will only be valid up until * the next JSONParser method is called. Any required data should be * read before that point. */ public CharArr getStringChars() throws IOException { goTo(STRING); return readStringChars(); } /** Reads a JSON string into the output, decoding any escaped characters. */ public void getString(CharArr output) throws IOException { goTo(STRING); readStringChars2(output,start); } /** Reads a number from the input stream and parses it as a long, only if * the value will in fact fit into a signed 64 bit integer. */ public long getLong() throws IOException { goTo(LONG); return lval; } /** Reads a number from the input stream and parses it as a double */ public double getDouble() throws IOException { return Double.parseDouble(getNumberChars().toString()); } /** Returns the characters of a JSON numeric value. *

The underlying buffer of the returned CharArr should *not* be * modified as it may be shared with the input buffer. *

The returned CharArr will only be valid up until * the next JSONParser method is called. Any required data should be * read before that point. */ public CharArr getNumberChars() throws IOException { int ev=0; if (valstate==0) ev = nextEvent(); if (valstate == LONG || valstate == NUMBER) { valstate=0; return out; } else if (valstate==BIGNUMBER) { continueNumber(out); valstate=0; return out; } else { throw err("Unexpected " + ev); } } /** Reads a JSON numeric value into the output. */ public void getNumberChars(CharArr output) throws IOException { int ev=0; if (valstate==0) ev=nextEvent(); if (valstate == LONG || valstate == NUMBER) output.write(this.out); else if (valstate==BIGNUMBER) { continueNumber(output); } else { throw err("Unexpected " + ev); } valstate=0; } /** Reads a boolean value */ public boolean getBoolean() throws IOException { goTo(BOOLEAN); return bool; } /** Reads a null value */ public void getNull() throws IOException { goTo(NULL); } /** * @return the current nesting level, the number of parent objects or arrays. */ public int getLevel() { return ptr; } public long getPosition() { return gpos+start; } } noggit-noggit-0.8/src/main/java/org/noggit/JSONParserWS.java000066400000000000000000000064121311143662000237120ustar00rootroot00000000000000/** * Copyright 2006- Yonik Seeley * * 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.noggit; import java.io.IOException; import java.io.Reader; public class JSONParserWS extends JSONParser { public static abstract class WhitespaceHandler { public abstract void whitespaceNotification(int state, CharArr whitespace, boolean containsComment); } private CharArr outWS = new CharArr(64); private WhitespaceHandler wsHandler = new WhitespaceHandler() { @Override public void whitespaceNotification(int state, CharArr whitespace, boolean containsComment) { System.out.println("state=" + state + " comment=" + containsComment + " ws="+whitespace.toString()); } }; public JSONParserWS(Reader in) { super(in); } public JSONParserWS(Reader in, char[] buffer) { super(in, buffer); } public JSONParserWS(char[] data, int start, int end) { super(data, start, end); } public JSONParserWS(String data) { super(data); } public JSONParserWS(String data, int start, int end) { super(data, start, end); } public void setWhitespaceHandler(WhitespaceHandler wsHandler) { this.wsHandler = wsHandler; } // TODO: use subclassing if handling comments is sufficiently slower? protected int getCharNWS() throws IOException { outWS.reset(); outer: for (;;) { int ch = getChar(); switch (ch) { case ' ' : case '\t' : case '\r' : case '\n' : outWS.write(ch); continue outer; case '#' : getNewlineComment(); continue outer; case '/' : getSlashComment(); continue outer; default: return ch; } } } protected void getNewlineComment() throws IOException { // read a # or a //, so go until newline for (;;) { int ch = getChar(); if (ch != -1) outWS.write(ch); // don't worry about DOS /r/n... we'll stop on the \r and let the rest of the whitespace // eater consume the \n if (ch == '\n' || ch == '\r' || ch == -1) { return; } } } protected void getSlashComment() throws IOException { int ch = getChar(); if (ch != -1) outWS.write(ch); if (ch == '/') { getNewlineComment(); return; } if (ch != '*') { throw err("Invalid comment: expected //, /*, or #"); } ch = getChar(); if (ch != -1) outWS.write(ch); for (;;) { if (ch == '*') { ch = getChar(); if (ch != -1) outWS.write(ch); if (ch == '/') { return; } else if (ch == '*') { // handle cases of *******/ continue; } } if (ch == -1) { return; } ch = getChar(); if (ch != -1) outWS.write(ch); } } } noggit-noggit-0.8/src/main/java/org/noggit/JSONUtil.java000077500000000000000000000137531311143662000231320ustar00rootroot00000000000000/** * Copyright 2006- Yonik Seeley * * 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.noggit; import java.util.Arrays; /** * @author yonik * @version $Id: JSONUtil.java 1209632 2011-12-02 18:48:42Z yonik $ */ public class JSONUtil { public static final char[] TRUE_CHARS = new char[] {'t','r','u','e'}; public static final char[] FALSE_CHARS = new char[] {'f','a','l','s','e'}; public static final char[] NULL_CHARS = new char[] {'n','u','l','l'}; public static final char[] HEX_CHARS = new char[] {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; public static final char VALUE_SEPARATOR = ','; public static final char NAME_SEPARATOR = ':'; public static final char OBJECT_START = '{'; public static final char OBJECT_END = '}'; public static final char ARRAY_START = '['; public static final char ARRAY_END = ']'; public static String toJSON(Object o) { CharArr out = new CharArr(); new JSONWriter(out).write(o); return out.toString(); } /** * @param o The object to convert to JSON * @param indentSize The number of space characters to use as an indent (default 2). 0=newlines but no spaces, -1=no indent at all. * @return */ public static String toJSON(Object o, int indentSize) { CharArr out = new CharArr(); new JSONWriter(out,indentSize).write(o); return out.toString(); } public static void writeNumber(int number, CharArr out) { out.write(Integer.toString(number)); } public static void writeNumber(long number, CharArr out) { out.write(Long.toString(number)); } public static void writeNumber(float number, CharArr out) { out.write(Float.toString(number)); } public static void writeNumber(double number, CharArr out) { out.write(Double.toString(number)); } public static void writeString(CharArr val, CharArr out) { writeString(val.getArray(), val.getStart(), val.getEnd(), out); } public static void writeString(char[] val, int start, int end, CharArr out) { out.write('"'); writeStringPart(val,start,end,out); out.write('"'); } public static void writeString(String val, int start, int end, CharArr out) { out.write('"'); writeStringPart(val,start,end,out); out.write('"'); } public static void writeString(CharSequence val, int start, int end, CharArr out) { out.write('"'); writeStringPart(val,start,end,out); out.write('"'); } public static void writeStringPart(char[] val, int start, int end, CharArr out) { for (int i=start; i=1f, (ch*146087937)&0xd6a01f80) is 0 only for characters that need escaping: " \\ u2028 u2029 // and has 7 false positives: 204a 4051 802f c022 c044 e04a e04b if (ch > 0x1f && ((ch * 146087937) & 0xd6a01f80) != 0) { out.write(ch); } else { writeChar(ch, out); } } } public static void writeChar(char ch, CharArr out) { switch(ch) { case '"': case '\\': out.write('\\'); out.write(ch); break; case '\r': out.write('\\'); out.write('r'); break; case '\n': out.write('\\'); out.write('n'); break; case '\t': out.write('\\'); out.write('t'); break; case '\b': out.write('\\'); out.write('b'); break; case '\f': out.write('\\'); out.write('f'); break; // case '/': case '\u2028': // valid JSON, but not valid json script case '\u2029': unicodeEscape(ch,out); break; default: if (ch <= 0x1F) { unicodeEscape(ch,out); } else { out.write(ch); } } } public static void writeStringPart(String chars, int start, int end, CharArr out) { // TODO: write in chunks? int toWrite = end - start; char[] arr = out.getArray(); int pos = out.getEnd(); int space = arr.length - pos; if (space < toWrite) { writeStringPart((CharSequence)chars, start, end, out); return; } // get chars directly from String into output array chars.getChars(start, end, arr, pos); int endInOut = pos + toWrite; out.setEnd(endInOut); for (int i=pos; i=1f, (ch*146087937)&0xd6a01f80) is 0 only for characters that need escaping: " \\ u2028 u2029 // and has 7 false positives: 204a 4051 802f c022 c044 e04a e04b if (ch<=0x1f || ((ch*146087937)&0xd6a01f80)==0 ) { // We hit a char that needs escaping. do the rest char by char. out.setEnd(i); writeStringPart((CharSequence)chars, start+(i-pos), end, out); return; } } } public static void writeStringPart(CharSequence chars, int start, int end, CharArr out) { for (int i=start; i=1f, (ch*146087937)&0xd6a01f80) is 0 only for characters that need escaping: " \\ u2028 u2029 // and has 7 false positives: 204a 4051 802f c022 c044 e04a e04b if ( ch>0x1f && ((ch*146087937)&0xd6a01f80)!=0 ) { out.write(ch); } else { writeChar(ch, out); } } } public static void unicodeEscape(int ch, CharArr out) { out.write('\\'); out.write('u'); out.write(HEX_CHARS[ch>>>12]); out.write(HEX_CHARS[(ch>>>8)&0xf]); out.write(HEX_CHARS[(ch>>>4)&0xf]); out.write(HEX_CHARS[ch&0xf]); } public static void writeNull(CharArr out) { out.write(NULL_CHARS); } public static void writeBoolean(boolean val, CharArr out) { out.write(val ? TRUE_CHARS : FALSE_CHARS); } } noggit-noggit-0.8/src/main/java/org/noggit/JSONWriter.java000077500000000000000000000172131311143662000234640ustar00rootroot00000000000000/** * Copyright 2006- Yonik Seeley * * 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.noggit; import java.util.*; /** * @author yonik * @version $Id: JSONWriter.java 1211150 2011-12-06 21:10:01Z yonik $ */ public class JSONWriter { /** Implement this interface on your class to support serialization */ public static interface Writable { public void write(JSONWriter writer); } protected int level; protected int indent; protected final CharArr out; /** * @param out the CharArr to write the output to. * @param indentSize The number of space characters to use as an indent (default 2). 0=newlines but no spaces, -1=no indent at all. */ public JSONWriter(CharArr out, int indentSize) { this.out = out; this.indent = indentSize; } public JSONWriter(CharArr out) { this(out, 2); } public void setIndentSize(int indentSize) { this.indent = indentSize; } public void indent() { if (indent >= 0) { out.write('\n'); if (indent > 0) { int spaces = level*indent; out.reserve(spaces); for (int i=0; i)o); } else if (o instanceof Collection) { write((Collection)o); } else if (o instanceof Boolean) { write(((Boolean)o).booleanValue()); } else if (o instanceof CharSequence) { writeString((CharSequence)o); } else if (o instanceof Writable) { ((Writable) o).write(this); } else if (o instanceof Object[]) { write(Arrays.asList((Object[])o)); } else if (o instanceof int[]) { write((int[])o); } else if (o instanceof float[]) { write((float[])o); } else if (o instanceof long[]) { write((long[])o); } else if (o instanceof double[]) { write((double[])o); } else if (o instanceof short[]) { write((short[])o); } else if (o instanceof boolean[]) { write((boolean[])o); } else if (o instanceof char[]) { write((char[])o); } else if (o instanceof byte[]) { write((byte[])o); } else { handleUnknownClass(o); } } /** Override this method for custom handling of unknown classes. Also see the Writable interface. */ public void handleUnknownClass(Object o) { writeString(o.toString()); } public void write(Map val) { startObject(); int sz = val.size(); boolean first = true; for (Map.Entry entry : val.entrySet()) { if (first) { first = false; } else { writeValueSeparator(); } if (sz>1) indent(); writeString(entry.getKey().toString()); writeNameSeparator(); write(entry.getValue()); } endObject(); } public void write(Collection val) { startArray(); int sz = val.size(); boolean first = true; for (Object o : val) { if (first) { first = false; } else { writeValueSeparator(); } if (sz>1) indent(); write(o); } endArray(); } /** A byte[] may be either a single logical value, or a list of small integers. * It's up to the implementation to decide. */ public void write(byte[] val) { startArray(); boolean first = true; for (short v : val) { if (first) { first = false; } else { writeValueSeparator(); } write(v); } endArray(); } public void write(short[] val) { startArray(); boolean first = true; for (short v : val) { if (first) { first = false; } else { writeValueSeparator(); } write(v); } endArray(); } public void write(int[] val) { startArray(); boolean first = true; for (int v : val) { if (first) { first = false; } else { writeValueSeparator(); } write(v); } endArray(); } public void write(long[] val) { startArray(); boolean first = true; for (long v : val) { if (first) { first = false; } else { writeValueSeparator(); } write(v); } endArray(); } public void write(float[] val) { startArray(); boolean first = true; for (float v : val) { if (first) { first = false; } else { writeValueSeparator(); } write(v); } endArray(); } public void write(double[] val) { startArray(); boolean first = true; for (double v : val) { if (first) { first = false; } else { writeValueSeparator(); } write(v); } endArray(); } public void write(boolean[] val) { startArray(); boolean first = true; for (boolean v : val) { if (first) { first = false; } else { writeValueSeparator(); } write(v); } endArray(); } public void write(short number) { write ((int)number); } public void write(byte number) { write((int)number); } public void writeNull() { JSONUtil.writeNull(out); } public void writeString(String str) { JSONUtil.writeString(str,0,str.length(),out); } public void writeString(CharSequence str) { JSONUtil.writeString(str,0,str.length(),out); } public void writeString(CharArr str) { JSONUtil.writeString(str,out); } public void writeStringStart() { out.write('"'); } public void writeStringChars(CharArr partialStr) { JSONUtil.writeStringPart(partialStr.getArray(), partialStr.getStart(), partialStr.getEnd(), out); } public void writeStringEnd() { out.write('"'); } public void write(long number) { JSONUtil.writeNumber(number,out); } public void write(int number) { JSONUtil.writeNumber(number,out); } public void write(double number) { JSONUtil.writeNumber(number,out); } public void write(float number) { JSONUtil.writeNumber(number,out); } public void write(boolean bool) { JSONUtil.writeBoolean(bool,out); } public void write(char[] val) { JSONUtil.writeString(val, 0, val.length, out); } public void writeNumber(CharArr digits) { out.write(digits); } public void writePartialNumber(CharArr digits) { out.write(digits); } public void startObject() { out.write('{'); level++; } public void endObject() { out.write('}'); level--; } public void startArray() { out.write('['); level++; } public void endArray() { out.write(']'); level--; } public void writeValueSeparator() { out.write(','); } public void writeNameSeparator() { out.write(':'); } } noggit-noggit-0.8/src/main/java/org/noggit/ObjectBuilder.java000066400000000000000000000102421311143662000242230ustar00rootroot00000000000000/** * Copyright 2006- Yonik Seeley * * 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.noggit; import java.util.*; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; /** * @author yonik * @version $Id$ */ public class ObjectBuilder { public static Object fromJSON(String json) throws IOException { JSONParser p = new JSONParser(json); return getVal(p); } public static Object getVal(JSONParser parser) throws IOException { return new ObjectBuilder(parser).getVal(); } final JSONParser parser; public ObjectBuilder(JSONParser parser) throws IOException { this.parser = parser; if (parser.lastEvent()==0) parser.nextEvent(); } public Object getVal() throws IOException { int ev = parser.lastEvent(); switch(ev) { case JSONParser.STRING: return getString(); case JSONParser.LONG: return getLong(); case JSONParser.NUMBER: return getNumber(); case JSONParser.BIGNUMBER: return getBigNumber(); case JSONParser.BOOLEAN: return getBoolean(); case JSONParser.NULL: return getNull(); case JSONParser.OBJECT_START: return getObject(); case JSONParser.OBJECT_END: return null; // OR ERROR? case JSONParser.ARRAY_START: return getArray(); case JSONParser.ARRAY_END: return null; // OR ERROR? case JSONParser.EOF: return null; // OR ERROR? default: return null; // OR ERROR? } } public Object getString() throws IOException { return parser.getString(); } public Object getLong() throws IOException { return Long.valueOf(parser.getLong()); } public Object getNumber() throws IOException { CharArr num = parser.getNumberChars(); String numstr = num.toString(); double d = Double.parseDouble(numstr); if (!Double.isInfinite(d)) return Double.valueOf(d); // TODO: use more efficient constructor in Java5 return new BigDecimal(num.buf, num.start, num.size()); } public Object getBigNumber() throws IOException { CharArr num = parser.getNumberChars(); String numstr = num.toString(); for(int ch; (ch=num.read())!=-1;) { if (ch=='.' || ch=='e' || ch=='E') return new BigDecimal(numstr); } return new BigInteger(numstr); } public Object getBoolean() throws IOException { return parser.getBoolean(); } public Object getNull() throws IOException { parser.getNull(); return null; } public Object newObject() throws IOException { return new LinkedHashMap(); } public Object getKey() throws IOException { return parser.getString(); } @SuppressWarnings("unchecked") public void addKeyVal(Object map, Object key, Object val) throws IOException { /* Object prev = */((Map) map).put(key, val); // TODO: test for repeated value? } public Object objectEnd(Object obj) { return obj; } public Object getObject() throws IOException { Object m = newObject(); for(;;) { int ev = parser.nextEvent(); if (ev==JSONParser.OBJECT_END) return objectEnd(m); Object key = getKey(); ev = parser.nextEvent(); Object val = getVal(); addKeyVal(m, key, val); } } public Object newArray() { return new ArrayList(); } @SuppressWarnings("unchecked") public void addArrayVal(Object arr, Object val) throws IOException { ((List)arr).add(val); } public Object endArray(Object arr) { return arr; } public Object getArray() throws IOException { Object arr = newArray(); for(;;) { int ev = parser.nextEvent(); if (ev==JSONParser.ARRAY_END) return endArray(arr); Object val = getVal(); addArrayVal(arr, val); } } } noggit-noggit-0.8/src/test/000077500000000000000000000000001311143662000157005ustar00rootroot00000000000000noggit-noggit-0.8/src/test/java/000077500000000000000000000000001311143662000166215ustar00rootroot00000000000000noggit-noggit-0.8/src/test/java/org/000077500000000000000000000000001311143662000174105ustar00rootroot00000000000000noggit-noggit-0.8/src/test/java/org/noggit/000077500000000000000000000000001311143662000206775ustar00rootroot00000000000000noggit-noggit-0.8/src/test/java/org/noggit/CharArrTest.java000066400000000000000000000007741311143662000237340ustar00rootroot00000000000000package org.noggit; import junit.framework.TestCase; /** * Created by eudoden on 07/01/2016. */ public class CharArrTest extends TestCase { public void testFixWriteCharArrToCharArr() throws Exception { CharArr target = new CharArr(10); target.append("1"); assertEquals(1,target.size()); CharArr source = new CharArr(10); source.append("23"); assertEquals(2, source.size()); target.write(source); assertEquals(3, target.size()); } }noggit-noggit-0.8/src/test/java/org/noggit/MyParse.java000077500000000000000000000062671311143662000231400ustar00rootroot00000000000000/** * Copyright 2006- Yonik Seeley * * 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.noggit; import java.io.StringReader; // handling numbers... either put a limit of buffersize on the number // while reading it all into another buffer, or ??? /* ISSUE: repeated keys in a map... is it an issue? Leave it to a higher level to validate this? The JSON standard says that keys *SHOULD* be unique, not *MUST*!!! That means we should support this when parsing. */ /** * @author yonik * @version $Id: MyParse.java 479919 2006-11-28 05:53:55Z yonik $ */ public class MyParse { public static void main(String[] args) throws Exception { StringReader sr; sr = new StringReader("\"hello\""); sr = new StringReader("[\"a\",\"b\"]"); sr = new StringReader(" [ \"a\" , \"b\" ] "); sr = new StringReader(" [ 1 , 23, 456,7890 ,999] "); sr = new StringReader("{}"); sr = new StringReader("{ \"a\" : 1}"); sr = new StringReader("[[1]]"); JSONParser js = new JSONParser(sr); int maxEv=100; for (int i=0; i0 ? buf[0] : -1; default: break; } } } public static int parseJSON(String s) throws IOException { JSONParser sr = new JSONParser(new StringReader(s)); for(;;) { int ev = sr.nextEvent(); switch (ev) { case JSONParser.EOF: return 1; case JSONParser.STRING: return sr.getStringChars().read(); // first char case JSONParser.LONG: return (int)sr.getLong(); default: break; } } } public static void main(String[] argv) throws Exception { int iter = Integer.parseInt(argv[0]); String[] xml = { " 1 2 3 4 5 ", " 1 2 3 4 5 ", " val1 val2 val3 val4 val5 ", "big nesting", "" + " val1a val2a val3a val4a val5a " + " val1b val2b val3b val4b val5b " + " val1c val2c val3c val4c val5c " + " val1d val2d val3d val4d val5d " + " val1e val2e val3e val4e val5e " + "" }; String[] json = { "[ 1, 2, 3, 4, 5 ]", "[ \"1\", \"2\", \"3\", \"4\", \"5\" ] ", "{ \"k1\":\"val1\", \"k2\":\"val2\", \"k3\":\"val3\", \"k4\":\"val4\", \"k5\":\"val5\" }", "{\"a\":{\"b\":{\"c\":{\"d\":{\"e\":{\"f\":{\"g\":{\"h\":{\"i\":{\"j\":{}}}}}}}}}}}", "[" + "{ \"k1\":\"val1a\", \"k2\":\"val2a\", \"k3\":\"val3a\", \"k4\":\"val4a\", \"k5\":\"val5a\" }," + "{ \"k1\":\"val1b\", \"k2\":\"val2b\", \"k3\":\"val3b\", \"k4\":\"val4b\", \"k5\":\"val5b\" }," + "{ \"k1\":\"val1c\", \"k2\":\"val2c\", \"k3\":\"val3c\", \"k4\":\"val4c\", \"k5\":\"val5c\" }," + "{ \"k1\":\"val1d\", \"k2\":\"val2d\", \"k3\":\"val3d\", \"k4\":\"val4d\", \"k5\":\"val5d\" }," + "{ \"k1\":\"val1e\", \"k2\":\"val2e\", \"k3\":\"val3e\", \"k4\":\"val4e\", \"k5\":\"val5e\" }" +"]", }; long start = System.currentTimeMillis(); int s = 0; int ret=0; for (int i=0; i=xml.length) s=0; // String val = xml[s]; // System.out.println(val); // ret += parseXML(val); String val = json[s]; ret += parseJSON(val); s++; } long end = System.currentTimeMillis(); System.out.println("Result:"+ret+", TIME="+(end-start)); } ***/ } noggit-noggit-0.8/src/test/java/org/noggit/TestJSONParser.java000077500000000000000000000543771311143662000243530ustar00rootroot00000000000000/** * Copyright 2006- Yonik Seeley * * 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.noggit; import java.io.IOException; import java.io.StringReader; import java.util.Map; import java.util.Random; import junit.framework.TestCase; /** * @author yonik * @version $Id: TestJSONParser.java 1099557 2011-05-04 18:54:26Z yonik $ */ public class TestJSONParser extends TestCase { public static Random r = new Random(); // these are to aid in debugging if an unexpected error occurs static int parserType; static int bufferSize; static String parserInput; static JSONParser lastParser; static int flags = JSONParser.FLAGS_DEFAULT; // the default public static String lastParser() { return "parserType=" + parserType + (parserType==1 ? " bufferSize=" + bufferSize : "") + " parserInput='" + parserInput + "'" + "flags : " + lastParser.flags; } public static JSONParser getParser(String s) { return getParser(s, r.nextInt(2), -1); } public static JSONParser getParser(String s, int type, int bufSize) { parserInput = s; parserType = type; JSONParser parser=null; switch (type) { case 0: // test directly using input buffer parser = new JSONParser(s.toCharArray(),0,s.length()); break; case 1: // test using Reader... // small input buffers can help find bugs on boundary conditions if (bufSize < 1) bufSize = r.nextInt(25) + 1; bufferSize = bufSize;// record in case there is an error parser = new JSONParser(new StringReader(s), new char[bufSize]); break; } if (parser == null) return null; lastParser = parser; if (flags != JSONParser.FLAGS_DEFAULT) { parser.setFlags(flags); } return parser; } /** for debugging purposes public void testSpecific() throws Exception { JSONParser parser = getParser("[0",1,1); for (;;) { int ev = parser.nextEvent(); if (ev == JSONParser.EOF) { break; } else { System.out.println("got " + JSONParser.getEventString(ev)); } } } **/ public static byte[] events = new byte[256]; static { events['{'] = JSONParser.OBJECT_START; events['}'] = JSONParser.OBJECT_END; events['['] = JSONParser.ARRAY_START; events[']'] = JSONParser.ARRAY_END; events['s'] = JSONParser.STRING; events['b'] = JSONParser.BOOLEAN; events['l'] = JSONParser.LONG; events['n'] = JSONParser.NUMBER; events['N'] = JSONParser.BIGNUMBER; events['0'] = JSONParser.NULL; events['e'] = JSONParser.EOF; } // match parser states with the expected states public static void parse(JSONParser p, String input, String expected) throws IOException { expected += "e"; for (int i=0; i>1) + 1; for (int j=0; j L(Object... lst) { return Arrays.asList(lst); } public static Object[] A(Object... lst) { return lst; } public static Map O(Object... lst) { LinkedHashMap map = new LinkedHashMap(); for (int i=0; i val = new LinkedHashMap(); val.put("a",1); val.put("b",2); writer.write(val); } } public void testWritable() throws Exception { test("[{'a':1,'b':2}]", L(new Custom()), -1); test("[10,{'a':1,'b':2},20]", L(10, new Custom(), 20), -1); } public void testUnknown() throws Exception { test("['a,\\\"b\\\",c']", L(new Unknown()), -1); } } noggit-noggit-0.8/src/test/java/org/noggit/TestObjectBuilder.java000066400000000000000000000067711311143662000251320ustar00rootroot00000000000000/** * Copyright 2006- Yonik Seeley * * 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.noggit; import junit.framework.TestCase; import java.io.IOException; import java.util.*; /** * @author yonik * @version $Id$ */ public class TestObjectBuilder extends TestCase { public void test(String val, Object expected) throws IOException { val = val.replace('\'','"'); Object v = ObjectBuilder.fromJSON(val); String s1 = JSONUtil.toJSON(v,-1); String s2 = JSONUtil.toJSON(expected,-1); assertEquals(s1, s2); // not make sure that it round-trips correctly JSONParser p2 = TestJSONParser.getParser(s1); Object v2 = ObjectBuilder.getVal(p2); String s3 = JSONUtil.toJSON(v2,-1); assertEquals(s1, s3); } public static List L(Object... lst) { return Arrays.asList(lst); } public static Object[] A(Object... lst) { return lst; } public static Map O(Object... lst) { LinkedHashMap map = new LinkedHashMap(); for (int i=0; i vals = new ArrayList(); static List objs = new ArrayList(); static boolean ws = false; static boolean nows = false; static boolean writer = false; public static void main(String[] args) throws Exception { int i=0; while (i out = new ArrayList(); for (String val : vals) { Object o = ObjectBuilder.fromJSON(val); if (writer) { objs.add(o); } else if (ws) { String s = JSONUtil.toJSON(o, 2); out.add(s); } else if (nows) { String s = JSONUtil.toJSON(o, -1); out.add(s); } } if (!writer) vals = out; } // calculate total size per iteration int sz = 0; for (String val : vals) { sz += val.length(); } long start = System.currentTimeMillis(); int ret=0; for (int j=0; j