From commits-return-5672-archive-asf-public=cust-asf.ponee.io@groovy.apache.org Wed Feb 28 12:37:54 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id 0468B18067A for ; Wed, 28 Feb 2018 12:37:51 +0100 (CET) Received: (qmail 72645 invoked by uid 500); 28 Feb 2018 11:37:51 -0000 Mailing-List: contact commits-help@groovy.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@groovy.apache.org Delivered-To: mailing list commits@groovy.apache.org Received: (qmail 72544 invoked by uid 99); 28 Feb 2018 11:37:51 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 28 Feb 2018 11:37:51 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 8B37FF320D; Wed, 28 Feb 2018 11:37:50 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: paulk@apache.org To: commits@groovy.apache.org Date: Wed, 28 Feb 2018 11:37:53 -0000 Message-Id: <64f31752e9d14bbfa875d60e5fdb3f84@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [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 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(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 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(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 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(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 bigDCache; + + public CharBuf addBigDecimal(BigDecimal key) { + if (bigDCache == null) { + bigDCache = new SimpleCache(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 bigICache; + + public CharBuf addBigInteger(BigInteger key) { + if (bigICache == null) { + bigICache = new SimpleCache(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 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(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 toEnum(Class 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 toEnum(Class cls, String value) { + try { + return (T) Enum.valueOf(cls, value); + } catch (Exception ex) { + return (T) Enum.valueOf(cls, value.toUpperCase().replace('-', '_')); + } + } + + public static T toEnum(Class 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 die(Class clazz, String message) { + throw new JsonInternalException(message); + } + + public static void handle(java.lang.Exception e) { + throw new JsonInternalException(e); + } + + public static T handle(Class clazz, java.lang.Exception e) { + if (e instanceof JsonInternalException) { + throw (JsonInternalException) e; + } + throw new JsonInternalException(e); + } + + public static T handle(Class 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(""); + } 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 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; + } +}