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;
+ }
+}
\ No newline at end of file
diff --git a/solr/solrj/src/java/org/noggit/JSONUtil.java b/solr/solrj/src/java/org/noggit/JSONUtil.java
new file mode 100644
index 00000000000..4c74759c1f6
--- /dev/null
+++ b/solr/solrj/src/java/org/noggit/JSONUtil.java
@@ -0,0 +1,203 @@
+/*
+ * 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;
+
+
+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.
+ */
+ 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 < end; i++) {
+ char ch = val[i];
+ // When ch>=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 < endInOut; i++) {
+ char ch = arr[i];
+
+ // When ch>=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 < end; i++) {
+ char ch = chars.charAt(i);
+ // When ch>=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);
+ }
+
+}
\ No newline at end of file
diff --git a/solr/solrj/src/java/org/noggit/JSONWriter.java b/solr/solrj/src/java/org/noggit/JSONWriter.java
new file mode 100644
index 00000000000..dfec390db4c
--- /dev/null
+++ b/solr/solrj/src/java/org/noggit/JSONWriter.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2006- Yonik Seeley
+ *
+ * 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.util.*;
+
+
+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 < spaces; i++) {
+ out.unsafeWrite(' ');
+ }
+ }
+ }
+ }
+
+ public void write(Object o) {
+ // NOTE: an instance-of chain was about 50% faster than hashing on the classes, even with perfect hashing.
+ if (o == null) {
+ writeNull();
+ } else if (o instanceof String) {
+ writeString((String) o);
+ } else if (o instanceof Number) {
+ if (o instanceof Integer || o instanceof Long) {
+ write(((Number) o).longValue());
+ } else if (o instanceof Float || o instanceof Double) {
+ write(((Number) o).doubleValue());
+ } else {
+ CharArr arr = new CharArr();
+ arr.write(o.toString());
+ writeNumber(arr);
+ }
+ } else if (o instanceof Map) {
+ write((Map, ?>) 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(':');
+ }
+
+}
diff --git a/solr/solrj/src/java/org/noggit/ObjectBuilder.java b/solr/solrj/src/java/org/noggit/ObjectBuilder.java
new file mode 100644
index 00000000000..945a96b20b0
--- /dev/null
+++ b/solr/solrj/src/java/org/noggit/ObjectBuilder.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2006- Yonik Seeley
+ *
+ * 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.util.*;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+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