pax_global_header 0000666 0000000 0000000 00000000064 12445072163 0014516 g ustar 00root root 0000000 0000000 52 comment=ad5403479e029d6c4da059f9a82095761d089e38
noggit-noggit-0.7/ 0000775 0000000 0000000 00000000000 12445072163 0014140 5 ustar 00root root 0000000 0000000 noggit-noggit-0.7/.gitignore 0000775 0000000 0000000 00000000047 12445072163 0016134 0 ustar 00root root 0000000 0000000 .classpath
.project
.settings
target noggit-noggit-0.7/LICENSE.txt 0000775 0000000 0000000 00000026135 12445072163 0015775 0 ustar 00root root 0000000 0000000 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.7/README.txt 0000775 0000000 0000000 00000002537 12445072163 0015650 0 ustar 00root root 0000000 0000000 N 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.7/pom.xml 0000775 0000000 0000000 00000004057 12445072163 0015466 0 ustar 00root root 0000000 0000000 4.0.0org.noggitnoggitjar0.7Noggithttp://github.com/yonik/noggitNoggit is the world's fastest streaming JSON parser for Java.org.sonatype.ossoss-parent7Apache License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0.txtreposcm:git:git@github.com:yonik/noggit.gitscm:git:git@github.com:yonik/noggit.gitgit@github.com:yonik/noggit.gityseeley@gmail.comYonik Seeleyhttps://github.com/yonikyonikUTF-8junitjunit3.8.1testorg.apache.maven.pluginsmaven-compiler-plugin3.11.51.5org.apache.maven.pluginsmaven-eclipse-plugin2.9
noggit-noggit-0.7/src/ 0000775 0000000 0000000 00000000000 12445072163 0014727 5 ustar 00root root 0000000 0000000 noggit-noggit-0.7/src/main/ 0000775 0000000 0000000 00000000000 12445072163 0015653 5 ustar 00root root 0000000 0000000 noggit-noggit-0.7/src/main/java/ 0000775 0000000 0000000 00000000000 12445072163 0016574 5 ustar 00root root 0000000 0000000 noggit-noggit-0.7/src/main/java/org/ 0000775 0000000 0000000 00000000000 12445072163 0017363 5 ustar 00root root 0000000 0000000 noggit-noggit-0.7/src/main/java/org/noggit/ 0000775 0000000 0000000 00000000000 12445072163 0020652 5 ustar 00root root 0000000 0000000 noggit-noggit-0.7/src/main/java/org/noggit/CharArr.java 0000775 0000000 0000000 00000020062 12445072163 0023042 0 ustar 00root root 0000000 0000000 /**
* 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(); }
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, start, end-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.7/src/main/java/org/noggit/CharUtil.java 0000775 0000000 0000000 00000003234 12445072163 0023235 0 ustar 00root root 0000000 0000000 /**
* 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.7/src/main/java/org/noggit/JSONParser.java 0000775 0000000 0000000 00000106272 12445072163 0023456 0 ustar 00root root 0000000 0000000 /**
* 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 nunber of extra commas in arrays and objects to be ignored
* Note that a trailing comma in [] would be [,] (hence calling the feature "trailing" commas
* isn't really correct. 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 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();
int flags = FLAGS_DEFAULT;
final char[] buf; // input buffer with JSON text in it
int start; // current position in the buffer
int end; // end position in the buffer (one past last valid index)
final Reader in; // optional reader to obtain data from
boolean eof=false; // true if the end of the stream was reached.
long gpos; // global position = gpos + start
int event; // last event read
int stringTerm; // The terminator for the last string we read: single quote, double quote, or 0 for unterminated.
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();
}
}
private 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();
}
}
private 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 += " BEFORE='" + 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)
private 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:
return event = next(getChar());
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 != ':') {
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 != ',') {
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 {
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.7/src/main/java/org/noggit/JSONParserWS.java 0000664 0000000 0000000 00000006412 12445072163 0023720 0 ustar 00root root 0000000 0000000 /**
* 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.7/src/main/java/org/noggit/JSONUtil.java 0000775 0000000 0000000 00000011757 12445072163 0023142 0 ustar 00root root 0000000 0000000 /**
* 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: 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(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>>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.7/src/main/java/org/noggit/JSONWriter.java 0000775 0000000 0000000 00000016577 12445072163 0023506 0 ustar 00root root 0000000 0000000 /**
* 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 Object[]) {
write(Arrays.asList((Object[])o));
} else if (o instanceof Boolean) {
write(((Boolean)o).booleanValue());
} else if (o instanceof Writable) {
((Writable) o).write(this);
}
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(out.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(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.7/src/main/java/org/noggit/ObjectBuilder.java 0000664 0000000 0000000 00000010212 12445072163 0024226 0 ustar 00root root 0000000 0000000 /**
* 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(numstr);
}
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