You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2018/02/28 11:37:53 UTC
[4/7] groovy git commit: GROOVY-8379: Rework groovy-json
FastStringUtils (closes #667)
http://git-wip-us.apache.org/repos/asf/groovy/blob/5a3f9996/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharBuf.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharBuf.java b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharBuf.java
new file mode 100644
index 0000000..db850e1
--- /dev/null
+++ b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharBuf.java
@@ -0,0 +1,842 @@
+/*
+ * 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.apache.groovy.json.internal;
+
+import groovy.json.JsonException;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * @author Rick Hightower
+ */
+public class CharBuf extends Writer implements CharSequence {
+
+ protected int capacity = 16;
+ protected int location = 0;
+
+ protected char[] buffer;
+
+ public CharBuf(char[] buffer) {
+ __init__(buffer);
+ }
+
+ private void __init__(char[] buffer) {
+ this.buffer = buffer;
+ this.capacity = buffer.length;
+ }
+
+ public CharBuf(byte[] bytes) {
+ this.buffer = null;
+ try {
+ String str = new String(bytes, "UTF-8");
+ __init__(FastStringUtils.toCharArray(str));
+ } catch (UnsupportedEncodingException e) {
+ Exceptions.handle(e);
+ }
+ }
+
+ public static CharBuf createExact(final int capacity) {
+ return new CharBuf(capacity) {
+ public CharBuf add(char[] chars) {
+ Chr._idx(buffer, location, chars);
+ location += chars.length;
+ return this;
+ }
+ };
+ }
+
+ public static CharBuf create(int capacity) {
+ return new CharBuf(capacity);
+ }
+
+ public static CharBuf create(char[] buffer) {
+ return new CharBuf(buffer);
+ }
+
+ protected CharBuf(int capacity) {
+ this.capacity = capacity;
+ init();
+ }
+
+ protected CharBuf() {
+ init();
+ }
+
+ public void write(char[] cbuf, int off, int len) {
+ if (off == 0 && cbuf.length == len) {
+ this.add(cbuf);
+ } else {
+ char[] buffer = ArrayUtils.copyRange(cbuf, off, off + len);
+ this.add(buffer);
+ }
+ }
+
+ public void flush() throws IOException {
+ }
+
+ public void close() throws IOException {
+ }
+
+ public void init() {
+ buffer = new char[capacity];
+ }
+
+ public final CharBuf add(String str) {
+ add(FastStringUtils.toCharArray(str));
+ return this;
+ }
+
+ public final CharBuf addString(String str) {
+ add(FastStringUtils.toCharArray(str));
+ return this;
+ }
+
+ public final CharBuf add(int i) {
+ add(Integer.toString(i));
+ return this;
+ }
+
+ private Cache<Integer, char[]> icache;
+
+ public final CharBuf addInt(int i) {
+ switch (i) {
+ case 0:
+ addChar('0');
+ return this;
+ case 1:
+ addChar('1');
+ return this;
+ case -1:
+ addChar('-');
+ addChar('1');
+ return this;
+ }
+
+ addInt(Integer.valueOf(i));
+ return this;
+ }
+
+ public final CharBuf addInt(Integer key) {
+ if (icache == null) {
+ icache = new SimpleCache<Integer, char[]>(20);
+ }
+ char[] chars = icache.get(key);
+
+ if (chars == null) {
+ String str = Integer.toString(key);
+ chars = FastStringUtils.toCharArray(str);
+ icache.put(key, chars);
+ }
+
+ addChars(chars);
+ return this;
+ }
+
+ final char[] trueChars = "true".toCharArray();
+ final char[] falseChars = "false".toCharArray();
+
+ public final CharBuf add(boolean b) {
+ addChars(b ? trueChars : falseChars);
+ return this;
+ }
+
+ public final CharBuf addBoolean(boolean b) {
+ add(Boolean.toString(b));
+ return this;
+ }
+
+ public final CharBuf add(byte i) {
+ add(Byte.toString(i));
+ return this;
+ }
+
+ public final CharBuf addByte(byte i) {
+ addInt(i);
+ return this;
+ }
+
+ public final CharBuf add(short i) {
+ add(Short.toString(i));
+ return this;
+ }
+
+ public final CharBuf addShort(short i) {
+ addInt(i);
+ return this;
+ }
+
+ public final CharBuf add(long l) {
+ add(Long.toString(l));
+ return this;
+ }
+
+ public final CharBuf add(double d) {
+ add(Double.toString(d));
+ return this;
+ }
+
+ private Cache<Double, char[]> dcache;
+
+ public final CharBuf addDouble(double d) {
+ addDouble(Double.valueOf(d));
+ return this;
+ }
+
+ public final CharBuf addDouble(Double key) {
+ if (dcache == null) {
+ dcache = new SimpleCache<Double, char[]>(20);
+ }
+ char[] chars = dcache.get(key);
+
+ if (chars == null) {
+ String str = Double.toString(key);
+ chars = FastStringUtils.toCharArray(str);
+ dcache.put(key, chars);
+ }
+
+ add(chars);
+ return this;
+ }
+
+ public final CharBuf add(float d) {
+ add(Float.toString(d));
+ return this;
+ }
+
+ private Cache<Float, char[]> fcache;
+
+ public final CharBuf addFloat(float d) {
+ addFloat(Float.valueOf(d));
+ return this;
+ }
+
+ public final CharBuf addFloat(Float key) {
+ if (fcache == null) {
+ fcache = new SimpleCache<Float, char[]>(20);
+ }
+ char[] chars = fcache.get(key);
+
+ if (chars == null) {
+ String str = Float.toString(key);
+ chars = FastStringUtils.toCharArray(str);
+ fcache.put(key, chars);
+ }
+
+ add(chars);
+
+ return this;
+ }
+
+ public final CharBuf addChar(byte i) {
+ add((char) i);
+ return this;
+ }
+
+ public final CharBuf addChar(int i) {
+ add((char) i);
+ return this;
+ }
+
+ public final CharBuf addChar(short i) {
+ add((char) i);
+ return this;
+ }
+
+ public final CharBuf addChar(final char ch) {
+ int _location = location;
+ char[] _buffer = buffer;
+ int _capacity = capacity;
+
+ if (1 + _location > _capacity) {
+ _buffer = Chr.grow(_buffer);
+ _capacity = _buffer.length;
+ }
+
+ _buffer[_location] = ch;
+ _location++;
+
+ location = _location;
+ buffer = _buffer;
+ capacity = _capacity;
+ return this;
+ }
+
+ public CharBuf addLine(String str) {
+ add(str.toCharArray());
+ add('\n');
+ return this;
+ }
+
+ public CharBuf addLine(CharSequence str) {
+ add(str.toString());
+ add('\n');
+ return this;
+ }
+
+ public CharBuf add(char[] chars) {
+ if (chars.length + location > capacity) {
+ buffer = Chr.grow(buffer, buffer.length * 2 + chars.length);
+ capacity = buffer.length;
+ }
+
+ Chr._idx(buffer, location, chars);
+ location += chars.length;
+ return this;
+ }
+
+ public final CharBuf addChars(char[] chars) {
+ if (chars.length + location > capacity) {
+ buffer = Chr.grow(buffer, buffer.length * 2 + chars.length);
+ capacity = buffer.length;
+ }
+
+ System.arraycopy(chars, 0, buffer, location, chars.length);
+ location += chars.length;
+ return this;
+ }
+
+ public final CharBuf addQuoted(char[] chars) {
+ int _location = location;
+ char[] _buffer = buffer;
+ int _capacity = capacity;
+
+ int sizeNeeded = chars.length + 2 + _location;
+ if (sizeNeeded > _capacity) {
+ _buffer = Chr.grow(_buffer, sizeNeeded * 2);
+ _capacity = _buffer.length;
+ }
+ _buffer[_location] = '"';
+ _location++;
+
+ System.arraycopy(chars, 0, _buffer, _location, chars.length);
+
+ _location += (chars.length);
+ _buffer[_location] = '"';
+ _location++;
+
+ location = _location;
+ buffer = _buffer;
+ capacity = _capacity;
+ return this;
+ }
+
+ public final CharBuf addJsonEscapedString(String jsonString) {
+ return addJsonEscapedString(jsonString, false);
+ }
+
+ public final CharBuf addJsonEscapedString(String jsonString, boolean disableUnicodeEscaping) {
+ char[] charArray = FastStringUtils.toCharArray(jsonString);
+ return addJsonEscapedString(charArray, disableUnicodeEscaping);
+ }
+
+ private static boolean shouldEscape(int c, boolean disableUnicodeEscaping) {
+ if (c < 32) { /* less than space is a control char */
+ return true;
+ } else if (c == 34) { /* double quote */
+ return true;
+ } else if (c == 92) { /* backslash */
+ return true;
+ } else if (!disableUnicodeEscaping && c > 126) { /* non-ascii char range */
+ return true;
+ }
+
+ return false;
+ }
+
+ private static boolean hasAnyJSONControlChars(final char[] charArray, boolean disableUnicodeEscaping) {
+ int index = 0;
+ char c;
+ while (true) {
+ c = charArray[index];
+ if (shouldEscape(c, disableUnicodeEscaping)) {
+ return true;
+ }
+ if (++index >= charArray.length) return false;
+ }
+ }
+
+ public final CharBuf addJsonEscapedString(final char[] charArray) {
+ return addJsonEscapedString(charArray, false);
+ }
+
+ public final CharBuf addJsonEscapedString(final char[] charArray, boolean disableUnicodeEscaping) {
+ if (charArray.length == 0) return this;
+ if (hasAnyJSONControlChars(charArray, disableUnicodeEscaping)) {
+ return doAddJsonEscapedString(charArray, disableUnicodeEscaping);
+ } else {
+ return this.addQuoted(charArray);
+ }
+ }
+
+ final byte[] encoded = new byte[2];
+
+ final byte[] charTo = new byte[2];
+
+ private CharBuf doAddJsonEscapedString(char[] charArray, boolean disableUnicodeEscaping) {
+ char[] _buffer = buffer;
+ int _location = this.location;
+
+ final byte[] _encoded = encoded;
+
+ final byte[] _charTo = charTo;
+ /* We are making a bet that not all chars will be unicode. */
+ int ensureThisMuch = charArray.length * 6 + 2;
+
+ int sizeNeeded = (ensureThisMuch) + _location;
+ if (sizeNeeded > capacity) {
+ int growBy = (_buffer.length * 2) < sizeNeeded ? sizeNeeded : (_buffer.length * 2);
+ _buffer = Chr.grow(buffer, growBy);
+ capacity = _buffer.length;
+ }
+
+ _buffer[_location] = '"';
+ _location++;
+
+ int index = 0;
+ while (true) {
+ char c = charArray[index];
+
+ if (shouldEscape(c, disableUnicodeEscaping)) {
+ /* We are covering our bet with a safety net.
+ otherwise we would have to have 5x buffer
+ allocated for control chars */
+ if (_location + 5 > _buffer.length) {
+ _buffer = Chr.grow(_buffer, 20);
+ }
+
+ switch (c) {
+ case '\"':
+ _buffer[_location] = '\\';
+ _location++;
+ _buffer[_location] = '"';
+ _location++;
+ break;
+ case '\\':
+ _buffer[_location] = '\\';
+ _location++;
+ _buffer[_location] = '\\';
+ _location++;
+ break;
+ //There is not requirement to escape solidus so we will not.
+// case '/':
+// _buffer[_location] = '\\';
+// _location ++;
+// _buffer[_location] = '/';
+// _location ++;
+// break;
+
+ case '\b':
+ _buffer[_location] = '\\';
+ _location++;
+ _buffer[_location] = 'b';
+ _location++;
+ break;
+ case '\f':
+ _buffer[_location] = '\\';
+ _location++;
+ _buffer[_location] = 'f';
+ _location++;
+ break;
+ case '\n':
+ _buffer[_location] = '\\';
+ _location++;
+ _buffer[_location] = 'n';
+ _location++;
+ break;
+ case '\r':
+ _buffer[_location] = '\\';
+ _location++;
+ _buffer[_location] = 'r';
+ _location++;
+ break;
+ case '\t':
+ _buffer[_location] = '\\';
+ _location++;
+ _buffer[_location] = 't';
+ _location++;
+ break;
+ default:
+ _buffer[_location] = '\\';
+ _location++;
+ _buffer[_location] = 'u';
+ _location++;
+ if (c <= 255) {
+ _buffer[_location] = '0';
+ _location++;
+ _buffer[_location] = '0';
+ _location++;
+ ByteScanner.encodeByteIntoTwoAsciiCharBytes(c, _encoded);
+ for (int b : _encoded) {
+ _buffer[_location] = (char) b;
+ _location++;
+ }
+ } else {
+ _charTo[1] = (byte) (c);
+ _charTo[0] = (byte) (c >>> 8);
+
+ for (int charByte : _charTo) {
+ ByteScanner.encodeByteIntoTwoAsciiCharBytes(charByte, _encoded);
+ for (int b : _encoded) {
+ _buffer[_location] = (char) b;
+ _location++;
+ }
+ }
+ }
+ }
+ } else {
+ _buffer[_location] = c;
+ _location++;
+ }
+
+ if (++index >= charArray.length) break;
+ }
+ _buffer[_location] = '"';
+ _location++;
+
+ buffer = _buffer;
+ location = _location;
+
+ return this;
+ }
+
+ public final CharBuf addJsonFieldName(String str) {
+ return addJsonFieldName(str, false);
+ }
+
+ public final CharBuf addJsonFieldName(String str, boolean disableUnicodeEscaping) {
+ return addJsonFieldName(FastStringUtils.toCharArray(str), disableUnicodeEscaping);
+ }
+
+ private static final char[] EMPTY_STRING_CHARS = Chr.array('"', '"');
+
+ public final CharBuf addJsonFieldName(char[] chars) {
+ return addJsonFieldName(chars, false);
+ }
+
+ public final CharBuf addJsonFieldName(char[] chars, boolean disableUnicodeEscaping) {
+ if (chars.length > 0) {
+ addJsonEscapedString(chars, disableUnicodeEscaping);
+ } else {
+ addChars(EMPTY_STRING_CHARS);
+ }
+ addChar(':');
+ return this;
+ }
+
+ public final CharBuf addQuoted(String str) {
+ final char[] chars = FastStringUtils.toCharArray(str);
+ addQuoted(chars);
+ return this;
+ }
+
+ public CharBuf add(char[] chars, final int length) {
+ if (length + location < capacity) {
+ Chr._idx(buffer, location, chars, length);
+ } else {
+ buffer = Chr.grow(buffer, buffer.length * 2 + length);
+ Chr._idx(buffer, location, chars);
+ capacity = buffer.length;
+ }
+ location += length;
+ return this;
+ }
+
+ public CharBuf add(byte[] chars) {
+ if (chars.length + location < capacity) {
+ Chr._idx(buffer, location, chars);
+ } else {
+ buffer = Chr.grow(buffer, buffer.length * 2 + chars.length);
+ Chr._idx(buffer, location, chars);
+ capacity = buffer.length;
+ }
+ location += chars.length;
+ return this;
+ }
+
+ public CharBuf add(byte[] bytes, int start, int end) {
+ int charsLength = end - start;
+ if (charsLength + location > capacity) {
+ buffer = Chr.grow(buffer, buffer.length * 2 + charsLength);
+ }
+ Chr._idx(buffer, location, bytes, start, end);
+ capacity = buffer.length;
+ location += charsLength;
+ return this;
+ }
+
+ public final CharBuf add(char ch) {
+ if (1 + location < capacity) {
+ buffer[location] = ch;
+ } else {
+ buffer = Chr.grow(buffer);
+ buffer[location] = ch;
+ capacity = buffer.length;
+ }
+ location += 1;
+ return this;
+ }
+
+ public int length() {
+ return len();
+ }
+
+ public char charAt(int index) {
+ return buffer[index];
+ }
+
+ public CharSequence subSequence(int start, int end) {
+ return new String(buffer, start, end - start);
+ }
+
+ public String toString() {
+ return new String(buffer, 0, location);
+ }
+
+ public String toDebugString() {
+ return "CharBuf{" +
+ "capacity=" + capacity +
+ ", location=" + location +
+ '}';
+ }
+
+ public String toStringAndRecycle() {
+ String str = new String(buffer, 0, location);
+ location = 0;
+ return str;
+ }
+
+ public int len() {
+ return location;
+ }
+
+ public char[] toCharArray() {
+ return this.buffer;
+ }
+
+ public void _len(int location) {
+ this.location = location;
+ }
+
+ public char[] readForRecycle() {
+ this.location = 0;
+ return this.buffer;
+ }
+
+ public void recycle() {
+ this.location = 0;
+ }
+
+ public double doubleValue() {
+ return CharScanner.parseDouble(this.buffer, 0, location);
+ }
+
+ public float floatValue() {
+ return CharScanner.parseFloat(this.buffer, 0, location);
+ }
+
+ public int intValue() {
+ return CharScanner.parseIntFromTo(buffer, 0, location);
+ }
+
+ public long longValue() {
+ return CharScanner.parseLongFromTo(buffer, 0, location);
+ }
+
+ public byte byteValue() {
+ return (byte) intValue();
+ }
+
+ public short shortValue() {
+ return (short) intValue();
+ }
+
+ public Number toIntegerWrapper() {
+ if (CharScanner.isInteger(buffer, 0, location)) {
+ return intValue();
+ } else {
+ return longValue();
+ }
+ }
+
+ static final char[] nullChars = "null".toCharArray();
+
+ public final void addNull() {
+ this.add(nullChars);
+ }
+
+ public void removeLastChar() {
+ if (location > 0) {
+ location--;
+ }
+ }
+
+ public void removeLastChar(char expect) {
+ if (location == 0 || buffer[location-1] != expect) {
+ return;
+ }
+ removeLastChar();
+ }
+
+ private Cache<BigDecimal, char[]> bigDCache;
+
+ public CharBuf addBigDecimal(BigDecimal key) {
+ if (bigDCache == null) {
+ bigDCache = new SimpleCache<BigDecimal, char[]>(20);
+ }
+ char[] chars = bigDCache.get(key);
+
+ if (chars == null) {
+ String str = key.toString();
+ chars = FastStringUtils.toCharArray(str);
+ bigDCache.put(key, chars);
+ }
+
+ add(chars);
+
+ return this;
+ }
+
+ private Cache<BigInteger, char[]> bigICache;
+
+ public CharBuf addBigInteger(BigInteger key) {
+ if (bigICache == null) {
+ bigICache = new SimpleCache<BigInteger, char[]>(20);
+ }
+ char[] chars = bigICache.get(key);
+
+ if (chars == null) {
+ String str = key.toString();
+ chars = FastStringUtils.toCharArray(str);
+ bigICache.put(key, chars);
+ }
+
+ add(chars);
+
+ return this;
+ }
+
+ private Cache<Long, char[]> lcache;
+
+ public final CharBuf addLong(long l) {
+ addLong(Long.valueOf(l));
+ return this;
+ }
+
+ public final CharBuf addLong(Long key) {
+ if (lcache == null) {
+ lcache = new SimpleCache<Long, char[]>(20);
+ }
+ char[] chars = lcache.get(key);
+
+ if (chars == null) {
+ String str = Long.toString(key);
+ chars = FastStringUtils.toCharArray(str);
+ lcache.put(key, chars);
+ }
+
+ add(chars);
+
+ return this;
+ }
+
+ public final CharBuf decodeJsonString(char[] chars) {
+ return decodeJsonString(chars, 0, chars.length);
+ }
+
+ public final CharBuf decodeJsonString(char[] chars, int start, int to) {
+ int len = to - start;
+
+ char[] buffer = this.buffer;
+ int location = this.location;
+
+ if (len > capacity) {
+ buffer = Chr.grow(buffer, buffer.length * 2 + len);
+ capacity = buffer.length;
+ }
+
+ for (int index = start; index < to; index++) {
+ char c = chars[index];
+ if (c == '\\') {
+ if (index < to) {
+ index++;
+ c = chars[index];
+ switch (c) {
+
+ case 'n':
+ buffer[location++] = '\n';
+ break;
+
+ case '/':
+ buffer[location++] = '/';
+ break;
+
+ case '"':
+ buffer[location++] = '"';
+ break;
+
+ case 'f':
+ buffer[location++] = '\f';
+ break;
+
+ case 't':
+ buffer[location++] = '\t';
+ break;
+
+ case '\\':
+ buffer[location++] = '\\';
+ break;
+
+ case 'b':
+ buffer[location++] = '\b';
+ break;
+
+ case 'r':
+ buffer[location++] = '\r';
+ break;
+
+ case 'u':
+ if (index + 4 < to) {
+ String hex = new String(chars, index + 1, 4);
+ char unicode = (char) Integer.parseInt(hex, 16);
+ buffer[location++] = unicode;
+ index += 4;
+ }
+ break;
+
+ default:
+ throw new JsonException("Unable to decode string");
+ }
+ }
+ } else {
+ buffer[location++] = c;
+ }
+ }
+
+ this.buffer = buffer;
+ this.location = location;
+
+ return this;
+ }
+}
+
+
http://git-wip-us.apache.org/repos/asf/groovy/blob/5a3f9996/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharScanner.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharScanner.java b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharScanner.java
new file mode 100644
index 0000000..2dfacf9
--- /dev/null
+++ b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharScanner.java
@@ -0,0 +1,512 @@
+/*
+ * 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.apache.groovy.json.internal;
+
+import java.math.BigDecimal;
+
+import static org.apache.groovy.json.internal.Exceptions.die;
+import static org.apache.groovy.json.internal.Exceptions.handle;
+
+/**
+ * @author Richard Hightower
+ */
+public class CharScanner {
+
+ protected static final int COMMA = ',';
+ protected static final int CLOSED_CURLY = '}';
+ protected static final int CLOSED_BRACKET = ']';
+ protected static final int LETTER_E = 'e';
+ protected static final int LETTER_BIG_E = 'E';
+ protected static final int DECIMAL_POINT = '.';
+ protected static final int ALPHA_0 = '0';
+ protected static final int ALPHA_9 = '9';
+ protected static final int MINUS = '-';
+ protected static final int PLUS = '+';
+
+ static final String MIN_LONG_STR_NO_SIGN = String.valueOf(Long.MIN_VALUE);
+ static final String MAX_LONG_STR = String.valueOf(Long.MAX_VALUE);
+ static final String MIN_INT_STR_NO_SIGN = String.valueOf(Integer.MIN_VALUE);
+ static final String MAX_INT_STR = String.valueOf(Integer.MAX_VALUE);
+
+ private static double powersOf10[] = {
+ 1.0,
+ 10.0,
+ 100.0,
+ 1000.0,
+ 10000.0,
+ 100000.0,
+ 1000000.0,
+ 10000000.0,
+ 100000000.0,
+ 1000000000.0,
+ 10000000000.0,
+ 100000000000.0,
+ 1000000000000.0,
+ 10000000000000.0,
+ 100000000000000.0,
+ 1000000000000000.0,
+ 10000000000000000.0,
+ 100000000000000000.0,
+ 1000000000000000000.0,
+ };
+
+ public static boolean isDigit(int c) {
+ return c >= ALPHA_0 && c <= ALPHA_9;
+ }
+
+ public static boolean isDecimalDigit(int c) {
+ return isDigit(c) || isDecimalChar(c);
+ }
+
+ public static boolean isDecimalChar(int currentChar) {
+ switch (currentChar) {
+ case MINUS:
+ case PLUS:
+ case LETTER_E:
+ case LETTER_BIG_E:
+ case DECIMAL_POINT:
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean hasDecimalChar(char[] chars, boolean negative) {
+ int index = 0;
+
+ if (negative) index++;
+
+ for (; index < chars.length; index++) {
+ switch (chars[index]) {
+ case MINUS:
+ case PLUS:
+ case LETTER_E:
+ case LETTER_BIG_E:
+ case DECIMAL_POINT:
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean isLong(char[] digitChars) {
+ return isLong(digitChars, 0, digitChars.length);
+ }
+
+ public static boolean isLong(char[] digitChars, int offset, int len) {
+ String cmpStr = digitChars[offset] == '-' ? MIN_LONG_STR_NO_SIGN : MAX_LONG_STR;
+ int cmpLen = cmpStr.length();
+ if (len < cmpLen) return true;
+ if (len > cmpLen) return false;
+
+ for (int i = 0; i < cmpLen; ++i) {
+ int diff = digitChars[offset + i] - cmpStr.charAt(i);
+ if (diff != 0) {
+ return (diff < 0);
+ }
+ }
+ return true;
+ }
+
+ public static boolean isInteger(char[] digitChars) {
+ return isInteger(digitChars, 0, digitChars.length);
+ }
+
+ public static boolean isInteger(char[] digitChars, int offset, int len) {
+ String cmpStr = (digitChars[offset] == '-') ? MIN_INT_STR_NO_SIGN : MAX_INT_STR;
+ int cmpLen = cmpStr.length();
+ if (len < cmpLen) return true;
+ if (len > cmpLen) return false;
+
+ for (int i = 0; i < cmpLen; ++i) {
+ int diff = digitChars[offset + i] - cmpStr.charAt(i);
+ if (diff != 0) {
+ return (diff < 0);
+ }
+ }
+ return true;
+ }
+
+ public static int parseInt(char[] digitChars) {
+ return parseIntFromTo(digitChars, 0, digitChars.length);
+ }
+
+ public static int parseIntFromTo(char[] digitChars, int offset, int to) {
+ try {
+ int num;
+ boolean negative = false;
+ char c = digitChars[offset];
+ if (c == '-') {
+ offset++;
+ negative = true;
+ }
+ if (offset >= to) {
+ die();
+ }
+ num = (digitChars[offset] - '0');
+ if (++offset < to) {
+ num = (num * 10) + (digitChars[offset] - '0');
+ if (++offset < to) {
+ num = (num * 10) + (digitChars[offset] - '0');
+ if (++offset < to) {
+ num = (num * 10) + (digitChars[offset] - '0');
+ if (++offset < to) {
+ num = (num * 10) + (digitChars[offset] - '0');
+ if (++offset < to) {
+ num = (num * 10) + (digitChars[offset] - '0');
+ if (++offset < to) {
+ num = (num * 10) + (digitChars[offset] - '0');
+ if (++offset < to) {
+ num = (num * 10) + (digitChars[offset] - '0');
+ if (++offset < to) {
+ num = (num * 10) + (digitChars[offset] - '0');
+ if (++offset < to) {
+ num = (num * 10) + (digitChars[offset] - '0');
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return negative ? num * -1 : num;
+ } catch (Exception ex) {
+ return handle(int.class, ex);
+ }
+ }
+
+ public static int parseIntFromToIgnoreDot(char[] digitChars, int offset, int to) {
+ int num;
+ boolean negative = false;
+ char c = digitChars[offset];
+ if (c == '-') {
+ offset++;
+ negative = true;
+ }
+ if (offset >= to) {
+ die();
+ }
+ c = digitChars[offset];
+ num = (c - '0');
+ offset++;
+
+ for (; offset < to; offset++) {
+ c = digitChars[offset];
+ if (c != '.') {
+ num = (num * 10) + (c - '0');
+ }
+ }
+
+ return negative ? num * -1 : num;
+ }
+
+ public static long parseLongFromToIgnoreDot(char[] digitChars, int offset, int to) {
+ long num;
+ boolean negative = false;
+ char c = digitChars[offset];
+ if (c == '-') {
+ offset++;
+ negative = true;
+ }
+ if (offset >= to) {
+ die();
+ }
+ c = digitChars[offset];
+ num = (c - '0');
+ offset++;
+
+ for (; offset < to; offset++) {
+ c = digitChars[offset];
+ if (c != '.') {
+ num = (num * 10) + (c - '0');
+ }
+ }
+
+ return negative ? num * -1 : num;
+ }
+
+ public static long parseLongFromTo(char[] digitChars, int offset, int to) {
+ long num;
+ boolean negative = false;
+ char c = digitChars[offset];
+ if (c == '-') {
+ offset++;
+ negative = true;
+ }
+ if (offset >= to) {
+ die();
+ }
+ c = digitChars[offset];
+ num = (c - '0');
+ offset++;
+
+ long digit;
+
+ for (; offset < to; offset++) {
+ c = digitChars[offset];
+ digit = (c - '0');
+ num = (num * 10) + digit;
+ }
+
+ return negative ? num * -1 : num;
+ }
+
+ public static long parseLong(char[] digitChars) {
+ return parseLongFromTo(digitChars, 0, digitChars.length);
+ }
+
+ public static Number parseJsonNumber(char[] buffer) {
+ return parseJsonNumber(buffer, 0, buffer.length);
+ }
+
+ public static Number parseJsonNumber(char[] buffer, int from, int to) {
+ return parseJsonNumber(buffer, from, to, null);
+ }
+
+ public static boolean isNumberDigit(int c) {
+ return c >= ALPHA_0 && c <= ALPHA_9;
+ }
+
+ protected static boolean isDelimiter(int c) {
+ return c == COMMA || c == CLOSED_CURLY || c == CLOSED_BRACKET;
+ }
+
+ public static Number parseJsonNumber(char[] buffer, int from, int max, int size[]) {
+ Number value = null;
+ boolean simple = true;
+ int digitsPastPoint = 0;
+
+ int index = from;
+
+ if (buffer[index] == '-') {
+ index++;
+ }
+ if (index >= max) {
+ die();
+ }
+
+ boolean foundDot = false;
+ for (; index < max; index++) {
+ char ch = buffer[index];
+ if (isNumberDigit(ch)) {
+ if (foundDot) {
+ digitsPastPoint++;
+ }
+ } else if (ch <= 32 || isDelimiter(ch)) {
+ break;
+ } else if (ch == '.') {
+ if (foundDot) {
+ die("unexpected character " + ch);
+ }
+ foundDot = true;
+ } else if (ch == 'E' || ch == 'e' || ch == '-' || ch == '+') {
+ simple = false;
+ } else {
+ die("unexpected character " + ch);
+ }
+ }
+
+ if (digitsPastPoint >= powersOf10.length - 1) {
+ simple = false;
+ }
+
+ final int length = index - from;
+
+ if (!foundDot && simple) {
+ if (isInteger(buffer, from, length)) {
+ value = parseIntFromTo(buffer, from, index);
+ } else {
+ value = parseLongFromTo(buffer, from, index);
+ }
+ } else {
+ value = new BigDecimal(buffer, from, length);
+ }
+
+ if (size != null) {
+ size[0] = index;
+ }
+
+ return value;
+ }
+
+ public static BigDecimal parseBigDecimal(char[] buffer) {
+ return new BigDecimal(buffer);
+ }
+
+ public static float parseFloat(char[] buffer, int from, int to) {
+ return (float) parseDouble(buffer, from, to);
+ }
+
+ public static double parseDouble(char[] buffer, int from, int to) {
+ double value;
+ boolean simple = true;
+ int digitsPastPoint = 0;
+
+ int index = from;
+
+ if (buffer[index] == '-') {
+ index++;
+ }
+
+ boolean foundDot = false;
+ for (; index < to; index++) {
+ char ch = buffer[index];
+ if (isNumberDigit(ch)) {
+ if (foundDot) {
+ digitsPastPoint++;
+ }
+ } else if (ch == '.') {
+ if (foundDot) {
+ die("unexpected character " + ch);
+ }
+ foundDot = true;
+ } else if (ch == 'E' || ch == 'e' || ch == '-' || ch == '+') {
+ simple = false;
+ } else {
+ die("unexpected character " + ch);
+ }
+ }
+
+ if (digitsPastPoint >= powersOf10.length - 1) {
+ simple = false;
+ }
+
+ final int length = index - from;
+
+ if (!foundDot && simple) {
+ if (isInteger(buffer, from, length)) {
+ value = parseIntFromTo(buffer, from, index);
+ } else {
+ value = parseLongFromTo(buffer, from, index);
+ }
+ } else if (foundDot && simple) {
+ long lvalue;
+
+ if (length < powersOf10.length) {
+ if (isInteger(buffer, from, length)) {
+ lvalue = parseIntFromToIgnoreDot(buffer, from, index);
+ } else {
+ lvalue = parseLongFromToIgnoreDot(buffer, from, index);
+ }
+
+ double power = powersOf10[digitsPastPoint];
+ value = lvalue / power;
+ } else {
+ value = Double.parseDouble(new String(buffer, from, length));
+ }
+ } else {
+ value = Double.parseDouble(new String(buffer, from, index - from));
+ }
+
+ return value;
+ }
+
+ public static int skipWhiteSpace(char[] array, int index, final int length) {
+ int c;
+ for (; index < length; index++) {
+ c = array[index];
+ if (c > 32) {
+ return index;
+ }
+ }
+ return index;
+ }
+
+ public static char[] readNumber(char[] array, int idx, final int len) {
+ final int startIndex = idx;
+
+ while (true) {
+ if (!CharScanner.isDecimalDigit(array[idx])) {
+ break;
+ } else {
+ idx++;
+ if (idx >= len) break;
+ }
+ }
+
+ return ArrayUtils.copyRange(array, startIndex, idx);
+ }
+
+ public static String errorDetails(String message, char[] array, int index, int ch) {
+ CharBuf buf = CharBuf.create(255);
+
+ buf.addLine(message);
+
+ buf.addLine("");
+ buf.addLine("The current character read is " + debugCharDescription(ch));
+
+ buf.addLine(message);
+
+ int line = 0;
+ int lastLineIndex = 0;
+
+ for (int i = 0; i < index && i < array.length; i++) {
+ if (array[i] == '\n') {
+ line++;
+ lastLineIndex = i + 1;
+ }
+ }
+
+ int count = 0;
+
+ for (int i = lastLineIndex; i < array.length; i++, count++) {
+ if (array[i] == '\n') {
+ break;
+ }
+ }
+
+ buf.addLine("line number " + (line + 1));
+ buf.addLine("index number " + index);
+
+ try {
+ buf.addLine(new String(array, lastLineIndex, count));
+ } catch (Exception ex) {
+ try {
+ int start = index = (index - 10 < 0) ? 0 : index - 10;
+
+ buf.addLine(new String(array, start, index));
+ } catch (Exception ex2) {
+ buf.addLine(new String(array, 0, array.length));
+ }
+ }
+ for (int i = 0; i < (index - lastLineIndex); i++) {
+ buf.add('.');
+ }
+ buf.add('^');
+
+ return buf.toString();
+ }
+
+ public static String debugCharDescription(int c) {
+ String charString;
+ if (c == ' ') {
+ charString = "[SPACE]";
+ } else if (c == '\t') {
+ charString = "[TAB]";
+ } else if (c == '\n') {
+ charString = "[NEWLINE]";
+ } else {
+ charString = "'" + (char) c + "'";
+ }
+
+ charString = charString + " with an int value of " + ((int) c);
+ return charString;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/5a3f9996/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharSequenceValue.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharSequenceValue.java b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharSequenceValue.java
new file mode 100644
index 0000000..40ebc16
--- /dev/null
+++ b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharSequenceValue.java
@@ -0,0 +1,278 @@
+/*
+ * 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.apache.groovy.json.internal;
+
+import groovy.json.JsonException;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Date;
+
+import static org.apache.groovy.json.internal.CharScanner.isInteger;
+import static org.apache.groovy.json.internal.CharScanner.parseIntFromTo;
+import static org.apache.groovy.json.internal.CharScanner.parseLongFromTo;
+import static org.apache.groovy.json.internal.Exceptions.die;
+
+/**
+ * @author Rick Hightower
+ */
+public class CharSequenceValue implements Value, CharSequence {
+
+ private final Type type;
+ private final boolean checkDate;
+ private final boolean decodeStrings;
+
+ private char[] buffer;
+ private boolean chopped;
+ private int startIndex;
+ private int endIndex;
+ private Object value;
+
+ public CharSequenceValue(boolean chop, Type type, int startIndex, int endIndex, char[] buffer,
+ boolean encoded, boolean checkDate) {
+ this.type = type;
+ this.checkDate = checkDate;
+ this.decodeStrings = encoded;
+
+ if (chop) {
+ try {
+ this.buffer = ArrayUtils.copyRange(buffer, startIndex, endIndex);
+ } catch (Exception ex) {
+ Exceptions.handle(ex);
+ }
+ this.startIndex = 0;
+ this.endIndex = this.buffer.length;
+ this.chopped = true;
+ } else {
+ this.startIndex = startIndex;
+ this.endIndex = endIndex;
+ this.buffer = buffer;
+ }
+ }
+
+ public String toString() {
+ if (startIndex == 0 && endIndex == buffer.length) {
+ return FastStringUtils.noCopyStringFromChars(buffer);
+ } else {
+ return new String(buffer, startIndex, (endIndex - startIndex));
+ }
+ }
+
+ public final Object toValue() {
+ return value != null ? value : (value = doToValue());
+ }
+
+ public <T extends Enum> T toEnum(Class<T> cls) {
+ switch (type) {
+ case STRING:
+ return toEnum(cls, stringValue());
+ case INTEGER:
+ return toEnum(cls, intValue());
+ case NULL:
+ return null;
+ }
+ die("toEnum " + cls + " value was " + stringValue());
+ return null;
+ }
+
+ public static <T extends Enum> T toEnum(Class<T> cls, String value) {
+ try {
+ return (T) Enum.valueOf(cls, value);
+ } catch (Exception ex) {
+ return (T) Enum.valueOf(cls, value.toUpperCase().replace('-', '_'));
+ }
+ }
+
+ public static <T extends Enum> T toEnum(Class<T> cls, int value) {
+ T[] enumConstants = cls.getEnumConstants();
+ for (T e : enumConstants) {
+ if (e.ordinal() == value) {
+ return e;
+ }
+ }
+ die("Can't convert ordinal value " + value + " into enum of type " + cls);
+ return null;
+ }
+
+ public boolean isContainer() {
+ return false;
+ }
+
+ private Object doToValue() {
+ switch (type) {
+ case DOUBLE:
+ return doubleValue();
+ case INTEGER:
+ if (isInteger(buffer, startIndex, endIndex - startIndex)) {
+ return intValue();
+ } else {
+ return longValue();
+ }
+ case STRING:
+ if (checkDate) {
+ Date date = null;
+ if (Dates.isISO8601QuickCheck(buffer, startIndex, endIndex)) {
+ if (Dates.isJsonDate(buffer, startIndex, endIndex)) {
+ date = Dates.fromJsonDate(buffer, startIndex, endIndex);
+ } else if (Dates.isISO8601(buffer, startIndex, endIndex)) {
+ date = Dates.fromISO8601(buffer, startIndex, endIndex);
+ } else {
+ return stringValue();
+ }
+
+ if (date == null) {
+ return stringValue();
+ } else {
+ return date;
+ }
+ }
+ }
+ return stringValue();
+ }
+ die();
+ return null;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Value)) return false;
+
+ CharSequenceValue value1 = (CharSequenceValue) o;
+
+ if (endIndex != value1.endIndex) return false;
+ if (startIndex != value1.startIndex) return false;
+ if (!Arrays.equals(buffer, value1.buffer)) return false;
+ if (type != value1.type) return false;
+ return value != null ? value.equals(value1.value) : value1.value == null;
+
+ }
+
+ public int hashCode() {
+ int result = type != null ? type.hashCode() : 0;
+ result = 31 * result + (buffer != null ? Arrays.hashCode(buffer) : 0);
+ result = 31 * result + startIndex;
+ result = 31 * result + endIndex;
+ result = 31 * result + (value != null ? value.hashCode() : 0);
+ return result;
+ }
+
+ public final int length() {
+ return buffer.length;
+ }
+
+ public final char charAt(int index) {
+ return buffer[index];
+ }
+
+ public final CharSequence subSequence(int start, int end) {
+ return new CharSequenceValue(false, type, start, end, buffer, decodeStrings, checkDate);
+ }
+
+ public BigDecimal bigDecimalValue() {
+ return new BigDecimal(buffer, startIndex, endIndex - startIndex);
+ }
+
+ public BigInteger bigIntegerValue() {
+ return new BigInteger(toString());
+ }
+
+ public String stringValue() {
+ if (this.decodeStrings) {
+ return JsonStringDecoder.decodeForSure(buffer, startIndex, endIndex);
+ } else {
+ return toString();
+ }
+ }
+
+ public String stringValueEncoded() {
+ return JsonStringDecoder.decode(buffer, startIndex, endIndex);
+ }
+
+ public Date dateValue() {
+ if (type == Type.STRING) {
+
+ if (Dates.isISO8601QuickCheck(buffer, startIndex, endIndex)) {
+
+ if (Dates.isJsonDate(buffer, startIndex, endIndex)) {
+ return Dates.fromJsonDate(buffer, startIndex, endIndex);
+
+ } else if (Dates.isISO8601(buffer, startIndex, endIndex)) {
+ return Dates.fromISO8601(buffer, startIndex, endIndex);
+ } else {
+ throw new JsonException("Unable to convert " + stringValue() + " to date ");
+ }
+ } else {
+ throw new JsonException("Unable to convert " + stringValue() + " to date ");
+ }
+ } else {
+ return new Date(Dates.utc(longValue()));
+ }
+ }
+
+ public int intValue() {
+ int sign = 1;
+ if (buffer[startIndex] == '-') {
+ startIndex++;
+ sign = -1;
+ }
+ return parseIntFromTo(buffer, startIndex, endIndex) * sign;
+ }
+
+ public long longValue() {
+ if (isInteger(buffer, startIndex, endIndex - startIndex)) {
+ return parseIntFromTo(buffer, startIndex, endIndex);
+ } else {
+ return parseLongFromTo(buffer, startIndex, endIndex);
+ }
+ }
+
+ public byte byteValue() {
+ return (byte) intValue();
+ }
+
+ public short shortValue() {
+ return (short) intValue();
+ }
+
+ public double doubleValue() {
+ return CharScanner.parseDouble(this.buffer, startIndex, endIndex);
+ }
+
+ public boolean booleanValue() {
+ return Boolean.parseBoolean(toString());
+ }
+
+ public float floatValue() {
+ return CharScanner.parseFloat(this.buffer, startIndex, endIndex);
+ }
+
+ public final void chop() {
+ if (!chopped) {
+ this.chopped = true;
+ this.buffer = ArrayUtils.copyRange(buffer, startIndex, endIndex);
+ this.startIndex = 0;
+ this.endIndex = this.buffer.length;
+ }
+ }
+
+ public char charValue() {
+ return buffer[startIndex];
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/5a3f9996/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharacterSource.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharacterSource.java b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharacterSource.java
new file mode 100644
index 0000000..6fdecd4
--- /dev/null
+++ b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharacterSource.java
@@ -0,0 +1,81 @@
+/*
+ * 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.apache.groovy.json.internal;
+
+/**
+ * @author Richard Hightower
+ */
+public interface CharacterSource {
+
+ /**
+ * Skip white space.
+ */
+ void skipWhiteSpace();
+
+ /**
+ * returns the next character moving the file pointer or index to the next location.
+ */
+ int nextChar();
+
+ /**
+ * returns the current character without changing the IO pointer or index.
+ */
+ int currentChar();
+
+ /**
+ * Checks to see if there is a next character.
+ */
+ boolean hasChar();
+
+ /**
+ * Useful for finding constants in a string like true, false, etc.
+ */
+ boolean consumeIfMatch(char[] match);
+
+ /**
+ * This is mostly for debugging and testing.
+ */
+ int location();
+
+ /**
+ * Combines the operations of nextChar and hasChar.
+ * Characters is -1 if not found which signifies end of file.
+ * This might be preferable to avoid two method calls.
+ */
+ int safeNextChar();
+
+ /**
+ * Used to find strings and their ilk
+ * Finds the next non-escaped char
+ *
+ * @param ch character to find
+ * @param esc escape character to avoid next char if escaped
+ * @return list of chars until this is found.
+ */
+ char[] findNextChar(int ch, int esc);
+
+ boolean hadEscape();
+
+ /**
+ * Reads a number from the character source.
+ */
+ char[] readNumber();
+
+ String errorDetails(String message);
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/5a3f9996/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/Chr.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/Chr.java b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/Chr.java
new file mode 100644
index 0000000..6996a8e
--- /dev/null
+++ b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/Chr.java
@@ -0,0 +1,213 @@
+/*
+ * 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.apache.groovy.json.internal;
+
+/**
+ * @author Rick Hightower
+ */
+public class Chr {
+
+ public static char[] array(final char... array) {
+ return array;
+ }
+
+ public static char[] chars(final String array) {
+ return array.toCharArray();
+ }
+
+ public static boolean in(char value, char[] array) {
+ for (char currentValue : array) {
+ if (currentValue == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean in(int value, char[] array) {
+ for (int currentValue : array) {
+ if (currentValue == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean in(char value, int offset, char[] array) {
+ for (int index = offset; index < array.length; index++) {
+ char currentValue = array[index];
+ if (currentValue == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean in(char value, int offset, int end, char[] array) {
+ for (int index = offset; index < end; index++) {
+ char currentValue = array[index];
+ if (currentValue == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static char[] grow(char[] array, final int size) {
+ char[] newArray = new char[array.length + size];
+ arraycopy(array, 0, newArray, 0, array.length);
+ return newArray;
+ }
+
+ public static char[] grow(char[] array) {
+ char[] newArray = new char[array.length * 2];
+ arraycopy(array, 0, newArray, 0, array.length);
+ return newArray;
+ }
+
+ public static char[] copy(char[] array) {
+ char[] newArray = new char[array.length];
+ arraycopy(array, 0, newArray, 0, array.length);
+ return newArray;
+ }
+
+ public static char[] copy(char[] array, int offset, int length) {
+ char[] newArray = new char[length];
+ arraycopy(array, offset, newArray, 0, length);
+ return newArray;
+ }
+
+ public static char[] add(char[] array, char v) {
+ char[] newArray = new char[array.length + 1];
+ arraycopy(array, 0, newArray, 0, array.length);
+ newArray[array.length] = v;
+ return newArray;
+ }
+
+ public static char[] add(char[] array, String str) {
+ return add(array, str.toCharArray());
+ }
+
+ public static char[] add(char[] array, StringBuilder stringBuilder) {
+ return add(array, getCharsFromStringBuilder(stringBuilder));
+ }
+
+ public static char[] add(char[] array, char[] array2) {
+ char[] newArray = new char[array.length + array2.length];
+ arraycopy(array, 0, newArray, 0, array.length);
+ arraycopy(array2, 0, newArray, array.length, array2.length);
+ return newArray;
+ }
+
+ /* End universal methods. */
+
+ private static char[] getCharsFromStringBuilder(StringBuilder sbuf) {
+ final int length = sbuf.length();
+ char[] array2 = new char[length];
+ sbuf.getChars(0, length, array2, 0);
+ return array2;
+ }
+
+ public static char[] lpad(final char[] in, final int size, char pad) {
+ if (in.length >= size) {
+ return in;
+ }
+
+ int delta = size - in.length;
+ int index = 0;
+ char[] newArray = new char[size];
+
+ for (; index < delta; index++) {
+ newArray[index] = pad;
+ }
+
+ for (int index2 = 0; index2 < in.length; index++, index2++) {
+ newArray[index] = in[index2];
+ }
+
+ return newArray;
+ }
+
+ public static boolean contains(char[] chars, char c, int start, final int length) {
+ final int to = length + start;
+ for (int index = start; index < to; index++) {
+ char ch = chars[index];
+ if (ch == c) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static void _idx(char[] buffer, int location, byte[] chars) {
+ int index2 = 0;
+ int endLocation = (location + chars.length);
+ for (int index = location; index < endLocation; index++, index2++) {
+ buffer[index] = (char) chars[index2];
+ }
+ }
+
+ public static void _idx(final char[] array, int startIndex, char[] input) {
+ try {
+ arraycopy(input, 0, array, startIndex, input.length);
+ } catch (Exception ex) {
+ Exceptions.handle(String.format("array size %d, startIndex %d, input length %d",
+ array.length, startIndex, input.length), ex);
+ }
+ }
+
+ private static void arraycopy(final char[] src, final int srcPos, final char[] dest, final int destPos, final int length) {
+ System.arraycopy(src, srcPos, dest, destPos, length);
+ }
+
+ public static void _idx(final char[] array, int startIndex, char[] input, final int inputLength) {
+ try {
+ arraycopy(input, 0, array, startIndex, inputLength);
+ } catch (Exception ex) {
+ Exceptions.handle(String.format("array size %d, startIndex %d, input length %d",
+ array.length, startIndex, input.length), ex);
+ }
+ }
+
+ public static void _idx(char[] buffer, int location, byte[] chars, int start, int end) {
+ int index2 = start;
+ int endLocation = (location + (end - start));
+ for (int index = location; index < endLocation; index++, index2++) {
+ buffer[index] = (char) chars[index2];
+ }
+ }
+
+ public static char[] add(char[]... strings) {
+ int length = 0;
+ for (char[] str : strings) {
+ if (str == null) {
+ continue;
+ }
+ length += str.length;
+ }
+ CharBuf builder = CharBuf.createExact(length);
+ for (char[] str : strings) {
+ if (str == null) {
+ continue;
+ }
+ builder.add(str);
+ }
+ return builder.toCharArray();
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/5a3f9996/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/Dates.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/Dates.java b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/Dates.java
new file mode 100644
index 0000000..0cd95f8
--- /dev/null
+++ b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/Dates.java
@@ -0,0 +1,184 @@
+/*
+ * 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.apache.groovy.json.internal;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * @author Rick Hightower
+ */
+public class Dates {
+
+ private static TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC");
+
+ public static long utc(long time) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(time);
+ calendar.setTimeZone(UTC_TIME_ZONE);
+ return calendar.getTime().getTime();
+ }
+
+ private static Date internalDate(TimeZone tz, int year, int month, int day, int hour,
+ int minute, int second, int miliseconds) {
+
+ Calendar calendar = Calendar.getInstance();
+
+ calendar.set(Calendar.YEAR, year);
+ calendar.set(Calendar.MONTH, month - 1);
+ calendar.set(Calendar.DAY_OF_MONTH, day);
+ calendar.set(Calendar.HOUR_OF_DAY, hour);
+ calendar.set(Calendar.MINUTE, minute);
+ calendar.set(Calendar.SECOND, second);
+ calendar.set(Calendar.MILLISECOND, miliseconds);
+
+ calendar.setTimeZone(tz);
+
+ return calendar.getTime();
+ }
+
+ public static Date toDate(TimeZone tz, int year, int month, int day,
+ int hour, int minute, int second) {
+ return internalDate(tz, year, month, day, hour, minute, second, 0);
+ }
+
+ public static Date toDate(TimeZone tz, int year, int month, int day,
+ int hour, int minute, int second, int miliseconds) {
+ return internalDate(tz, year, month, day, hour, minute, second, miliseconds);
+ }
+
+ static final int SHORT_ISO_8601_TIME_LENGTH = "1994-11-05T08:15:30Z".length();
+ static final int LONG_ISO_8601_TIME_LENGTH = "1994-11-05T08:15:30-05:00".length();
+ public static final int JSON_TIME_LENGTH = "2013-12-14T01:55:33.412Z".length();
+
+ public static Date fromISO8601(char[] charArray, int from, int to) {
+ try {
+ if (isISO8601(charArray, from, to)) {
+ int year = CharScanner.parseIntFromTo(charArray, from, from + 4);
+ int month = CharScanner.parseIntFromTo(charArray, from + 5, from + 7);
+ int day = CharScanner.parseIntFromTo(charArray, from + 8, from + 10);
+ int hour = CharScanner.parseIntFromTo(charArray, from + 11, from + 13);
+
+ int minute = CharScanner.parseIntFromTo(charArray, from + 14, from + 16);
+
+ int second = CharScanner.parseIntFromTo(charArray, from + 17, from + 19);
+ TimeZone tz = null;
+
+ if (charArray[from + 19] == 'Z') {
+ tz = TimeZone.getTimeZone("GMT");
+ } else {
+ String tzStr = "GMT" + String.valueOf(charArray, from + 19, 6);
+ tz = TimeZone.getTimeZone(tzStr);
+ }
+ return toDate(tz, year, month, day, hour, minute, second);
+ } else {
+ return null;
+ }
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+
+ public static Date fromJsonDate(char[] charArray, int from, int to) {
+ try {
+ if (isJsonDate(charArray, from, to)) {
+ int year = CharScanner.parseIntFromTo(charArray, from, from + 4);
+ int month = CharScanner.parseIntFromTo(charArray, from + 5, from + 7);
+ int day = CharScanner.parseIntFromTo(charArray, from + 8, from + 10);
+ int hour = CharScanner.parseIntFromTo(charArray, from + 11, from + 13);
+
+ int minute = CharScanner.parseIntFromTo(charArray, from + 14, from + 16);
+
+ int second = CharScanner.parseIntFromTo(charArray, from + 17, from + 19);
+
+ int miliseconds = CharScanner.parseIntFromTo(charArray, from + 20, from + 23);
+
+ TimeZone tz = TimeZone.getTimeZone("GMT");
+
+ return toDate(tz, year, month, day, hour, minute, second, miliseconds);
+ } else {
+ return null;
+ }
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+
+ public static boolean isISO8601(char[] charArray, int start, int to) {
+ boolean valid = true;
+ final int length = to - start;
+
+ if (length == SHORT_ISO_8601_TIME_LENGTH) {
+ valid &= (charArray[start + 19] == 'Z');
+ } else if (length == LONG_ISO_8601_TIME_LENGTH) {
+ valid &= (charArray[start + 19] == '-' || charArray[start + 19] == '+');
+ valid &= (charArray[start + 22] == ':');
+ } else {
+ return false;
+ }
+
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4
+ // "1 9 9 4 - 1 1 - 0 5 T 0 8 : 1 5 : 3 0 - 0 5 : 0 0
+
+ valid &= (charArray[start + 4] == '-') &&
+ (charArray[start + 7] == '-') &&
+ (charArray[start + 10] == 'T') &&
+ (charArray[start + 13] == ':') &&
+ (charArray[start + 16] == ':');
+
+ return valid;
+ }
+
+ public static boolean isISO8601QuickCheck(char[] charArray, int start, int to) {
+ final int length = to - start;
+
+ try {
+ return length == JSON_TIME_LENGTH || length == LONG_ISO_8601_TIME_LENGTH
+ || length == SHORT_ISO_8601_TIME_LENGTH || (length >= 17 && (charArray[start + 16] == ':'));
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return false;
+ }
+ }
+
+ public static boolean isJsonDate(char[] charArray, int start, int to) {
+ boolean valid = true;
+ final int length = to - start;
+
+ if (length != JSON_TIME_LENGTH) {
+ return false;
+ }
+
+ valid &= (charArray[start + 19] == '.' || charArray[start + 19] == '+');
+
+ if (!valid) {
+ return false;
+ }
+
+ valid &= (charArray[start + 4] == '-') &&
+ (charArray[start + 7] == '-') &&
+ (charArray[start + 10] == 'T') &&
+ (charArray[start + 13] == ':') &&
+ (charArray[start + 16] == ':');
+
+ return valid;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/5a3f9996/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/Exceptions.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/Exceptions.java b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/Exceptions.java
new file mode 100644
index 0000000..e122778
--- /dev/null
+++ b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/Exceptions.java
@@ -0,0 +1,178 @@
+/*
+ * 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.apache.groovy.json.internal;
+
+import groovy.json.JsonException;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.Collections;
+
+/**
+ * @author Rick Hightower
+ */
+public class Exceptions {
+
+ public static boolean die() {
+ throw new JsonInternalException("died");
+ }
+
+ public static boolean die(String message) {
+ throw new JsonInternalException(message);
+ }
+
+ public static <T> T die(Class<T> clazz, String message) {
+ throw new JsonInternalException(message);
+ }
+
+ public static void handle(java.lang.Exception e) {
+ throw new JsonInternalException(e);
+ }
+
+ public static <T> T handle(Class<T> clazz, java.lang.Exception e) {
+ if (e instanceof JsonInternalException) {
+ throw (JsonInternalException) e;
+ }
+ throw new JsonInternalException(e);
+ }
+
+ public static <T> T handle(Class<T> clazz, String message, Throwable e) {
+ throw new JsonInternalException(message, e);
+ }
+
+ public static void handle(String message, Throwable e) {
+ throw new JsonInternalException(message, e);
+ }
+
+ public static class JsonInternalException extends JsonException {
+
+ public JsonInternalException(String message) {
+ super(message);
+ }
+
+ public JsonInternalException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public JsonInternalException(Throwable cause) {
+ super("Wrapped Exception", cause);
+ }
+
+ public void printStackTrace(PrintStream s) {
+ s.println(this.getMessage());
+ if (getCause() != null) {
+ s.println("This Exception was wrapped, the original exception\n" +
+ "stack trace is:\n");
+ getCause().printStackTrace(s);
+ } else {
+ super.printStackTrace(s);
+ }
+ }
+
+ public String getMessage() {
+ return super.getMessage() + (getCause() == null ? "" :
+ getCauseMessage());
+ }
+
+ private String getCauseMessage() {
+ return "\n CAUSE " + getCause().getClass().getName() + " :: " +
+ getCause().getMessage();
+ }
+
+ public String getLocalizedMessage() {
+ return this.getMessage();
+ }
+
+ public StackTraceElement[] getStackTrace() {
+ if (getCause() != null) {
+ return getCause().getStackTrace();
+ } else {
+ return super.getStackTrace();
+ }
+ }
+
+ public Throwable getCause() {
+ return super.getCause();
+ }
+
+ public void printStackTrace(PrintWriter s) {
+ s.println(this.getMessage());
+
+ if (getCause() != null) {
+ s.println("This Exception was wrapped, the original exception\n" +
+ "stack trace is:\n");
+ getCause().printStackTrace(s);
+ } else {
+ super.printStackTrace(s);
+ }
+ }
+
+ public void printStackTrace() {
+ System.err.println(this.getMessage());
+
+ if (getCause() != null) {
+ System.err.println("This Exception was wrapped, the original exception\n" +
+ "stack trace is:\n");
+ getCause().printStackTrace();
+ } else {
+ super.printStackTrace();
+ }
+ }
+ }
+
+ public static String toString(Exception ex) {
+ CharBuf buffer = CharBuf.create(255);
+ buffer.addLine(ex.getLocalizedMessage());
+
+ final StackTraceElement[] stackTrace = ex.getStackTrace();
+ for (StackTraceElement element : stackTrace) {
+ buffer.add(element.getClassName());
+ sputs(buffer, "class", element.getClassName(),
+ "method", element.getMethodName(), "line", element.getLineNumber());
+ }
+
+ return buffer.toString();
+ }
+
+ public static String sputs(CharBuf buf, Object... messages) {
+ int index = 0;
+ for (Object message : messages) {
+ if (index != 0) {
+ buf.add(' ');
+ }
+ index++;
+
+ if (message == null) {
+ buf.add("<NULL>");
+ } else if (message.getClass().isArray()) {
+ buf.add(Collections.singletonList(message).toString());
+ } else {
+ buf.add(message.toString());
+ }
+ }
+ buf.add('\n');
+
+ return buf.toString();
+ }
+
+ public static String sputs(Object... messages) {
+ CharBuf buf = CharBuf.create(100);
+ return sputs(buf, messages);
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/5a3f9996/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/FastStringUtils.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/FastStringUtils.java b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/FastStringUtils.java
new file mode 100644
index 0000000..a8c5dc6
--- /dev/null
+++ b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/FastStringUtils.java
@@ -0,0 +1,85 @@
+/*
+ * 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.apache.groovy.json.internal;
+
+import org.apache.groovy.json.DefaultFastStringService;
+import org.apache.groovy.json.FastStringService;
+import org.apache.groovy.json.FastStringServiceFactory;
+
+import java.util.ServiceLoader;
+
+/**
+ * Internal class for fast processing of Strings during JSON parsing
+ */
+public class FastStringUtils {
+
+ private static class ServiceHolder {
+ static final FastStringService INSTANCE = loadService();
+
+ private static FastStringService loadService() {
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+// left classloading very simple in light of potential changes needed for jdk9
+// that means you might need @GrabConfig(systemClassLoader=true) if getting json via grab
+// ClassLoader rootLoader = DefaultGroovyMethods.getRootLoader(loader);
+ ServiceLoader<FastStringServiceFactory> serviceLoader = ServiceLoader.load(FastStringServiceFactory.class, loader);
+ FastStringService found = null;
+ for (FastStringServiceFactory factory : serviceLoader) {
+ FastStringService service = factory.getService();
+ if (service != null) {
+ found = service;
+ if (!(service instanceof DefaultFastStringService)) {
+ break;
+ }
+ }
+ }
+ return found;
+ }
+ }
+
+ private static FastStringService getService() {
+ if (ServiceHolder.INSTANCE == null) {
+ throw new RuntimeException("Unable to load FastStringService");
+ }
+ return ServiceHolder.INSTANCE;
+ }
+
+ /**
+ * @param string string to grab array from.
+ * @return char array from string
+ */
+ public static char[] toCharArray(final String string) {
+ return getService().toCharArray(string);
+ }
+
+ /**
+ * @param charSequence to grab array from.
+ * @return char array from char sequence
+ */
+ public static char[] toCharArray(final CharSequence charSequence) {
+ return toCharArray(charSequence.toString());
+ }
+
+ /**
+ * @param chars to shove array into.
+ * @return new string with chars copied into it
+ */
+ public static String noCopyStringFromChars(final char[] chars) {
+ return getService().noCopyStringFromChars(chars);
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/5a3f9996/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/IO.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/IO.java b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/IO.java
new file mode 100644
index 0000000..fdfec3e
--- /dev/null
+++ b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/IO.java
@@ -0,0 +1,91 @@
+/*
+ * 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.apache.groovy.json.internal;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+
+/**
+ * @author Rick Hightower
+ */
+public class IO {
+
+ private static final int EOF = -1;
+
+ private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+
+ public static CharBuf read(Reader input, CharBuf charBuf, final int bufSize) {
+ if (charBuf == null) {
+ charBuf = CharBuf.create(bufSize);
+ } else {
+ charBuf.readForRecycle();
+ }
+
+ try {
+ char[] buffer = charBuf.toCharArray();
+ int size = input.read(buffer);
+ if (size != -1) {
+ charBuf._len(size);
+ }
+ if (size < 0) {
+ return charBuf;
+ }
+
+ copy(input, charBuf);
+ } catch (IOException e) {
+ Exceptions.handle(e);
+ } finally {
+ try {
+ input.close();
+ } catch (IOException e) {
+ Exceptions.handle(e);
+ }
+ }
+
+ return charBuf;
+ }
+
+ public static int copy(Reader input, Writer output) {
+ long count = copyLarge(input, output);
+ if (count > Integer.MAX_VALUE) {
+ return -1;
+ }
+ return (int) count;
+ }
+
+ public static long copyLarge(Reader reader, Writer writer) {
+ return copyLarge(reader, writer, new char[DEFAULT_BUFFER_SIZE]);
+ }
+
+ public static long copyLarge(Reader reader, Writer writer, char[] buffer) {
+ long count = 0;
+ int n;
+
+ try {
+ while (EOF != (n = reader.read(buffer))) {
+ writer.write(buffer, 0, n);
+ count += n;
+ }
+ } catch (IOException e) {
+ Exceptions.handle(e);
+ }
+ return count;
+ }
+}