From 56d3113012ff12b9320ea9934b45efd5a5b5ef10 Mon Sep 17 00:00:00 2001 From: salyh Date: Thu, 24 Jul 2014 16:10:26 +0200 Subject: [PATCH] FLEECE-9 Signed-off-by: salyh --- .../apache/fleece/core/JsonBaseStreamParser.java | 830 ------------------ .../fleece/core/JsonCharBufferStreamParser.java | 93 -- .../java/org/apache/fleece/core/JsonChars.java | 28 +- .../org/apache/fleece/core/JsonDoubleImpl.java | 4 +- .../org/apache/fleece/core/JsonLocationImpl.java | 6 +- .../apache/fleece/core/JsonParserFactoryImpl.java | 25 +- .../org/apache/fleece/core/JsonReaderImpl.java | 11 +- .../apache/fleece/core/JsonStreamParserImpl.java | 968 +++++++++++++++++++++ .../fleece/core/RFC4627AwareInputStreamReader.java | 110 +++ .../main/java/org/apache/fleece/core/Strings.java | 14 +- .../fleece/core/JsonArrayBuilderImplTest.java | 55 +- .../org/apache/fleece/core/JsonArrayImplTest.java | 31 +- .../apache/fleece/core/JsonGeneratorImplTest.java | 21 +- .../org/apache/fleece/core/JsonNumberTest.java | 51 ++ .../fleece/core/JsonObjectBuilderImplTest.java | 4 +- .../org/apache/fleece/core/JsonObjectImplTest.java | 6 +- .../org/apache/fleece/core/JsonParserTest.java | 493 ++++++++++- .../org/apache/fleece/core/JsonReaderImplTest.java | 32 + .../org/apache/fleece/core/JsonWriterImplTest.java | 9 +- .../java/org/apache/fleece/core/LocationTest.java | 196 +++++ .../src/test/resources/json/fails/fail23.json | 4 +- .../src/test/resources/json/fails/fail25.json | 2 +- .../src/test/resources/json/fails/fail26.json | 7 + .../src/test/resources/json/fails/fail27.json | 7 + .../src/test/resources/json/fails/fail28.json | 7 + .../src/test/resources/json/fails/fail29.json | 7 + .../src/test/resources/json/fails/fail30.json | 7 + .../src/test/resources/json/fails/fail31.json | 7 + .../src/test/resources/json/fails/fail32.json | 7 + .../src/test/resources/json/fails/fail33.json | 7 + .../src/test/resources/json/fails/fail34.json | 7 + .../src/test/resources/json/fails/fail35.json | 7 + .../src/test/resources/json/fails/fail36.json | 3 + .../src/test/resources/json/fails/fail37.json | 4 + .../src/test/resources/json/fails/fail38.json | 3 + .../src/test/resources/json/fails/fail39.json | 3 + .../src/test/resources/json/fails/fail40.json | 4 + .../src/test/resources/json/fails/fail41.json | 3 + .../src/test/resources/json/fails/fail42.json | 3 + .../src/test/resources/json/fails/fail43.json | 3 + .../src/test/resources/json/fails/fail44.json | 3 + .../src/test/resources/json/fails/fail45.json | 3 + .../src/test/resources/json/fails/fail46.json | 3 + .../src/test/resources/json/fails/fail47.json | 1 + .../src/test/resources/json/fails/fail48.json | 6 + .../src/test/resources/json/fails/fail49.json | 1 + .../src/test/resources/json/fails/fail50.json | 4 + .../src/test/resources/json/fails/fail51.json | 1 + .../src/test/resources/json/fails/fail52.json | 1 + .../src/test/resources/json/fails/fail53.json | 11 + .../src/test/resources/json/fails/fail54.json | 12 + .../src/test/resources/json/fails/fail55.json | 12 + .../src/test/resources/json/fails/fail56.json | 11 + .../src/test/resources/json/fails/fail57.json | 1 + .../src/test/resources/json/fails/fail58.json | 3 + .../src/test/resources/json/fails/fail59.json | 1 + .../src/test/resources/json/fails/fail60.json | 1 + .../src/test/resources/json/fails/fail61.json | 3 + .../src/test/resources/json/fails/fail62.json | 3 + .../src/test/resources/json/fails/fail63.json | 3 + .../src/test/resources/json/fails/fail64.json | 3 + .../src/test/resources/json/fails/fail65.json | 3 + .../src/test/resources/json/fails/fail66.json | 3 + .../src/test/resources/json/fails/fail67.json | 3 + .../src/test/resources/json/fails/fail68.json | 3 + .../src/test/resources/json/fails/fail69.json | 3 + .../src/test/resources/json/fails/fail70.json | 3 + .../src/test/resources/json/fails/fail71.json | 3 + .../src/test/resources/json/fails/fail72.json | 3 + .../src/test/resources/json/fails/numbers.json | 18 + fleece-core/src/test/resources/json/numbers.json | 4 +- fleece-core/src/test/resources/json/simple2.json | 8 + .../src/test/resources/json/simple_utf16le.json | Bin 0 -> 136 bytes 73 files changed, 2258 insertions(+), 972 deletions(-) delete mode 100644 fleece-core/src/main/java/org/apache/fleece/core/JsonBaseStreamParser.java delete mode 100644 fleece-core/src/main/java/org/apache/fleece/core/JsonCharBufferStreamParser.java create mode 100644 fleece-core/src/main/java/org/apache/fleece/core/JsonStreamParserImpl.java create mode 100644 fleece-core/src/main/java/org/apache/fleece/core/RFC4627AwareInputStreamReader.java create mode 100644 fleece-core/src/test/java/org/apache/fleece/core/JsonNumberTest.java create mode 100644 fleece-core/src/test/java/org/apache/fleece/core/LocationTest.java create mode 100644 fleece-core/src/test/resources/json/fails/fail26.json create mode 100644 fleece-core/src/test/resources/json/fails/fail27.json create mode 100644 fleece-core/src/test/resources/json/fails/fail28.json create mode 100644 fleece-core/src/test/resources/json/fails/fail29.json create mode 100644 fleece-core/src/test/resources/json/fails/fail30.json create mode 100644 fleece-core/src/test/resources/json/fails/fail31.json create mode 100644 fleece-core/src/test/resources/json/fails/fail32.json create mode 100644 fleece-core/src/test/resources/json/fails/fail33.json create mode 100644 fleece-core/src/test/resources/json/fails/fail34.json create mode 100644 fleece-core/src/test/resources/json/fails/fail35.json create mode 100644 fleece-core/src/test/resources/json/fails/fail36.json create mode 100644 fleece-core/src/test/resources/json/fails/fail37.json create mode 100644 fleece-core/src/test/resources/json/fails/fail38.json create mode 100644 fleece-core/src/test/resources/json/fails/fail39.json create mode 100644 fleece-core/src/test/resources/json/fails/fail40.json create mode 100644 fleece-core/src/test/resources/json/fails/fail41.json create mode 100644 fleece-core/src/test/resources/json/fails/fail42.json create mode 100644 fleece-core/src/test/resources/json/fails/fail43.json create mode 100644 fleece-core/src/test/resources/json/fails/fail44.json create mode 100644 fleece-core/src/test/resources/json/fails/fail45.json create mode 100644 fleece-core/src/test/resources/json/fails/fail46.json create mode 100644 fleece-core/src/test/resources/json/fails/fail47.json create mode 100644 fleece-core/src/test/resources/json/fails/fail48.json create mode 100644 fleece-core/src/test/resources/json/fails/fail49.json create mode 100644 fleece-core/src/test/resources/json/fails/fail50.json create mode 100644 fleece-core/src/test/resources/json/fails/fail51.json create mode 100644 fleece-core/src/test/resources/json/fails/fail52.json create mode 100644 fleece-core/src/test/resources/json/fails/fail53.json create mode 100644 fleece-core/src/test/resources/json/fails/fail54.json create mode 100644 fleece-core/src/test/resources/json/fails/fail55.json create mode 100644 fleece-core/src/test/resources/json/fails/fail56.json create mode 100644 fleece-core/src/test/resources/json/fails/fail57.json create mode 100644 fleece-core/src/test/resources/json/fails/fail58.json create mode 100644 fleece-core/src/test/resources/json/fails/fail59.json create mode 100644 fleece-core/src/test/resources/json/fails/fail60.json create mode 100644 fleece-core/src/test/resources/json/fails/fail61.json create mode 100644 fleece-core/src/test/resources/json/fails/fail62.json create mode 100644 fleece-core/src/test/resources/json/fails/fail63.json create mode 100644 fleece-core/src/test/resources/json/fails/fail64.json create mode 100644 fleece-core/src/test/resources/json/fails/fail65.json create mode 100644 fleece-core/src/test/resources/json/fails/fail66.json create mode 100644 fleece-core/src/test/resources/json/fails/fail67.json create mode 100644 fleece-core/src/test/resources/json/fails/fail68.json create mode 100644 fleece-core/src/test/resources/json/fails/fail69.json create mode 100644 fleece-core/src/test/resources/json/fails/fail70.json create mode 100644 fleece-core/src/test/resources/json/fails/fail71.json create mode 100644 fleece-core/src/test/resources/json/fails/fail72.json create mode 100644 fleece-core/src/test/resources/json/fails/numbers.json create mode 100644 fleece-core/src/test/resources/json/simple2.json create mode 100644 fleece-core/src/test/resources/json/simple_utf16le.json diff --git a/fleece-core/src/main/java/org/apache/fleece/core/JsonBaseStreamParser.java b/fleece-core/src/main/java/org/apache/fleece/core/JsonBaseStreamParser.java deleted file mode 100644 index d4ba437..0000000 --- a/fleece-core/src/main/java/org/apache/fleece/core/JsonBaseStreamParser.java +++ /dev/null @@ -1,830 +0,0 @@ -/* - * 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.fleece.core; - -import javax.json.JsonException; -import javax.json.stream.JsonLocation; -import javax.json.stream.JsonParsingException; - -import java.io.IOException; -import java.math.BigDecimal; -import java.util.NoSuchElementException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static org.apache.fleece.core.Strings.asEscapedChar; - -public abstract class JsonBaseStreamParser implements JsonChars, - EscapedStringAwareJsonParser { - private static final Logger LOGGER = Logger.getLogger(JsonBaseStreamParser.class.getName()); - private static final boolean LOG = LOGGER.isLoggable(Level.FINE); - - private final int maxStringSize; - - // current state - private Event event = null; - private Event lastEvent = null; - private int lastSignificantChar = -1; - - protected final char[] currentValue; - private int valueLength = 0; - - // location - private int line = 1; - private int column = 1; - private int offset = 0; - - private boolean constructingStringValue = false; - private boolean withinArray = false; - private boolean stringValueIsKey = false; - - private boolean isCurrentNumberIntegral = false; - private Integer currentIntegralNumber = null; //for number from 0 - 9 - private BigDecimal currentBigDecimalNumber = null; - - private int openObjects = 0; - private int openArrays = 0; - private boolean escaped = false; - - protected JsonBaseStreamParser(final int maxStringLength, final char[] valueBuffer) { - this.maxStringSize = maxStringLength <= 0 ? 8192 : maxStringLength; - this.currentValue = valueBuffer; - } - - private void appendValue(final char c) { - if (valueLength >= maxStringSize) { - throw new JsonParsingException("to many chars", createLocation()); - } - - currentValue[valueLength] = c; - valueLength++; - } - - private void resetValue() { - valueLength = 0; - - } - - private String getValue() { - return new String(currentValue, 0, valueLength); - } - - @Override - public final boolean hasNext() { - return event == null || !(openArrays == 0 && openObjects == 0); - } - - private static boolean isAsciiDigit(final char value) { - return value >= ZERO && value <= NINE; - } - - private static boolean isHexDigit(final char value) { - return isAsciiDigit(value) || (value >= 'a' && value <= 'f') - || (value >= 'A' && value <= 'F'); - } - - private JsonLocationImpl createLocation() { - return new JsonLocationImpl(line, column, offset); - } - - private boolean ifConstructingStringValueAdd(char c) throws IOException { - - if(!constructingStringValue) { - return false; - } - - if (escaped) { - - if (c == 'u') { - final char[] tmp = read(4); - - for (final char aTmp : tmp) { - if (!isHexDigit(aTmp)) { - throw new JsonParsingException("unexpected character " + aTmp, createLocation()); - } - } - - if (LOG) { - LOGGER.fine((int) tmp[3] + "/" + (int) tmp[2] + "/" - + (int) tmp[1] + "/" + (int) tmp[0]); - } - - final int decimal = ((tmp[3]) - 48) + ((tmp[2]) - 48) * 16 - + ((tmp[1]) - 48) * 256 + ((tmp[0]) - 48) * 4096; - c = (char) decimal; - - } else { - c = asEscapedChar(c); - } - - escaped = false; - } - - appendValue(c); - - return true; - } - - protected abstract char readNextChar() throws IOException; - - protected abstract void mark(); - - private void resetToMark() { - - reset(); - offset--; - column--; - } - - protected abstract void reset(); - - private char read() throws IOException { - final char c = readNextChar(); - - if (LOG) { - LOGGER.fine("reading: " + c + " -> " + ((int) c)); - } - - if (c == -1) { - // hasNext = false; - throw new NoSuchElementException(); - } - - offset++; - column++; - - return c; - } - - private char[] read(final int count) throws IOException { - final char[] tmp = new char[count]; - - for (int i = 0; i < tmp.length; i++) { - tmp[i] = read(); - - } - - return tmp; - } - - // Event.START_ARRAY - // Event.START_OBJECT - - // Event.END_ARRAY - // Event.END_OBJECT - - // Event.KEY_NAME - - // ** 5 Value Event - // Event.VALUE_FALSE - // Event.VALUE_NULL - // Event.VALUE_NUMBER - // Event.VALUE_STRING - // Event.VALUE_TRUE - - // *********************** - // *********************** - // Significant chars (8) - - // 0 - start doc - // " - quote - // , - comma - - // : - separator - // { - start obj - // } - end obj - // [ - start arr - // ] - end arr - - @Override - public final Event next() { - int dosCount = 0; - lastEvent = event; - event = null; - isCurrentNumberIntegral = false; - currentBigDecimalNumber = null; - currentIntegralNumber = null; - - resetValue(); - - try { - while (true) { - final char c = read(); - - switch (c) { - - case START_OBJECT_CHAR: - - if (ifConstructingStringValueAdd(c)) { - continue; - } - - handleStartObject(c); - - break; - - case END_OBJECT_CHAR: - - if (ifConstructingStringValueAdd(c)) { - continue; - } - - handleEndObject(c); - - break; - case START_ARRAY_CHAR: - - if (ifConstructingStringValueAdd(c)) { - continue; - } - - handleStartArray(c); - - break; - case END_ARRAY_CHAR: - - if (ifConstructingStringValueAdd(c)) { - continue; - } - - handleEndArray(c); - break; - case EOL: - if (ifConstructingStringValueAdd(c)) { - throw new JsonParsingException("Unexpected character " - + c + " (" + (int) c + ")", createLocation()); - } - line++; - column = 1; - continue; // eol no allowed within a value - - case TAB: - case CR: - case SPACE: - if (ifConstructingStringValueAdd(c)) { // escaping - - continue; - - } else { - // dos check - if (dosCount >= maxStringSize) { - throw new JsonParsingException( - "max string size reached", createLocation()); - } - dosCount++; - } - - break; - case COMMA: - if (ifConstructingStringValueAdd(c)) { - continue; - } - - final char lastSignificant = (char) lastSignificantChar; - if (lastSignificantChar >= 0 - && lastSignificant != QUOTE - && lastSignificant != END_ARRAY_CHAR - && lastSignificant != END_OBJECT_CHAR) { - throw new JsonParsingException("Unexpected character " - + c + " (last significant was " - + lastSignificantChar + ")", createLocation()); - } - - lastSignificantChar = c; - - stringValueIsKey = true; - - break; - case KEY_SEPARATOR: - if (ifConstructingStringValueAdd(c)) { - continue; - } - - if (lastSignificantChar >= 0 - && (char) lastSignificantChar != QUOTE) { - throw new JsonParsingException("Unexpected character " - + c, createLocation()); - } - - lastSignificantChar = c; - - stringValueIsKey = false; - - break; - - case QUOTE: // must be escaped within a value - - if (handleQuote(c)) { - continue; - } else { - break; - } - - // non string values (literals) - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case MINUS: - case FALSE_F: // false - case TRUE_T: // true - case NULL_N: // null - - if (ifConstructingStringValueAdd(c)) { - continue; - } - - handleLiteral(c); - - break; - - // escape char - case ESCAPE_CHAR:// must be escaped within a value - if (!constructingStringValue) { - throw new JsonParsingException("Unexpected character " - + c, createLocation()); - } - - if (escaped) { - appendValue(ESCAPE_CHAR); - escaped = false; - } else { - escaped = true; - } - - break; - - // eof - case EOF: - - throw new NoSuchElementException(); - - default: - if (ifConstructingStringValueAdd(c)) { - continue; - } - lastSignificantChar = -2; - throw new JsonParsingException("Unexpected character " + c, - createLocation()); - - } - - if (event != null) { - return event; - } - } - } catch (final IOException e) { - throw new JsonParsingException("Unexpected IO Exception", e, createLocation()); - } - } - - private void handleStartObject(final char c) { - final char significantChar = (char) lastSignificantChar; // cast eagerly means we avoid 2 castings and are slwoer in error case only - if (lastSignificantChar == -2 - || (lastSignificantChar != -1 - && significantChar != KEY_SEPARATOR - && significantChar != COMMA && significantChar != START_ARRAY_CHAR)) { - throw new JsonParsingException("Unexpected character " + c - + " (last significant was " + lastSignificantChar + ")", - createLocation()); - } - - stringValueIsKey = true; - withinArray = false; - - lastSignificantChar = c; - openObjects++; - event = Event.START_OBJECT; - - } - - private void handleEndObject(final char c) { - final char significantChar = (char) lastSignificantChar; - if (lastSignificantChar >= 0 - && significantChar != START_OBJECT_CHAR - && significantChar != END_ARRAY_CHAR - && significantChar != QUOTE - && significantChar != END_OBJECT_CHAR) { - throw new JsonParsingException("Unexpected character " + c - + " (last significant was " + lastSignificantChar + ")", - createLocation()); - } - - if (openObjects == 0) { - throw new JsonParsingException("Unexpected character " + c, - createLocation()); - } - - lastSignificantChar = c; - openObjects--; - event = Event.END_OBJECT; - } - - private void handleStartArray(final char c) { - withinArray = true; - - final char significantChar = (char) lastSignificantChar; - if (lastSignificantChar == -2 - || (lastSignificantChar != -1 - && significantChar != KEY_SEPARATOR - && significantChar != COMMA - && significantChar != START_ARRAY_CHAR)) { - throw new JsonParsingException("Unexpected character " + c - + " (last significant was " + lastSignificantChar + ")", - createLocation()); - } - - lastSignificantChar = c; - openArrays++; - event = Event.START_ARRAY; - } - - private void handleEndArray(final char c) { - withinArray = false; - - final char significantChar = (char) lastSignificantChar; - if (lastSignificantChar >= 0 - && significantChar != START_ARRAY_CHAR - && significantChar != END_ARRAY_CHAR - && significantChar != END_OBJECT_CHAR - && significantChar != QUOTE) { - throw new JsonParsingException("Unexpected character " + c - + " (last significant was " + lastSignificantChar + ")", - createLocation()); - } - - if (openArrays == 0) { - throw new JsonParsingException("Unexpected character " + c, - createLocation()); - } - - lastSignificantChar = c; - openArrays--; - - event = Event.END_ARRAY; - } - - private boolean handleQuote(final char c) { - - final char significantChar = (char) lastSignificantChar; - if (lastSignificantChar >= 0 - && significantChar != QUOTE - && significantChar != KEY_SEPARATOR - && significantChar != START_OBJECT_CHAR - && significantChar != START_ARRAY_CHAR - && significantChar != COMMA) { - throw new JsonParsingException("Unexpected character " + c - + " (last significant was " + lastSignificantChar + ")", - createLocation()); - } - - lastSignificantChar = c; - - if (constructingStringValue) { - - if (escaped) { - appendValue(QUOTE); - escaped = false; - return true; - } else { - - if (!withinArray && stringValueIsKey) { - event = Event.KEY_NAME; - stringValueIsKey = false; - } else { - - if (lastEvent != Event.KEY_NAME && !withinArray) { - throw new JsonParsingException("Unexpected character " - + c + " (lastevent " + lastEvent - + ", comma missing)", createLocation()); - } - - // string value end - event = Event.VALUE_STRING; - } - - constructingStringValue = false; - - return false; - } - } else { - - if (escaped) { - throw new JsonParsingException("Unexpected character " + c, - createLocation()); - } - - // string value start - resetValue(); - constructingStringValue = true; - return false; - } - - } - - private void handleLiteral(final char c) throws IOException { - if (lastSignificantChar >= 0 - && lastSignificantChar != KEY_SEPARATOR - && lastSignificantChar != COMMA - && lastSignificantChar != START_ARRAY_CHAR) { - throw new JsonParsingException("unexpected character " + c, - createLocation()); - } - - lastSignificantChar = -2; - - resetValue(); - - if (lastSignificantChar != QUOTE) { - // probe literals - switch (c) { - case TRUE_T: - final char[] tmpt = read(3); - if (tmpt[0] != TRUE_R || tmpt[1] != TRUE_U || tmpt[2] != TRUE_E) { - throw new JsonParsingException("Unexpected literal " + c - + new String(tmpt), createLocation()); - } - event = Event.VALUE_TRUE; - break; - case FALSE_F: - final char[] tmpf = read(4); - if (tmpf[0] != FALSE_A || tmpf[1] != FALSE_L - || tmpf[2] != FALSE_S || tmpf[3] != FALSE_E) { - throw new JsonParsingException("Unexpected literal " + c - + new String(tmpf), createLocation()); - } - - event = Event.VALUE_FALSE; - break; - case NULL_N: - final char[] tmpn = read(3); - if (tmpn[0] != NULL_U || tmpn[1] != NULL_L || tmpn[2] != NULL_L) { - throw new JsonParsingException("Unexpected literal " + c - + new String(tmpn), createLocation()); - } - event = Event.VALUE_NULL; - break; - - default: // number - appendValue(c); - - boolean endExpected = false; - final boolean zeropassed = c == '0'; - final boolean beginningMinusPassed = c == '-'; - boolean dotpassed = false; - boolean epassed = false; - char last = c; - int i = -1; - - while (true) { - i++; - - final char n = read(); - mark(); - - if (n == COMMA || n == END_ARRAY_CHAR - || n == END_OBJECT_CHAR) { - resetToMark(); - - isCurrentNumberIntegral=(!dotpassed && !epassed); - - if(isCurrentNumberIntegral && beginningMinusPassed && i==1 && last >= '0' && last <= '9') - { - currentIntegralNumber=-((int)last - 48); //optimize -0 till -99 - } - - if(isCurrentNumberIntegral && !beginningMinusPassed && i==0 && last >= '0' && last <= '9') - { - currentIntegralNumber=((int)last - 48); //optimize 0 till 9 - } - - - event = Event.VALUE_NUMBER; - break; - } - - if (n == EOL) { - last = n; - continue; - } - - if (endExpected && n != SPACE && n != TAB && n != CR) { - throw new JsonParsingException("unexpected character " - + n + " (" + (int) n + ")", createLocation()); - } - - if (n == SPACE || n == TAB || n == CR) { - endExpected = true; - last = n; - continue; - } - - if (!isNumber(n)) { - throw new JsonParsingException("unexpected character " - + n, createLocation()); - } - - // minus only allowed as first char or after e/E - if (n == MINUS && i != 0 && last != EXP_LOWERCASE - && last != EXP_UPPERCASE) { - throw new JsonParsingException("unexpected character " - + n, createLocation()); - } - - // plus only allowed after e/E - if (n == PLUS && last != EXP_LOWERCASE - && last != EXP_UPPERCASE) { - throw new JsonParsingException("unexpected character " - + n, createLocation()); - } - - if (!dotpassed && zeropassed && i == 0 && n != DOT) { - throw new JsonParsingException("unexpected character " - + n + " (no leading zeros allowed)", - createLocation()); - } - - if (LOG) { - LOGGER.fine("dotpassed:" + dotpassed - + "/zeropassed:" + zeropassed + "/i:" + i - + "/n:" + n); - } - - if (n == DOT) { - - if (dotpassed) { - throw new JsonParsingException("more than one dot", - createLocation()); - } - - dotpassed = true; - - } - - if (n == EXP_LOWERCASE || n == EXP_UPPERCASE) { - - if (epassed) { - throw new JsonParsingException("more than one e/E", - createLocation()); - } - - epassed = true; - } - - appendValue(n); - last = n; - - } - - break; - - } - - } else { - throw new JsonParsingException("Unexpected character " + c, - createLocation()); - } - - } - - private static boolean isNumber(final char c) { - return isAsciiDigit(c) || c == DOT || c == MINUS || c == PLUS - || c == EXP_LOWERCASE || c == EXP_UPPERCASE; - } - - @Override - public String getString() { - if (event == Event.KEY_NAME || event == Event.VALUE_STRING - || event == Event.VALUE_NUMBER) { - return getValue(); - } - throw new IllegalStateException(event + " doesn't support getString()"); - } - - @Override - public boolean isIntegralNumber() { - - if (event != Event.VALUE_NUMBER) { - throw new IllegalStateException(event - + " doesn't support isIntegralNumber()"); - } - - return isCurrentNumberIntegral; - } - - @Override - public int getInt() { - if (event != Event.VALUE_NUMBER) { - throw new IllegalStateException(event + " doesn't support getInt()"); - } - - if (isCurrentNumberIntegral && currentIntegralNumber != null) { - return currentIntegralNumber; - } - - if(isCurrentNumberIntegral) { - return (int) parseLongFromChars(currentValue, 0, valueLength); - } - - return getBigDecimal().intValue(); - } - - @Override - public long getLong() { - if (event != Event.VALUE_NUMBER) { - throw new IllegalStateException(event + " doesn't support getLong()"); - } - - if (isCurrentNumberIntegral && currentIntegralNumber != null) { - return currentIntegralNumber; - } // int is ok, its only from 0-9 - - if (isCurrentNumberIntegral) { - return parseLongFromChars(currentValue, 0, valueLength); - } - - return getBigDecimal().longValue(); - } - - @Override - public BigDecimal getBigDecimal() { - if (event != Event.VALUE_NUMBER) { - throw new IllegalStateException(event + " doesn't support getBigDecimal()"); - } - - if (currentBigDecimalNumber != null) { - return currentBigDecimalNumber; - } - - return (currentBigDecimalNumber = new BigDecimal(getString())); - } - - @Override - public JsonLocation getLocation() { - return createLocation(); - } - - protected abstract void closeUnderlyingSource() throws IOException; - - @Override - public void close() { - try { - closeUnderlyingSource(); - } catch (final IOException e) { - throw new JsonException(e.getMessage(), e); - } - } - - @Override - public String getEscapedString() { - return Strings.escape(getValue()); - } - - private static long parseLongFromChars(char[] chars, int start, int end) { - - if (chars == null - || chars.length == 0 - || start < 0 - || end <= start - || end > chars.length - 1 - || start > chars.length - 1) { - throw new IllegalArgumentException(); - } - - long retVal = 0; - boolean negative = chars[start] == MINUS; - for (int i = negative ? start + 1 : start; i < end; i++) { - - //int this context we know its an integral number, so skip this due to perf reasons - /*if (chars[i] < ZERO || chars[i] > NINE) { - throw new IllegalArgumentException("Not a integral number"); - }*/ - retVal = retVal * 10 + (chars[i] - ZERO); - } - - return negative ? -retVal : retVal; - } - -} diff --git a/fleece-core/src/main/java/org/apache/fleece/core/JsonCharBufferStreamParser.java b/fleece-core/src/main/java/org/apache/fleece/core/JsonCharBufferStreamParser.java deleted file mode 100644 index 3bd3af0..0000000 --- a/fleece-core/src/main/java/org/apache/fleece/core/JsonCharBufferStreamParser.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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.fleece.core; - -import java.io.IOException; -import java.io.Reader; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class JsonCharBufferStreamParser extends JsonBaseStreamParser { - private static final Logger LOGGER = Logger.getLogger(JsonCharBufferStreamParser.class.getName()); - private static final boolean LOG = LOGGER.isLoggable(Level.FINE); - - private final char[] buffer; - private final Reader in; - private final BufferStrategy.BufferProvider bufferProvider; - private final BufferStrategy.BufferProvider valueProvider; - private int pointer = -1; - private int avail; - private char mark; - private boolean reset; - - public JsonCharBufferStreamParser(final Reader reader, final int maxStringLength, - final BufferStrategy.BufferProvider bufferProvider, - final BufferStrategy.BufferProvider valueBuffer) { - super(maxStringLength, valueBuffer.newBuffer()); - this.in = reader; - this.buffer = bufferProvider.newBuffer(); - this.bufferProvider = bufferProvider; - this.valueProvider = valueBuffer; - } - - @Override - protected char readNextChar() throws IOException { - if (reset) { - reset = false; - return mark; - } - - if (avail <= 0) { - avail = in.read(buffer, 0, buffer.length); - - pointer = -1; - - if (LOG) { - LOGGER.fine("******* Fill buffer with " + avail - + " chars"); - } - - if (avail <= 0) { - throw new IOException("EOF"); - } - } - - pointer++; - avail--; - return buffer[pointer]; - - } - - @Override - protected void mark() { - mark = buffer[pointer]; - } - - @Override - protected void reset() { - reset = true; - } - - @Override - protected void closeUnderlyingSource() throws IOException { - bufferProvider.release(buffer); - valueProvider.release(currentValue); - in.close(); - } -} diff --git a/fleece-core/src/main/java/org/apache/fleece/core/JsonChars.java b/fleece-core/src/main/java/org/apache/fleece/core/JsonChars.java index d812f08..3a92c9b 100644 --- a/fleece-core/src/main/java/org/apache/fleece/core/JsonChars.java +++ b/fleece-core/src/main/java/org/apache/fleece/core/JsonChars.java @@ -18,6 +18,8 @@ */ package org.apache.fleece.core; +import javax.json.stream.JsonParser.Event; + public interface JsonChars { char EOF = Character.MIN_VALUE; @@ -25,10 +27,13 @@ public interface JsonChars { char END_OBJECT_CHAR = '}'; char START_ARRAY_CHAR = '['; char END_ARRAY_CHAR = ']'; - char EOL = '\n'; + char QUOTE = '"'; char COMMA = ','; - char SPACE = ' '; char KEY_SEPARATOR = ':'; + + char EOL = '\n'; + char SPACE = ' '; + char TRUE_T = 't'; char TRUE_R = 'r'; char TRUE_U = 'u'; @@ -41,7 +46,7 @@ public interface JsonChars { char NULL_N = 'n'; char NULL_U = 'u'; char NULL_L = 'l'; - char QUOTE = '"'; + char ZERO = '0'; char NINE = '9'; char DOT = '.'; @@ -57,4 +62,21 @@ public interface JsonChars { char CR = '\r'; String NULL = "null"; + + static final byte START_ARRAY = (byte) Event.START_ARRAY.ordinal(); + static final byte START_OBJECT = (byte) Event.START_OBJECT.ordinal(); + static final byte KEY_NAME=(byte) Event.KEY_NAME.ordinal(); + static final byte VALUE_STRING=(byte) Event.VALUE_STRING.ordinal(); + static final byte VALUE_NUMBER=(byte) Event.VALUE_NUMBER.ordinal(); + static final byte VALUE_TRUE=(byte) Event.VALUE_TRUE.ordinal(); + static final byte VALUE_FALSE=(byte) Event.VALUE_FALSE.ordinal(); + static final byte VALUE_NULL=(byte) Event.VALUE_NULL.ordinal(); + static final byte END_OBJECT=(byte) Event.END_OBJECT.ordinal(); + static final byte END_ARRAY=(byte) Event.END_ARRAY.ordinal(); + + static final byte COMMA_EVENT=Byte.MAX_VALUE; + static final byte KEY_SEPARATOR_EVENT=Byte.MIN_VALUE; + + static final Event[] EVT_MAP =Event.values(); + } diff --git a/fleece-core/src/main/java/org/apache/fleece/core/JsonDoubleImpl.java b/fleece-core/src/main/java/org/apache/fleece/core/JsonDoubleImpl.java index 6d9d370..5b63d41 100644 --- a/fleece-core/src/main/java/org/apache/fleece/core/JsonDoubleImpl.java +++ b/fleece-core/src/main/java/org/apache/fleece/core/JsonDoubleImpl.java @@ -56,12 +56,12 @@ public class JsonDoubleImpl implements JsonNumber { @Override public BigInteger bigIntegerValue() { - return new BigInteger(toString()); + return new BigDecimal(toString()).toBigInteger(); } @Override public BigInteger bigIntegerValueExact() { - return bigIntegerValue(); + return new BigDecimal(toString()).toBigIntegerExact(); } @Override diff --git a/fleece-core/src/main/java/org/apache/fleece/core/JsonLocationImpl.java b/fleece-core/src/main/java/org/apache/fleece/core/JsonLocationImpl.java index d76ce39..c1c094e 100644 --- a/fleece-core/src/main/java/org/apache/fleece/core/JsonLocationImpl.java +++ b/fleece-core/src/main/java/org/apache/fleece/core/JsonLocationImpl.java @@ -19,9 +19,13 @@ package org.apache.fleece.core; import javax.json.stream.JsonLocation; + import java.io.Serializable; public class JsonLocationImpl implements JsonLocation, Serializable { + + public static final JsonLocation UNKNOW_LOCATION = new JsonLocationImpl(-1, -1, -1); + private final long lineNumber; private final long columnNumber; private final long streamOffset; @@ -71,6 +75,6 @@ public class JsonLocationImpl implements JsonLocation, Serializable { @Override public String toString() { - return "JsonLocationImpl [lineNumber=" + lineNumber + ", columnNumber=" + columnNumber + ", streamOffset=" + streamOffset + "]"; + return "[lineNumber=" + lineNumber + ", columnNumber=" + columnNumber + ", streamOffset=" + streamOffset + "]"; } } diff --git a/fleece-core/src/main/java/org/apache/fleece/core/JsonParserFactoryImpl.java b/fleece-core/src/main/java/org/apache/fleece/core/JsonParserFactoryImpl.java index 6195d83..505542e 100644 --- a/fleece-core/src/main/java/org/apache/fleece/core/JsonParserFactoryImpl.java +++ b/fleece-core/src/main/java/org/apache/fleece/core/JsonParserFactoryImpl.java @@ -23,7 +23,6 @@ import javax.json.JsonObject; import javax.json.stream.JsonParser; import javax.json.stream.JsonParserFactory; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.Reader; import java.io.Serializable; import java.nio.charset.Charset; @@ -35,7 +34,7 @@ public class JsonParserFactoryImpl implements JsonParserFactory, Serializable { public static final String BUFFER_STRATEGY = "org.apache.fleece.buffer-strategy"; public static final String MAX_STRING_LENGTH = "org.apache.fleece.max-string-length"; public static final String BUFFER_LENGTH = "org.apache.fleece.default-char-buffer"; - public static final int DEFAULT_MAX_SIZE = Integer.getInteger(MAX_STRING_LENGTH, 8192); + public static final int DEFAULT_MAX_SIZE = Integer.getInteger(MAX_STRING_LENGTH, 8192*32); private final Map config; private final int maxSize; @@ -56,6 +55,9 @@ public class JsonParserFactoryImpl implements JsonParserFactory, Serializable { } private BufferStrategy getBufferProvider() { + if(config==null) { + return BufferStrategy.QUEUE; + } final Object name = config.get(BUFFER_STRATEGY); if (name != null) { return BufferStrategy.valueOf(name.toString().toUpperCase(Locale.ENGLISH)); @@ -64,6 +66,9 @@ public class JsonParserFactoryImpl implements JsonParserFactory, Serializable { } private int getInt(final String key) { + if(config==null) { + return DEFAULT_MAX_SIZE; + } final Object maxStringSize = config.get(key); if (maxStringSize == null) { return DEFAULT_MAX_SIZE; @@ -73,17 +78,19 @@ public class JsonParserFactoryImpl implements JsonParserFactory, Serializable { return Integer.parseInt(maxStringSize.toString()); } - private JsonCharBufferStreamParser getDefaultJsonParserImpl(final InputStream in) { - return new JsonCharBufferStreamParser( - new InputStreamReader(in, Charset.defaultCharset()), maxSize, bufferProvider, valueBufferProvider); + private EscapedStringAwareJsonParser getDefaultJsonParserImpl(final InputStream in) { + //UTF Auto detection RFC 4627 + return new JsonStreamParserImpl(in, maxSize, bufferProvider, valueBufferProvider); } - private JsonCharBufferStreamParser getDefaultJsonParserImpl(final InputStream in, final Charset charset) { - return new JsonCharBufferStreamParser(new InputStreamReader(in, charset), maxSize, bufferProvider, valueBufferProvider); + private EscapedStringAwareJsonParser getDefaultJsonParserImpl(final InputStream in, final Charset charset) { + //use provided charset + return new JsonStreamParserImpl(in, charset, maxSize, bufferProvider, valueBufferProvider); } - private JsonCharBufferStreamParser getDefaultJsonParserImpl(final Reader in) { - return new JsonCharBufferStreamParser(in, maxSize, bufferProvider, valueBufferProvider); + private EscapedStringAwareJsonParser getDefaultJsonParserImpl(final Reader in) { + //no charset necessary + return new JsonStreamParserImpl(in, maxSize, bufferProvider, valueBufferProvider); } @Override diff --git a/fleece-core/src/main/java/org/apache/fleece/core/JsonReaderImpl.java b/fleece-core/src/main/java/org/apache/fleece/core/JsonReaderImpl.java index 15a9d0f..0bc3850 100644 --- a/fleece-core/src/main/java/org/apache/fleece/core/JsonReaderImpl.java +++ b/fleece-core/src/main/java/org/apache/fleece/core/JsonReaderImpl.java @@ -25,6 +25,7 @@ import javax.json.JsonStructure; import javax.json.JsonValue; import javax.json.stream.JsonParser; import javax.json.stream.JsonParsingException; + import java.math.BigDecimal; public class JsonReaderImpl implements JsonReader { @@ -43,20 +44,28 @@ public class JsonReaderImpl implements JsonReader { @Override public JsonStructure read() { if (!parser.hasNext()) { - throw new JsonParsingException("Nothing to read", new JsonLocationImpl(1, 1, 0)); + throw new IllegalStateException("Nothing to read"); } switch (parser.next()) { case START_OBJECT: final JsonReaderListener subObject = listenerFactory.subObject(); parseObject(subObject); + if (parser.hasNext()) { + throw new JsonParsingException("Expected end of file", parser.getLocation()); + } return JsonObject.class.cast(subObject.getObject()); case START_ARRAY: final JsonReaderListener subArray = listenerFactory.subArray(); parseArray(subArray); + if (parser.hasNext()) { + throw new JsonParsingException("Expected end of file", parser.getLocation()); + } return JsonArray.class.cast(subArray.getObject()); default: throw new JsonParsingException("Unknown structure: " + parser.next(), parser.getLocation()); } + + } @Override diff --git a/fleece-core/src/main/java/org/apache/fleece/core/JsonStreamParserImpl.java b/fleece-core/src/main/java/org/apache/fleece/core/JsonStreamParserImpl.java new file mode 100644 index 0000000..b6e1e32 --- /dev/null +++ b/fleece-core/src/main/java/org/apache/fleece/core/JsonStreamParserImpl.java @@ -0,0 +1,968 @@ +/* + * 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.fleece.core; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.math.BigDecimal; +import java.nio.charset.Charset; +import java.util.NoSuchElementException; + +import javax.json.JsonException; +import javax.json.stream.JsonLocation; +import javax.json.stream.JsonParsingException; + +//This class represents either the Json tokenizer and the Json parser. +public class JsonStreamParserImpl implements JsonChars, EscapedStringAwareJsonParser { + + //the main buffer where the stream will be buffered + private final char[] buffer; + + //current parser position within the buffer + //Initial MIN_VALUE will trigger buffer refill, normally bufferPos is >= -1 + //-1 would cause a re-read of the first character in the buffer (which is at zero index) + private int bufferPos = Integer.MIN_VALUE; + + //available character in the buffer + //normally this is buffer.length, except for the last buffer page it might be <= buffer.length + private int availableCharsInBuffer; + + //start and end position of values in the buffer + //may cross boundaries, then value is in fallBackCopyBuffer + private int startOfValueInBuffer = -1; + private int endOfValueInBuffer = -1; + + private final Reader in; + + //do we read from a character stream or a byte stream + //not used at the moment but maybe relevant in future to calculate the JsonLocation offset + @SuppressWarnings("unused") + private final boolean readBytes; + private final BufferStrategy.BufferProvider bufferProvider; + private final BufferStrategy.BufferProvider valueProvider; + + //max length for strings and numbers (max count of characters) + private final int maxValueLength; + + //we use a byte here, because comparing bytes + //is more efficient than comparing enums + //Additionally we handle internally two more event: COMMA_EVENT and KEY_SEPARATOR_EVENT + private byte previousEvent; + + //this buffer is used to store current String or Number value in case that + //within the value a buffer boundary is crossed or the string contains escaped characters + private final char[] fallBackCopyBuffer; + private int fallBackCopyBufferLength; + + // location (line, column, offset) + // We try to calculate this efficiently so we do not just increment the values per char read + // Instead we calculate the column and offset relative to the pastBufferReadCount and/or lastLineBreakPosition. + private long currentLine = 1; + private long lastLineBreakPosition; + private long pastBufferReadCount; + + //cache (if current value is a number) integral state and the number itself if its only one digit + private boolean isCurrentNumberIntegral = true; + private Integer currentIntegralNumber; //for number from 0 - 9 + + //maybe we want also cache BigDecimals + //private BigDecimal currentBigDecimalNumber = null; + + //We need a stack if we want detect bad formatted Json do determine if we are within an array or not + //example + // Streamparser sees: ],1 <-- we look from here + //the 1 is only allowed if we are within an array + //This can only be determined by build up a stack which tracks the trail of Json objects and arrays + //This stack here is only needed for validating the above mentioned case, if we want to be lenient we can skip suing the stack. + //Stack can cause out of memory issues when the nesting depth of a Json stream is too deep. + private StructureElement currentStructureElement = null; + + //minimal stack implementation + private static final class StructureElement { + final StructureElement previous; + final boolean isArray; + + StructureElement(final StructureElement previous, final boolean isArray) { + super(); + this.previous = previous; + this.isArray = isArray; + } + } + + //detect charset according to RFC 4627 + public JsonStreamParserImpl(final InputStream inputStream, final int maxStringLength, + final BufferStrategy.BufferProvider bufferProvider, final BufferStrategy.BufferProvider valueBuffer) { + + this(inputStream, null, null, maxStringLength, bufferProvider, valueBuffer); + } + + //use charset provided + public JsonStreamParserImpl(final InputStream inputStream, final Charset encoding, final int maxStringLength, + final BufferStrategy.BufferProvider bufferProvider, final BufferStrategy.BufferProvider valueBuffer) { + + this(inputStream, null, encoding, maxStringLength, bufferProvider, valueBuffer); + } + + public JsonStreamParserImpl(final Reader reader, final int maxStringLength, final BufferStrategy.BufferProvider bufferProvider, + final BufferStrategy.BufferProvider valueBuffer) { + + this(null, reader, null, maxStringLength, bufferProvider, valueBuffer); + } + + private JsonStreamParserImpl(final InputStream inputStream, final Reader reader, final Charset encoding, final int maxStringLength, + final BufferStrategy.BufferProvider bufferProvider, final BufferStrategy.BufferProvider valueBuffer) { + + this.maxValueLength = maxStringLength <= 0 ? 8192 : maxStringLength; + this.fallBackCopyBuffer = valueBuffer.newBuffer(); + this.buffer = bufferProvider.newBuffer(); + this.bufferProvider = bufferProvider; + this.valueProvider = valueBuffer; + + if (fallBackCopyBuffer.length < maxStringLength) { + throw cust("Size of value buffer cannot be smaller than maximum string length"); + } + + if (reader != null) { + this.in = reader; + readBytes = false; + } else if (encoding == null) { + this.in = new RFC4627AwareInputStreamReader(inputStream); + readBytes = true; + + } else { + this.in = new InputStreamReader(inputStream, encoding.newDecoder()); + readBytes = true; + } + + } + + //append a single char to the value buffer + private void appendToCopyBuffer(final char c) { + fallBackCopyBuffer[fallBackCopyBufferLength] = c; + fallBackCopyBufferLength++; + } + + //copy content between "start" and "end" from buffer to value buffer + private void copyCurrentValue() { + + if ((endOfValueInBuffer - startOfValueInBuffer) > 0) { + + if ((endOfValueInBuffer - startOfValueInBuffer) > maxValueLength) { + throw tmc(); + } + + System.arraycopy(buffer, startOfValueInBuffer, fallBackCopyBuffer, fallBackCopyBufferLength, + (endOfValueInBuffer - startOfValueInBuffer)); + fallBackCopyBufferLength += (endOfValueInBuffer - startOfValueInBuffer); + + } + + startOfValueInBuffer = endOfValueInBuffer = -1; + } + + @Override + public final boolean hasNext() { + + if (currentStructureElement != null || (previousEvent != END_ARRAY && previousEvent != END_OBJECT) || previousEvent == 0) { + return true; + } + + //detect garbage at the end of the file after last object or array is closed + if (bufferPos < availableCharsInBuffer - 2) { + + final char c = readNextNonWhitespaceChar(); + + if (c == EOF) { + return false; + } + + if (bufferPos < availableCharsInBuffer) { + throw uexc("EOF expected"); + } + + } + + return false; + + } + + private static boolean isAsciiDigit(final char value) { + return value <= NINE && value >= ZERO; + } + + //check if value is a valid hex digit and return the numeric value + private int parseHexDigit(final char value) { + + if (isAsciiDigit(value)) { + return value - 48; + } else if (value <= 'f' && value >= 'a') { + return (value) - 87; + } else if ((value <= 'F' && value >= 'A')) { + return (value) - 55; + } else { + throw uexc("Invalid hex character"); + } + } + + private JsonLocation createLocation() { + + //we start with column = 1, so column is always >= 1 + //APi is not clear in this, but starting column with 1 is convenient + long column = 1; + long charOffset = 0; + + if (bufferPos >= -1) { + + charOffset = pastBufferReadCount + bufferPos + 1; + column = lastLineBreakPosition == 0 ? charOffset + 1 : charOffset - lastLineBreakPosition; + } + + //For now its unclear how to calculate offset for (byte) inputsream. + //API says count bytes but thats dependent on encoding and not efficient + //skip this for now, count always bytes and defer this until the JSR TCK arrives. + + return new JsonLocationImpl(currentLine, column, charOffset); + } + + //read the next char from the stream and set/increment the bufferPos + //will also refill buffer if neccessary + //if we are currently processing a value (string or number) and buffer + //refill is neccessary copy the already readed value part into the value buffer + private char readNextChar() { + + if ((buffer.length - bufferPos) <= 1) { + //fillbuffer + + //copy content from old buffer to valuebuffer + //correct start end mark + if (startOfValueInBuffer > -1 && endOfValueInBuffer == -1) { + endOfValueInBuffer = availableCharsInBuffer; + copyCurrentValue(); + + startOfValueInBuffer = 0; + } + + if (bufferPos >= -1) { + pastBufferReadCount += availableCharsInBuffer; + } + + try { + availableCharsInBuffer = in.read(buffer, 0, buffer.length); + if (availableCharsInBuffer <= 0) { + return EOF; + } + + } catch (final IOException e) { + close(); + throw uexio(e); + } + + bufferPos = 0; + //end fillbuffer + } else { + bufferPos++; + } + + return buffer[bufferPos]; + } + + //skip whitespaces + //tracks location informations (line, column) + //returns the first non whitespace character + private char readNextNonWhitespaceChar() { + + int dosCount = 0; + char c = readNextChar(); + + while (c == SPACE || c == TAB || c == CR || c == EOL) { + + if (c == EOL) { + currentLine++; + lastLineBreakPosition = pastBufferReadCount + bufferPos; + } + + //prevent DOS (denial of service) attack + if (dosCount >= maxValueLength) { + throw tmc(); + } + dosCount++; + + //read next character + c = readNextChar(); + + } + + return c; + } + + @Override + public final Event next() { + //main entry, make decision how to handle the current character in the stream + + if (!hasNext()) { + throw new NoSuchElementException(); + } + + if (previousEvent != 0 && currentStructureElement == null) { + throw uexc("Unexpected end of structure"); + } + + final char c = readNextNonWhitespaceChar(); + + if (c == COMMA) { + + //last event must one of the following-> " ] } LITERAL + if (previousEvent == START_ARRAY || previousEvent == START_OBJECT || previousEvent == COMMA_EVENT || previousEvent == KEY_NAME) { + throw uexc("Expected \" ] } LITERAL"); + } + + previousEvent = COMMA_EVENT; + return next(); + + } + + if (c == KEY_SEPARATOR) { + + if (previousEvent != KEY_NAME) { + throw uexc("A : can only follow a key name"); + } + + previousEvent = KEY_SEPARATOR_EVENT; + return next(); + + } + + if (!isCurrentNumberIntegral) { + isCurrentNumberIntegral = true; + } + // if (currentBigDecimalNumber != null) { + // currentBigDecimalNumber = null; + // } + if (currentIntegralNumber != null) { + currentIntegralNumber = null; + } + + if (fallBackCopyBufferLength != 0) { + fallBackCopyBufferLength = 0; + } + + startOfValueInBuffer = endOfValueInBuffer = -1; + + switch (c) { + + case START_OBJECT_CHAR: + + return handleStartObject(); + + case END_OBJECT_CHAR: + + return handleEndObject(); + + case START_ARRAY_CHAR: + + return handleStartArray(); + + case END_ARRAY_CHAR: + + return handleEndArray(); + + case QUOTE: + + return handleQuote(); + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case MINUS: + case FALSE_F: // false + case TRUE_T: // true + case NULL_N: // null + + return handleLiteral(c); + default: + throw uexc("Excpected structural character or digit or 't' or 'n' or 'f' or '-'"); + + } + + } + + private Event handleStartObject() { + + //last event must one of the following-> : , [ + if (previousEvent != 0 && previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_ARRAY && previousEvent != COMMA_EVENT) { + throw uexc("Excpected : , ["); + } + + //push upon the stack + if (currentStructureElement == null) { + currentStructureElement = new StructureElement(null, false); + } else { + final StructureElement localStructureElement = new StructureElement(currentStructureElement, false); + currentStructureElement = localStructureElement; + } + + return EVT_MAP[previousEvent = START_OBJECT]; + + } + + private Event handleEndObject() { + + //last event must one of the following-> " ] { } LITERAL + if (previousEvent == START_ARRAY || previousEvent == COMMA_EVENT || previousEvent == KEY_NAME + || previousEvent == KEY_SEPARATOR_EVENT || currentStructureElement == null) { + throw uexc("Expected \" ] { } LITERAL"); + } + + if (currentStructureElement.isArray) { + throw uexc("Expected : ]"); + } + + //pop from stack + currentStructureElement = currentStructureElement.previous; + + return EVT_MAP[previousEvent = END_OBJECT]; + } + + private Event handleStartArray() { + + //last event must one of the following-> : , [ + if (previousEvent != 0 && previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_ARRAY && previousEvent != COMMA_EVENT) { + throw uexc("Expected : , ["); + } + + //push upon the stack + if (currentStructureElement == null) { + currentStructureElement = new StructureElement(null, true); + } else { + final StructureElement localStructureElement = new StructureElement(currentStructureElement, true); + currentStructureElement = localStructureElement; + } + + return EVT_MAP[previousEvent = START_ARRAY]; + } + + private Event handleEndArray() { + + //last event must one of the following-> [ ] } " LITERAL + if (previousEvent == START_OBJECT || previousEvent == COMMA_EVENT || previousEvent == KEY_SEPARATOR_EVENT + || currentStructureElement == null) { + throw uexc("Expected [ ] } \" LITERAL"); + } + + if (!currentStructureElement.isArray) { + throw uexc("Expected : }"); + } + + //pop from stack + currentStructureElement = currentStructureElement.previous; + + return EVT_MAP[previousEvent = END_ARRAY]; + } + + //read a string, gets called recursively + //Handles escape/d characters + //if string contains escape chars and/or cross buffer boundary then copy in the value buffer + //if not then denote string start and end in startOfValueInBuffer and endOfValueInBuffer and read directly from buffer + private void readString() { + + char n = readNextChar(); + //when first called n its first char after the starting quote + //after that its the next character after the while loop below + + if (n == QUOTE) { + endOfValueInBuffer = startOfValueInBuffer = bufferPos; //->"" case + return; + } else if (n == EOL) { + throw uexc("Unexpected linebreak"); + + } else if (n >= '\u0000' && n <= '\u001F') { + throw uexc("Unescaped control character"); + + } else if (n == ESCAPE_CHAR) { + + n = readNextChar(); + + // \ u XXXX -> unicode char + if (n == 'u') { + n = parseUnicodeHexChars(); + appendToCopyBuffer(n); + + // \\ -> \ + } else if (n == ESCAPE_CHAR) { + appendToCopyBuffer(n); + + //another escape chars, for example \t + } else { + appendToCopyBuffer(Strings.asEscapedChar(n)); + + } + + } else { + + startOfValueInBuffer = bufferPos; + endOfValueInBuffer = -1; + + while ((n = readNextChar()) > '\u001F' && n != ESCAPE_CHAR && n != EOL && n != QUOTE) { + //read fast + } + + endOfValueInBuffer = bufferPos; + + if (n == QUOTE) { + + if (fallBackCopyBufferLength > 0) { + copyCurrentValue(); + } else { + if ((endOfValueInBuffer - startOfValueInBuffer) > maxValueLength) { + throw tmc(); + } + + } + + return; + } else if (n == EOL) { + throw uexc("Unexpected linebreak"); + + } else if (n >= '\u0000' && n <= '\u001F') { + throw uexc("Unescaped control character"); + } + + copyCurrentValue(); + + //current n is one of < '\u001F' -OR- ESCAPE_CHAR -OR- EOL -OR- QUOTE + + bufferPos--; //unread one char + + } + + //recurse until string is terminated by a non escaped quote + readString(); + + } + + //maybe we want to check invalid utf encoding + //not clear yet if the InputStreamReader is doing that + + /* + private char checkSurrogates(char n, char highSurrogate) { + //check for invalid surrogates + //high followed by low + if (Character.isHighSurrogate(n)) { + + if (highSurrogate != 0) { + throw uexc("Unexpected high surrogate"); + } + return n; + } else if (Character.isLowSurrogate(n)) { + + if (highSurrogate == 0) { + throw uexc("Unexpected low surrogate"); + } else if (!Character.isSurrogatePair(highSurrogate, n)) { + throw uexc("Invalid surrogate pair"); + } + return 0; + } else if (highSurrogate != 0 && !Character.isLowSurrogate(n)) { + throw uexc("Expected low surrogate"); + } + + return highSurrogate; + }*/ + + //read the next four chars, check them and treat them as an single unicode char + private char parseUnicodeHexChars() { + // \u08Ac etc + return (char) (((parseHexDigit(readNextChar())) * 4096) + ((parseHexDigit(readNextChar())) * 256) + + ((parseHexDigit(readNextChar())) * 16) + ((parseHexDigit(readNextChar())))); + + } + + private Event handleQuote() { + + //always the beginning quote of a key or value + + //last event must one of the following-> : { [ , + if (previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_OBJECT && previousEvent != START_ARRAY + && previousEvent != COMMA_EVENT) { + throw uexc("Expected : { [ ,"); + } + //starting quote already consumed + readString(); + //end quote already consumed + + //make the decision if its an key or value + if (previousEvent == KEY_SEPARATOR_EVENT) { + //must be value + + if (currentStructureElement != null && currentStructureElement.isArray) { + //not in array, only allowed within array + throw uexc("Key value pair not allowed in an array"); + } + + return EVT_MAP[previousEvent = VALUE_STRING]; + + } else { //Event is START_OBJECT OR START_ARRAY OR COMMA_EVENT + //must be a key if we are in an object, if not its a value + + if (currentStructureElement != null && currentStructureElement.isArray) { + return EVT_MAP[previousEvent = VALUE_STRING]; + } + + return EVT_MAP[previousEvent = KEY_NAME]; + } + + } + + //read a number + //if a number cross buffer boundary then copy in the value buffer + //if not then denote string start and end in startOfValueInBuffer and endOfValueInBuffer and read directly from buffer + private void readNumber(final char c) { + + //start can change on any read() if we cross buffer boundary + startOfValueInBuffer = bufferPos; + endOfValueInBuffer = -1; + + char y = EOF; + + //sum up the digit values + int cumulatedDigitValue = 0; + while (isAsciiDigit(y = readNextChar())) { + + if (c == ZERO) { + throw uexc("Leading zeros not allowed"); + } + + if (c == MINUS && cumulatedDigitValue == 48) { + throw uexc("Leading zeros after minus not allowed"); + } + + cumulatedDigitValue += y; + + } + + if (c == MINUS && cumulatedDigitValue == 0) { + + throw uexc("Unexpected premature end of number"); + } + + if (y == DOT) { + isCurrentNumberIntegral = false; + cumulatedDigitValue = 0; + while (isAsciiDigit(y = readNextChar())) { + cumulatedDigitValue++; + } + + if (cumulatedDigitValue == 0) { + + throw uexc("Unexpected premature end of number"); + } + + } + + if (y == EXP_LOWERCASE || y == EXP_UPPERCASE) { + isCurrentNumberIntegral = false; + + y = readNextChar(); //+ or - or digit + + if (!isAsciiDigit(y) && y != MINUS && y != PLUS) { + throw uexc("Expected DIGIT or + or -"); + } + + if (y == MINUS || y == PLUS) { + y = readNextChar(); + if (!isAsciiDigit(y)) { + throw uexc("Unexpected premature end of number"); + } + + } + + while (isAsciiDigit(y = readNextChar())) { + //no-op + } + + } + + endOfValueInBuffer = bufferPos; + + if (y == COMMA || y == END_ARRAY_CHAR || y == END_OBJECT_CHAR || y == EOL || y == SPACE || y == TAB || y == CR) { + + bufferPos--;//unread one char + + //['-', DIGIT] + if (isCurrentNumberIntegral && c == MINUS && cumulatedDigitValue >= 48 && cumulatedDigitValue <= 57) { + + currentIntegralNumber = -(cumulatedDigitValue - 48); //optimize -0 till -9 + return; + } + + //[DIGIT] + if (isCurrentNumberIntegral && c != MINUS && cumulatedDigitValue == 0) { + + currentIntegralNumber = (c - 48); //optimize 0 till 9 + return; + } + + if (fallBackCopyBufferLength > 0) { + + //we crossed a buffer boundary, use value buffer + copyCurrentValue(); + + } else { + if ((endOfValueInBuffer - startOfValueInBuffer) >= maxValueLength) { + throw tmc(); + } + } + + return; + + } + + throw uexc("Unexpected premature end of number"); + + } + + //handles false, true, null and numbers + private Event handleLiteral(final char c) { + + //last event must one of the following-> : , [ + if (previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_ARRAY && previousEvent != COMMA_EVENT) { + throw uexc("Excpected : , ["); + } + + if (previousEvent == COMMA_EVENT && !currentStructureElement.isArray) { + //only allowed within array + throw uexc("Not in an array context"); + } + + // probe literals + switch (c) { + case TRUE_T: + + if (readNextChar() != TRUE_R || readNextChar() != TRUE_U || readNextChar() != TRUE_E) { + throw uexc("Expected LITERAL: true"); + } + return EVT_MAP[previousEvent = VALUE_TRUE]; + case FALSE_F: + + if (readNextChar() != FALSE_A || readNextChar() != FALSE_L || readNextChar() != FALSE_S || readNextChar() != FALSE_E) { + throw uexc("Expected LITERAL: false"); + } + + return EVT_MAP[previousEvent = VALUE_FALSE]; + + case NULL_N: + + if (readNextChar() != NULL_U || readNextChar() != NULL_L || readNextChar() != NULL_L) { + throw uexc("Expected LITERAL: null"); + } + return EVT_MAP[previousEvent = VALUE_NULL]; + + default: + readNumber(c); + return EVT_MAP[previousEvent = VALUE_NUMBER]; + } + + } + + @Override + public String getString() { + if (previousEvent == KEY_NAME || previousEvent == VALUE_STRING || previousEvent == VALUE_NUMBER) { + + //if there a content in the value buffer read from them, if not use main buffer + return fallBackCopyBufferLength > 0 ? new String(fallBackCopyBuffer, 0, fallBackCopyBufferLength) : new String(buffer, + startOfValueInBuffer, endOfValueInBuffer - startOfValueInBuffer); + } else { + throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getString()"); + } + } + + @Override + public boolean isIntegralNumber() { + + if (previousEvent != VALUE_NUMBER) { + throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support isIntegralNumber()"); + } else { + return isCurrentNumberIntegral; + } + } + + @Override + public int getInt() { + if (previousEvent != VALUE_NUMBER) { + throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getInt()"); + } else if (isCurrentNumberIntegral && currentIntegralNumber != null) { + return currentIntegralNumber; + } else if (isCurrentNumberIntegral) { + //if there a content in the value buffer read from them, if not use main buffer + final Integer retVal = fallBackCopyBufferLength > 0 ? parseIntegerFromChars(fallBackCopyBuffer, 0, fallBackCopyBufferLength) + : parseIntegerFromChars(buffer, startOfValueInBuffer, endOfValueInBuffer); + if (retVal == null) { + return getBigDecimal().intValue(); + } else { + return retVal.intValue(); + } + } else { + return getBigDecimal().intValue(); + } + } + + @Override + public long getLong() { + if (previousEvent != VALUE_NUMBER) { + throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getLong()"); + } else if (isCurrentNumberIntegral && currentIntegralNumber != null) { + return currentIntegralNumber; + } else if (isCurrentNumberIntegral) { + //if there a content in the value buffer read from them, if not use main buffer + final Long retVal = fallBackCopyBufferLength > 0 ? parseLongFromChars(fallBackCopyBuffer, 0, fallBackCopyBufferLength) + : parseLongFromChars(buffer, startOfValueInBuffer, endOfValueInBuffer); + if (retVal == null) { + return getBigDecimal().longValue(); + } else { + return retVal.longValue(); + } + } else { + return getBigDecimal().longValue(); + } + + } + + @Override + public BigDecimal getBigDecimal() { + if (previousEvent != VALUE_NUMBER) { + throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getBigDecimal()"); + // } else if (currentBigDecimalNumber != null) { + // return currentBigDecimalNumber; + } else if (isCurrentNumberIntegral && currentIntegralNumber != null) { + return new BigDecimal(currentIntegralNumber); + } else if (isCurrentNumberIntegral) { + //if there a content in the value buffer read from them, if not use main buffer + final Long retVal = fallBackCopyBufferLength > 0 ? parseLongFromChars(fallBackCopyBuffer, 0, fallBackCopyBufferLength) + : parseLongFromChars(buffer, startOfValueInBuffer, endOfValueInBuffer); + if (retVal == null) { + return (/*currentBigDecimalNumber = */fallBackCopyBufferLength > 0 ? new BigDecimal(fallBackCopyBuffer, 0, + fallBackCopyBufferLength) : new BigDecimal(buffer, startOfValueInBuffer, + (endOfValueInBuffer - startOfValueInBuffer))); + } else { + return (/*currentBigDecimalNumber = */new BigDecimal(retVal.longValue())); + } + } else { + //if there a content in the value buffer read from them, if not use main buffer + return (/*currentBigDecimalNumber = */fallBackCopyBufferLength > 0 ? new BigDecimal(fallBackCopyBuffer, 0, + fallBackCopyBufferLength) : new BigDecimal(buffer, startOfValueInBuffer, (endOfValueInBuffer - startOfValueInBuffer))); + } + + } + + @Override + public JsonLocation getLocation() { + return createLocation(); + } + + @Override + public void close() { + bufferProvider.release(buffer); + valueProvider.release(fallBackCopyBuffer); + + try { + in.close(); + } catch (final IOException e) { + throw new JsonException("Unexpected IO exception " + e.getMessage(), e); + } + } + + @Override + public String getEscapedString() { + return Strings.escape(getString()); + } + + //parse a char[] to long while checking overflow + //if overflowed return null + //no additional checks since we are sure here that there are no non digits in the array + private static Long parseLongFromChars(final char[] chars, final int start, final int end) { + + long retVal = 0; + final boolean negative = chars[start] == MINUS; + for (int i = negative ? start + 1 : start; i < end; i++) { + final long tmp = retVal * 10 + (chars[i] - ZERO); + if (tmp < retVal) { //check overflow + return null; + } else { + retVal = tmp; + } + } + + return negative ? -retVal : retVal; + } + + //parse a char[] to int while checking overflow + //if overflowed return null + //no additional checks since we are sure here that there are no non digits in the array + private static Integer parseIntegerFromChars(final char[] chars, final int start, final int end) { + + int retVal = 0; + final boolean negative = chars[start] == MINUS; + for (int i = negative ? start + 1 : start; i < end; i++) { + final int tmp = retVal * 10 + (chars[i] - ZERO); + if (tmp < retVal) { //check overflow + return null; + } else { + retVal = tmp; + } + } + + return negative ? -retVal : retVal; + } + + private JsonParsingException uexc(final char c, final String message) { + final JsonLocation location = createLocation(); + return new JsonParsingException("Unexpected character '" + c + "' (Codepoint: " + String.valueOf(c).codePointAt(0) + ") on " + + location + ". Reason is [[" + message + "]]", location); + } + + private JsonParsingException uexc(final String message) { + final char c = bufferPos < 0 ? 0 : buffer[bufferPos]; + return uexc(c, message); + } + + private JsonParsingException tmc() { + final JsonLocation location = createLocation(); + return new JsonParsingException("Too many characters. Maximum string/number length of " + maxValueLength + " exceeded on " + + location, location); + } + + private JsonParsingException uexio(final IOException e) { + final JsonLocation location = createLocation(); + return new JsonParsingException("Unexpected IO exception on " + location, e, location); + } + + private JsonParsingException cust(final String message) { + final JsonLocation location = createLocation(); + return new JsonParsingException("General exception on " + location + ". Reason is [[" + message + "]]", location); + } + +} \ No newline at end of file diff --git a/fleece-core/src/main/java/org/apache/fleece/core/RFC4627AwareInputStreamReader.java b/fleece-core/src/main/java/org/apache/fleece/core/RFC4627AwareInputStreamReader.java new file mode 100644 index 0000000..78d3f57 --- /dev/null +++ b/fleece-core/src/main/java/org/apache/fleece/core/RFC4627AwareInputStreamReader.java @@ -0,0 +1,110 @@ +/* + * 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.fleece.core; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PushbackInputStream; +import java.nio.charset.Charset; + +import javax.json.JsonException; + +final class RFC4627AwareInputStreamReader extends InputStreamReader { + + public RFC4627AwareInputStreamReader(final InputStream in) { + this(new PushbackInputStream(in,4)); + } + + private RFC4627AwareInputStreamReader(final PushbackInputStream in) { + super(in, getCharset(in).newDecoder()); + + } + + + /* + * RFC 4627 + + JSON text SHALL be encoded in Unicode. The default encoding is + UTF-8. + + Since the first two characters of a JSON text will always be ASCII + characters [RFC0020], it is possible to determine whether an octet + stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + at the pattern of nulls in the first four octets. + + 00 00 00 xx UTF-32BE + 00 xx 00 xx UTF-16BE + xx 00 00 00 UTF-32LE + xx 00 xx 00 UTF-16LE + xx xx xx xx UTF-8 + + */ + + private static Charset getCharset(final PushbackInputStream inputStream) { + Charset charset = Charset.forName("UTF-8"); + final byte[] utfBytes = new byte[4]; + + try { + final int read = inputStream.read(utfBytes); + if (read < 2) { + throw new JsonException("Invalid Json. Valid Json has at least 2 bytes"); + } else { + + int first = (utfBytes[0] & 0xFF); + int second = (utfBytes[1] & 0xFF); + + if (first == 0x00) { + charset = (second == 0x00) ? Charset.forName("UTF-32BE") : Charset.forName("UTF-16BE"); + } else if (read > 2 && second == 0x00) { + int third = (utfBytes[2] & 0xFF); + charset = (third == 0x00) ? Charset.forName("UTF-32LE") : Charset.forName("UTF-16LE"); + } else { + + //check BOM + if(first == 0xFE && second == 0xFF) { + charset = Charset.forName("UTF-16BE"); + } else if(read > 3 && first == 0x00 && second == 0x00 && (utfBytes[2]&0xff) == 0xFE && (utfBytes[3]&0xff) == 0xFF){ + charset = Charset.forName("UTF-32BE"); + } else if(first == 0xFF && second == 0xFE) { + + if(read > 3 && (utfBytes[2]&0xff) == 0x00 && (utfBytes[3]&0xff) == 0x00) { + charset = Charset.forName("UTF-32LE"); + }else { + charset = Charset.forName("UTF-16LE"); + } + + } + + } + + } + + + inputStream.unread(utfBytes); + + + } catch (final IOException e) { + throw new JsonException("Unable to detect charset due to "+e.getMessage(), e); + } + + return charset; + } + +} diff --git a/fleece-core/src/main/java/org/apache/fleece/core/Strings.java b/fleece-core/src/main/java/org/apache/fleece/core/Strings.java index c9fc799..71e8820 100644 --- a/fleece-core/src/main/java/org/apache/fleece/core/Strings.java +++ b/fleece-core/src/main/java/org/apache/fleece/core/Strings.java @@ -21,6 +21,8 @@ package org.apache.fleece.core; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import javax.json.stream.JsonParsingException; + public class Strings implements JsonChars { private static final BufferStrategy.BufferProvider BUILDER_CACHE = BufferStrategy.valueOf(System.getProperty("fleece.string-builder.strategy", "QUEUE")) @@ -44,10 +46,18 @@ public class Strings implements JsonChars { return '\n'; case '"': return '\"'; + case '\\': + return '\\'; + case '/': + return '/'; default: - // no-op + if(Character.isHighSurrogate(current) || Character.isLowSurrogate(current)) { + return current; + } + throw new JsonParsingException("Invalid escape sequence '"+current +"' (Codepoint: "+String.valueOf(current). + codePointAt(0),JsonLocationImpl.UNKNOW_LOCATION); } - return current; + } public static String escape(final String value) { diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayBuilderImplTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayBuilderImplTest.java index 65244c3..fd443c5 100644 --- a/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayBuilderImplTest.java +++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayBuilderImplTest.java @@ -18,17 +18,17 @@ */ package org.apache.fleece.core; +import static org.junit.Assert.assertEquals; + import java.math.BigDecimal; import java.math.BigInteger; -import org.junit.Test; - import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObjectBuilder; import javax.json.JsonValue; -import static org.junit.Assert.assertEquals; +import org.junit.Test; public class JsonArrayBuilderImplTest { @Test @@ -39,12 +39,46 @@ public class JsonArrayBuilderImplTest { } @Test + public void escapedStringArray() { + final JsonArrayBuilder builder = Json.createArrayBuilder(); + builder.add("a\"").add("\u0000"); + assertEquals("[\"a\\\"\",\"\\u0000\"]", builder.build().toString()); + } + + @Test public void emptyArray() { final JsonArrayBuilder builder = Json.createArrayBuilder(); assertEquals("[]", builder.build().toString()); } @Test + public void emptyArrayInEmtyArray() { + final JsonArrayBuilder builder = Json.createArrayBuilder(); + builder.add(Json.createArrayBuilder()); + assertEquals("[[]]", builder.build().toString()); + } + + @Test + public void arrayInArray() { + final JsonArrayBuilder builder = Json.createArrayBuilder(); + builder.add(3).add(4); + final JsonArrayBuilder builder2 = Json.createArrayBuilder(); + builder2.add(1).add(2).add(builder); + assertEquals("[1,2,[3,4]]", builder2.build().toString()); + } + + @Test + public void arrayObjectInArray() { + final JsonObjectBuilder objectBuilder = Json.createObjectBuilder(); + objectBuilder.add("key", "val"); + final JsonArrayBuilder builder = Json.createArrayBuilder(); + builder.add(3).add(4).add(objectBuilder); + final JsonArrayBuilder builder2 = Json.createArrayBuilder(); + builder2.add(1).add(2).add(builder); + assertEquals("[1,2,[3,4,{\"key\":\"val\"}]]", builder2.build().toString()); + } + + @Test public void nullArray() { final JsonArrayBuilder builder = Json.createArrayBuilder(); builder.addNull().addNull(); @@ -52,6 +86,14 @@ public class JsonArrayBuilderImplTest { } @Test + public void nullArrayNonChaining() { + final JsonArrayBuilder builder = Json.createArrayBuilder(); + builder.addNull(); + builder.addNull(); + assertEquals("[null,null]", builder.build().toString()); + } + + @Test public void nullJsonValueArray() { final JsonArrayBuilder builder = Json.createArrayBuilder(); builder.add(JsonValue.NULL).add(JsonValue.NULL); @@ -65,6 +107,13 @@ public class JsonArrayBuilderImplTest { assertEquals("[true,false]", builder.build().toString()); } + @Test + public void numJsonValueArray() { + final JsonArrayBuilder builder = Json.createArrayBuilder(); + builder.add(123.12d).add(new BigDecimal("456.789E-12")).add(-0).add(0).add((short)1).add((byte)1); + assertEquals("[123.12,4.56789E-10,0,0,1,1]", builder.build().toString()); + } + @Test(expected=NullPointerException.class) public void addStringNpeIfNull() { final JsonArrayBuilder builder = Json.createArrayBuilder(); diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayImplTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayImplTest.java index 0b89de4..59b74d2 100644 --- a/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayImplTest.java +++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonArrayImplTest.java @@ -18,11 +18,13 @@ */ package org.apache.fleece.core; -import org.apache.fleece.core.JsonArrayImpl; -import org.apache.fleece.core.JsonStringImpl; -import org.junit.Test; - import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import javax.json.JsonArray; + +import org.junit.Test; public class JsonArrayImplTest { @Test @@ -32,4 +34,25 @@ public class JsonArrayImplTest { object.addInternal(new JsonStringImpl("b")); assertEquals("[\"a\",\"b\"]", object.toString()); } + + @Test + public void arrayIndex() { + final JsonArrayImpl object = new JsonArrayImpl(); + object.addInternal(new JsonStringImpl("a")); + object.addInternal(new JsonStringImpl("b")); + object.addInternal(new JsonLongImpl(5)); + final JsonArray array = (JsonArray) object; + assertFalse(array.isEmpty()); + assertEquals("a", object.getJsonString(0).getString()); + assertEquals("b", object.getJsonString(1).getString()); + assertEquals(5, object.getJsonNumber(2).longValue()); + assertEquals("[\"a\",\"b\",5]", object.toString()); + } + + @Test + public void emptyArray() { + final JsonArray array = new JsonArrayImpl(); + assertTrue(array.isEmpty()); + assertEquals("[]", array.toString()); + } } diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonGeneratorImplTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonGeneratorImplTest.java index a6e750f..374526f 100644 --- a/fleece-core/src/test/java/org/apache/fleece/core/JsonGeneratorImplTest.java +++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonGeneratorImplTest.java @@ -18,16 +18,16 @@ */ package org.apache.fleece.core; -import org.junit.Test; +import static org.junit.Assert.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.util.HashMap; import javax.json.Json; import javax.json.JsonValue; import javax.json.stream.JsonGenerator; -import java.io.ByteArrayOutputStream; -import java.util.HashMap; - -import static org.junit.Assert.assertEquals; +import org.junit.Test; public class JsonGeneratorImplTest { @Test @@ -67,8 +67,15 @@ public class JsonGeneratorImplTest { @Test public void stringArrayEscapes() { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Json.createGenerator(baos).writeStartArray().write("\"val1\t\u0080").write("val2\\").writeEnd().close(); - assertEquals("[\"\\\"val1\\t\\u0080\",\"val2\\\\\"]", new String(baos.toByteArray())); + Json.createGenerator(baos).writeStartArray().write("\"val1\t\u0010").write("val2\\").writeEnd().close(); + assertEquals("[\"\\\"val1\\t\\u0010\",\"val2\\\\\"]", new String(baos.toByteArray())); + } + + @Test + public void stringArrayEscapes2() { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Json.createGenerator(baos).writeStartArray().write("\"val1\t\u0067").write("val2\\").writeEnd().close(); + assertEquals("[\"\\\"val1\\tg\",\"val2\\\\\"]", new String(baos.toByteArray())); } @Test diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonNumberTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonNumberTest.java new file mode 100644 index 0000000..7c95827 --- /dev/null +++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonNumberTest.java @@ -0,0 +1,51 @@ +/* + * 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.fleece.core; + +import java.math.BigInteger; + +import javax.json.Json; +import javax.json.JsonArray; + +import org.junit.Assert; +import org.junit.Test; + + + +public class JsonNumberTest { + + @Test(expected=ArithmeticException.class) + public void testBigIntegerExact() { + + JsonArray array = Json.createArrayBuilder().add(100.0200).build(); + array.getJsonNumber(0).bigIntegerValueExact(); + + + } + + @Test + public void testBigInteger() { + + JsonArray array = Json.createArrayBuilder().add(100.0200).build(); + Assert.assertEquals(new BigInteger("100"), array.getJsonNumber(0).bigIntegerValue()); + + + } + +} diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectBuilderImplTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectBuilderImplTest.java index ea14a71..3462a41 100644 --- a/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectBuilderImplTest.java +++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectBuilderImplTest.java @@ -18,12 +18,12 @@ */ package org.apache.fleece.core; -import org.junit.Test; +import static org.junit.Assert.assertEquals; import javax.json.Json; import javax.json.JsonObjectBuilder; -import static org.junit.Assert.assertEquals; +import org.junit.Test; public class JsonObjectBuilderImplTest { @Test diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectImplTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectImplTest.java index a7aff07..6ebb3e5 100644 --- a/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectImplTest.java +++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonObjectImplTest.java @@ -18,12 +18,10 @@ */ package org.apache.fleece.core; -import org.apache.fleece.core.JsonObjectImpl; -import org.apache.fleece.core.JsonStringImpl; -import org.junit.Test; - import static org.junit.Assert.assertEquals; +import org.junit.Test; + public class JsonObjectImplTest { @Test public void objectToString() { diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonParserTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonParserTest.java index dd6b534..a5027b5 100644 --- a/fleece-core/src/test/java/org/apache/fleece/core/JsonParserTest.java +++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonParserTest.java @@ -25,21 +25,43 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; +import java.io.CharArrayReader; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; +import java.nio.charset.Charset; import java.util.Collections; import java.util.HashMap; +import java.util.NoSuchElementException; import javax.json.Json; import javax.json.JsonArray; +import javax.json.JsonException; import javax.json.JsonReader; import javax.json.stream.JsonParser; +import javax.json.stream.JsonParser.Event; +import javax.json.stream.JsonParserFactory; import javax.json.stream.JsonParsingException; import org.junit.Test; public class JsonParserTest { + + + static final Charset UTF_8 = Charset.forName("UTF-8"); + static final Charset UTF_16BE = Charset.forName("UTF-16BE"); + static final Charset UTF_16LE = Charset.forName("UTF-16LE"); + static final Charset UTF_16 = Charset.forName("UTF-16"); + static final Charset UTF_32LE = Charset.forName("UTF-32LE"); + static final Charset UTF_32BE = Charset.forName("UTF-32BE"); + + public JsonParserTest() { + super(); + if (!Charset.defaultCharset().equals(Charset.forName("UTF-8"))) { + throw new RuntimeException("Default charset is " + Charset.defaultCharset() + ", must must be UTF-8"); + } + } + private void assertSimple(final JsonParser parser) { { assertTrue(parser.hasNext()); @@ -216,6 +238,21 @@ public class JsonParserTest { assertNotNull(parser); assertSimple(parser); } + + @Test + public void simpleUTF16LE() { + final JsonParser parser = Json.createParserFactory(null).createParser(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/simple_utf16le.json"),UTF_16LE); + assertNotNull(parser); + assertSimple(parser); + } + + @Test + public void simpleUTF16LEAutoDetect() { + final JsonParser parser = Json.createParserFactory(null).createParser(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/simple_utf16le.json")); + assertNotNull(parser); + assertSimple(parser); + } + @Test public void nested() { @@ -713,13 +750,80 @@ public class JsonParserTest { Json.createReader(new ByteArrayInputStream("{\"z\":nulll}".getBytes())).read(); } - @Test(expected = JsonParsingException.class) + @Test(expected = JsonException.class) public void zeroByteInput() { // using a reader as wrapper of parser Json.createReader(new ByteArrayInputStream(new byte[]{})).read(); } + @Test(expected = JsonParsingException.class) + public void zeroCharInput() { + // using a reader as wrapper of parser + + Json.createReader(new CharArrayReader(new char[]{})).read(); + } + + @Test + public void testUTF32LEStream() { + ByteArrayInputStream bin = new ByteArrayInputStream("[\"UTF32LE\"]".getBytes(UTF_32LE)); + JsonParser parser = Json.createParser(bin); + parser.next(); + parser.next(); + assertEquals("UTF32LE", parser.getString()); + parser.next(); + assertTrue(!parser.hasNext()); + parser.close(); + } + + @Test + public void testUTF32BEStream() { + ByteArrayInputStream bin = new ByteArrayInputStream("[\"UTF32BE\"]".getBytes(UTF_32BE)); + JsonParser parser = Json.createParser(bin); + parser.next(); + parser.next(); + assertEquals("UTF32BE", parser.getString()); + parser.next(); + assertTrue(!parser.hasNext()); + parser.close(); + } + + @Test + public void testUTF16BEStream() { + ByteArrayInputStream bin = new ByteArrayInputStream("[\"UTF16BE\"]".getBytes(UTF_16BE)); + JsonParser parser = Json.createParser(bin); + parser.next(); + parser.next(); + assertEquals("UTF16BE", parser.getString()); + parser.next(); + assertTrue(!parser.hasNext()); + parser.close(); + } + + @Test + public void testUTF16LEStream() { + ByteArrayInputStream bin = new ByteArrayInputStream("[\"UTF16LE\"]".getBytes(UTF_16LE)); + JsonParser parser = Json.createParser(bin); + parser.next(); + parser.next(); + assertEquals("UTF16LE", parser.getString()); + parser.next(); + assertTrue(!parser.hasNext()); + parser.close(); + } + + @Test + public void testUTF8Stream() { + ByteArrayInputStream bin = new ByteArrayInputStream("[\"UTF8\"]".getBytes(UTF_8)); + JsonParser parser = Json.createParser(bin); + parser.next(); + parser.next(); + assertEquals("UTF8", parser.getString()); + parser.next(); + assertTrue(!parser.hasNext()); + parser.close(); + } + @Test public void shortestNonEmtyJsonFile() { // using a reader as wrapper of parser @@ -735,6 +839,109 @@ public class JsonParserTest { assertEquals(0L, Json.createReader(new ByteArrayInputStream(" \n\n [ 0 ] \n\n".getBytes())).readArray().getJsonNumber(0).longValue()); } + @Test + public void escapeStart() { + // using a reader as wrapper of parser + + assertEquals("\\abcdef", Json.createReader(new ByteArrayInputStream("[\"\\\\abcdef\"]".getBytes())).readArray().getString(0)); + } + + @Test + public void escapeStart2() { + // using a reader as wrapper of parser + + assertEquals("\"abcdef", Json.createReader(new ByteArrayInputStream("[\"\\\"abcdef\"]".getBytes())).readArray().getString(0)); + } + + @Test + public void threeLiterals() { + final JsonParser parser = Json.createParserFactory(new HashMap() {{ + put(JsonParserFactoryImpl.MAX_STRING_LENGTH, 10); + }}).createParser(new ByteArrayInputStream("{\"a\":true,\"b\":null,\"c\":false,\"arr\":[false, true, null]}".getBytes())); + parser.next(); + parser.next(); + assertEquals(JsonParser.Event.VALUE_TRUE, parser.next()); + parser.next(); + assertEquals(JsonParser.Event.VALUE_NULL, parser.next()); + parser.next(); + assertEquals(JsonParser.Event.VALUE_FALSE, parser.next()); + parser.next(); + parser.next(); + assertEquals(JsonParser.Event.VALUE_FALSE, parser.next()); + assertEquals(JsonParser.Event.VALUE_TRUE, parser.next()); + assertEquals(JsonParser.Event.VALUE_NULL, parser.next()); + parser.close(); + } + + @Test + public void maxStringStringOK() { + // using a reader as wrapper of parser + Json.createReaderFactory(new HashMap() { + { + put("org.apache.fleece.max-string-length", "5"); + } + }).createReader(new ByteArrayInputStream("[\"abcde\"]".getBytes())).read(); + + } + + @Test(expected = JsonParsingException.class) + public void maxStringStringFail() { + // using a reader as wrapper of parser + Json.createReaderFactory(new HashMap() { + { + put("org.apache.fleece.max-string-length", "5"); + } + }).createReader(new ByteArrayInputStream("[\"abcdef\"]".getBytes())).read(); + + } + + @Test + public void maxStringNumberOK() { + // using a reader as wrapper of parser + Json.createReaderFactory(new HashMap() { + { + put("org.apache.fleece.max-string-length", "5"); + } + }).createReader(new ByteArrayInputStream("[12.3]".getBytes())).read(); + + } + + @Test(expected = JsonParsingException.class) + public void maxStringNumberFail() { + // using a reader as wrapper of parser + Json.createReaderFactory(new HashMap() { + { + put("org.apache.fleece.max-string-length", "5"); + } + }).createReader(new ByteArrayInputStream("[12.333]".getBytes())).read(); + + } + + @Test(expected = JsonParsingException.class) + public void maxStringWhitespace() { + // using a reader as wrapper of parser + Json.createReaderFactory(new HashMap() { + { + put("org.apache.fleece.max-string-length", "5"); + } + }).createReader(new ByteArrayInputStream("[\"12\" ]".getBytes())).read(); + + } + + + @Test + public void testEmptyArray() { + JsonParser parser = Json.createParser(new ByteArrayInputStream("[]".getBytes())); + assertEquals(Event.START_ARRAY, parser.next()); + assertEquals(Event.END_ARRAY, parser.next()); + assertEquals(false, parser.hasNext()); + try { + parser.next(); + fail("Should have thrown a NoSuchElementException"); + } catch (NoSuchElementException ne) { + } + } + @Test(expected = JsonParsingException.class) public void fail1() { @@ -886,4 +1093,288 @@ public class JsonParserTest { Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail25.json")).read(); } + @Test(expected = JsonParsingException.class) + public void fail26() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail26.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail27() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail27.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail28() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail28.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail29() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail29.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail30() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail30.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail31() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail31.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail32() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail32.json")).read(); + } + + + @Test(expected = JsonParsingException.class) + public void fail33() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail33.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail34() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail34.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail35() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail35.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail36() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail36.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail37() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail37.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail38() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail38.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail39() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail39.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail40() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail40.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail41() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail41.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail42() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail42.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail43() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail43.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail44() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail44.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail45() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail45.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail46() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail46.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail47() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail47.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail48() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail48.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail49() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail49.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail50() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail50.json")).read(); + } + + //@Test(expected = JsonParsingException.class) + public void fail51() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail51.json")).read(); + } + + //@Test(expected = JsonParsingException.class) + public void fail52() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail52.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail53() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail53.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail54() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail54.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail55() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail55.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail56() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail56.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail57() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail57.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail58() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail58.json")).read(); + } + + @Test(expected = JsonException.class) + public void fail59() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail59.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail60() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail60.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail61() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail61.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail62() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail62.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail63() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail63.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail64() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail64.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail65() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail65.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail66() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail66.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail67() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail67.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail68() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail68.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail69() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail69.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail70() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail70.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail71() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail71.json")).read(); + } + + @Test(expected = JsonParsingException.class) + public void fail72() { + + Json.createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/fails/fail72.json")).read(); + } + + } diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonReaderImplTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonReaderImplTest.java index 2cb95c1..abad642 100644 --- a/fleece-core/src/test/java/org/apache/fleece/core/JsonReaderImplTest.java +++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonReaderImplTest.java @@ -42,6 +42,15 @@ import org.junit.Test; public class JsonReaderImplTest { + + + public JsonReaderImplTest() { + super(); + if (!Charset.defaultCharset().equals(Charset.forName("UTF-8"))) { + throw new RuntimeException("Default charset is " + Charset.defaultCharset() + ", must must be UTF-8"); + } + } + protected static Charset utf8Charset = Charset.forName("UTF8"); protected static Charset asciiCharset = Charset.forName("ASCII"); @@ -125,6 +134,7 @@ public class JsonReaderImplTest { assertTrue(object.getJsonNumber("w").doubleValue() > 4 && object.getJsonNumber("w").doubleValue() < 5); assertEquals(110, object.getInt("1.4312")); assertEquals("\"", object.getString("\"")); + assertTrue(object.isNull("\u0044")); assertEquals("ন:4::,[{", object.getString("থii:üäöÖ.,;.-<>!§$%&()=?ß´'`*+#")); reader.close(); } @@ -319,6 +329,28 @@ public class JsonReaderImplTest { assertEquals(-2, array.getInt(1)); reader.close(); } + + @Test + public void simple2BadBufferSize8() { + final JsonReader reader = Json.createReaderFactory(new HashMap() { + { + put("org.apache.fleece.default-char-buffer", "8"); + } + }).createReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/simple2.json"), utf8Charset); + assertNotNull(reader); + final JsonObject object = reader.readObject(); + assertNotNull(object); + assertEquals(3, object.size()); + assertEquals("b", object.getString("a")); + assertEquals(4, object.getInt("c")); + assertThat(object.get("d"), instanceOf(JsonArray.class)); + final JsonArray array = object.getJsonArray("d"); + assertNotNull(array); + assertEquals(2, array.size()); + assertEquals(1, array.getInt(0)); + assertEquals(-2, array.getInt(1)); + reader.close(); + } @Test public void simpleBadBufferSize9() { diff --git a/fleece-core/src/test/java/org/apache/fleece/core/JsonWriterImplTest.java b/fleece-core/src/test/java/org/apache/fleece/core/JsonWriterImplTest.java index 8c3d921..8887570 100644 --- a/fleece-core/src/test/java/org/apache/fleece/core/JsonWriterImplTest.java +++ b/fleece-core/src/test/java/org/apache/fleece/core/JsonWriterImplTest.java @@ -18,15 +18,14 @@ */ package org.apache.fleece.core; -import org.apache.fleece.core.JsonObjectImpl; -import org.apache.fleece.core.JsonStringImpl; -import org.junit.Test; +import static org.junit.Assert.assertEquals; + +import java.io.ByteArrayOutputStream; import javax.json.Json; import javax.json.JsonWriter; -import java.io.ByteArrayOutputStream; -import static org.junit.Assert.assertEquals; +import org.junit.Test; public class JsonWriterImplTest { @Test diff --git a/fleece-core/src/test/java/org/apache/fleece/core/LocationTest.java b/fleece-core/src/test/java/org/apache/fleece/core/LocationTest.java new file mode 100644 index 0000000..1d73d3d --- /dev/null +++ b/fleece-core/src/test/java/org/apache/fleece/core/LocationTest.java @@ -0,0 +1,196 @@ +/* + * 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.fleece.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.StringReader; +import java.util.HashMap; + +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonReader; +import javax.json.JsonWriter; +import javax.json.stream.JsonLocation; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParsingException; + +import org.junit.Assert; +import org.junit.Test; + +public class LocationTest { + @Test + public void failBytesInput() { + + try { + JsonReader reader = Json.createReader(new StringReader("{\"z\":nulll}")); + reader.read(); + Assert.fail("Exception expected"); + } catch (JsonParsingException e) { + JsonLocation location = e.getLocation(); + Assert.assertNotNull(location); + Assert.assertEquals(new JsonLocationImpl(1, 11, 10), location); + + } + + + try { + JsonReader reader = Json.createReader(new StringReader("{\"z\":\nnulll}")); + reader.read(); + Assert.fail("Exception expected"); + } catch (JsonParsingException e) { + JsonLocation location = e.getLocation(); + Assert.assertNotNull(location); + Assert.assertEquals(new JsonLocationImpl(2, 6, 11), location); + + } + + try { + JsonReader reader = Json.createReader(new StringReader("aaa")); + reader.read(); + Assert.fail("Exception expected"); + } catch (JsonParsingException e) { + JsonLocation location = e.getLocation(); + Assert.assertNotNull(location); + Assert.assertEquals(new JsonLocationImpl(1, 2, 1), location); + + } + } + + + @Test + public void simpleLocation() { + + + JsonParser parser = Json.createParser(new StringReader("{\n \"ö \uffff c\": null ,\n \"test\" :\"testval\",\n \"num\": 45.1e-12 \n}")); + + /* + + +{ + "z a c": null , + "test" :"testval", + "num": 45.1e-12 //2 ws +} + + + */ + + Assert.assertEquals(new JsonLocationImpl(1, 1, 0), parser.getLocation()); + parser.next(); //after first { + Assert.assertEquals(new JsonLocationImpl(1, 2, 1), parser.getLocation()); + parser.next(); //after "ö \uffff c" + Assert.assertEquals(new JsonLocationImpl(2, 12, 13), parser.getLocation()); + parser.next(); //after null + Assert.assertEquals(new JsonLocationImpl(2, 18, 19), parser.getLocation()); + parser.next(); //after test + Assert.assertEquals(new JsonLocationImpl(3, 11, 32), parser.getLocation()); + parser.next(); //after testval + Assert.assertEquals(new JsonLocationImpl(3, 23, 44), parser.getLocation()); + parser.next(); //after num + Assert.assertEquals(new JsonLocationImpl(4, 10, 55), parser.getLocation()); + parser.next(); //after 45.1e-12 + Assert.assertEquals(new JsonLocationImpl(4, 20, 65), parser.getLocation()); + parser.next(); //after } + Assert.assertEquals(new JsonLocationImpl(5, 2, 69), parser.getLocation()); + + } + + /*@Test + public void simpleLocationBytes() { + + JsonParser parser = Json.createParser(new ByteArrayInputStream("{\n \"ö \uffff c\": null ,\n \"test\" :\"testval\",\n \"num\": 45.1e-12 \n}".getBytes())); + + Assert.assertEquals(new JsonLocationImpl(1, 1, 0), parser.getLocation()); + parser.next(); //after first { + Assert.assertEquals(new JsonLocationImpl(1, 2, 2), parser.getLocation()); + parser.next(); //after "ö \uffff c" + Assert.assertEquals(new JsonLocationImpl(2, 12, 26), parser.getLocation()); + parser.next(); //after null + Assert.assertEquals(new JsonLocationImpl(2, 18, 38), parser.getLocation()); + parser.next(); //after test + Assert.assertEquals(new JsonLocationImpl(3, 11, 64), parser.getLocation()); + parser.next(); //after testval + Assert.assertEquals(new JsonLocationImpl(3, 23, 88), parser.getLocation()); + parser.next(); //after num + Assert.assertEquals(new JsonLocationImpl(4, 10, 110), parser.getLocation()); + parser.next(); //after 45.1e-12 + Assert.assertEquals(new JsonLocationImpl(4, 20, 130), parser.getLocation()); + parser.next(); //after } + Assert.assertEquals(new JsonLocationImpl(5, 2, 138), parser.getLocation()); + + }*/ + + @Test + public void simpleLocationCrossingBufferBoundaries() { + + + for (int i = 1; i <= 100; i++) { + final String value = String.valueOf(i); + final JsonParser parser = Json.createParserFactory(new HashMap() { + { + put("org.apache.fleece.default-char-buffer", value); + } + }).createParser(new StringReader("{\n \"z a c\": null ,\n \"test\" :\"testval\",\n \"num\": 45.1e-12 \n}")); + + + /* + + + { + "z a c": null , + "test" :"testval", + "num": 45.1e-12 //2 ws + } + + + */ + + + + Assert.assertEquals(new JsonLocationImpl(1, 1, 0), parser.getLocation()); + parser.next(); //after first { + Assert.assertEquals(new JsonLocationImpl(1, 2, 1), parser.getLocation()); + parser.next(); //after "z a c" + Assert.assertEquals(new JsonLocationImpl(2, 12, 13), parser.getLocation()); + parser.next(); //after null + Assert.assertEquals(new JsonLocationImpl(2, 18, 19), parser.getLocation()); + parser.next(); //after test + Assert.assertEquals(new JsonLocationImpl(3, 11, 32), parser.getLocation()); + parser.next(); //after testval + Assert.assertEquals(new JsonLocationImpl(3, 23, 44), parser.getLocation()); + parser.next(); //after num + Assert.assertEquals(new JsonLocationImpl(4, 10, 55), parser.getLocation()); + parser.next(); //after 45.1e-12 + Assert.assertEquals(new JsonLocationImpl(4, 20, 65), parser.getLocation()); + parser.next(); //after } + Assert.assertEquals(new JsonLocationImpl(5, 2, 69), parser.getLocation()); + + Assert.assertFalse(parser.hasNext()); + Assert.assertFalse(parser.hasNext()); + } + + + + + } +} diff --git a/fleece-core/src/test/resources/json/fails/fail23.json b/fleece-core/src/test/resources/json/fails/fail23.json index 489c56b..b10e1d9 100644 --- a/fleece-core/src/test/resources/json/fails/fail23.json +++ b/fleece-core/src/test/resources/json/fails/fail23.json @@ -1,8 +1,8 @@ { "a": "\u6565", - "c": "\u000", + "c": "\u123", "d": [ - -2, "\u0020" + -2, "\u0048" ] } \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail25.json b/fleece-core/src/test/resources/json/fails/fail25.json index ceadcbe..f72b803 100644 --- a/fleece-core/src/test/resources/json/fails/fail25.json +++ b/fleece-core/src/test/resources/json/fails/fail25.json @@ -3,6 +3,6 @@ "c": "\up000", "d": [ - -2, "\u0020" + -2, "\u0044" ] } \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail26.json b/fleece-core/src/test/resources/json/fails/fail26.json new file mode 100644 index 0000000..128454e --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail26.json @@ -0,0 +1,7 @@ +,{ + "a": "\u6565", + "d": [ + + -2, "\u0044" + ] +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail27.json b/fleece-core/src/test/resources/json/fails/fail27.json new file mode 100644 index 0000000..3ae9085 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail27.json @@ -0,0 +1,7 @@ +"{ + "a": "\u6565", + "d": [ + + -2, "\u0044" + ] +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail28.json b/fleece-core/src/test/resources/json/fails/fail28.json new file mode 100644 index 0000000..11ba875 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail28.json @@ -0,0 +1,7 @@ +true, { + "a": "\u6565", + "d": [ + + -2, "\u0044" + ] +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail29.json b/fleece-core/src/test/resources/json/fails/fail29.json new file mode 100644 index 0000000..8eff000 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail29.json @@ -0,0 +1,7 @@ + : { + "a": "\u6565", + "d": [ + + -2, "\u0044" + ] +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail30.json b/fleece-core/src/test/resources/json/fails/fail30.json new file mode 100644 index 0000000..555e395 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail30.json @@ -0,0 +1,7 @@ +null{ + "a": "\u6565", + "d": [ + + -2, "\u0044" + ] +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail31.json b/fleece-core/src/test/resources/json/fails/fail31.json new file mode 100644 index 0000000..f1c8332 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail31.json @@ -0,0 +1,7 @@ +"string":{ + "a": "\u6565", + "d": [ + + -2, "\u0044" + ] +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail32.json b/fleece-core/src/test/resources/json/fails/fail32.json new file mode 100644 index 0000000..7883a28 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail32.json @@ -0,0 +1,7 @@ +{ + "a": "\u6565", + "d": [ + + -2, 20e-1.2 + ] +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail33.json b/fleece-core/src/test/resources/json/fails/fail33.json new file mode 100644 index 0000000..308571d --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail33.json @@ -0,0 +1,7 @@ +14{ + "a": "\u6565", + "d": [ + + -2, "\u0044" + ] +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail34.json b/fleece-core/src/test/resources/json/fails/fail34.json new file mode 100644 index 0000000..619a684 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail34.json @@ -0,0 +1,7 @@ +{ + "a": "\u6565", + "d": [ + + -2, 2.0e-1.2 + ] +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail35.json b/fleece-core/src/test/resources/json/fails/fail35.json new file mode 100644 index 0000000..cfda2b8 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail35.json @@ -0,0 +1,7 @@ + , { + "a": "\u6565", + "d": [ + + -2, "\u0044" + ] +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail36.json b/fleece-core/src/test/resources/json/fails/fail36.json new file mode 100644 index 0000000..57fff2f --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail36.json @@ -0,0 +1,3 @@ +{ + "a": " " +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail37.json b/fleece-core/src/test/resources/json/fails/fail37.json new file mode 100644 index 0000000..6e894f9 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail37.json @@ -0,0 +1,4 @@ +{ + "a + ": "x" +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail38.json b/fleece-core/src/test/resources/json/fails/fail38.json new file mode 100644 index 0000000..0c90427 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail38.json @@ -0,0 +1,3 @@ +{ + "a": 12.2e- +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail39.json b/fleece-core/src/test/resources/json/fails/fail39.json new file mode 100644 index 0000000..ac47891 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail39.json @@ -0,0 +1,3 @@ +{ + "a": 12.2e +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail40.json b/fleece-core/src/test/resources/json/fails/fail40.json new file mode 100644 index 0000000..7a503fb --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail40.json @@ -0,0 +1,4 @@ +{ + "a": "v" +} +some garbage \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail41.json b/fleece-core/src/test/resources/json/fails/fail41.json new file mode 100644 index 0000000..352963d --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail41.json @@ -0,0 +1,3 @@ +{ + "a": "\vbadescape" +} diff --git a/fleece-core/src/test/resources/json/fails/fail42.json b/fleece-core/src/test/resources/json/fails/fail42.json new file mode 100644 index 0000000..ed6e301 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail42.json @@ -0,0 +1,3 @@ +{ + "a": -055 +} diff --git a/fleece-core/src/test/resources/json/fails/fail43.json b/fleece-core/src/test/resources/json/fails/fail43.json new file mode 100644 index 0000000..46fcf2e --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail43.json @@ -0,0 +1,3 @@ +{ + "a": -01.55 +} diff --git a/fleece-core/src/test/resources/json/fails/fail44.json b/fleece-core/src/test/resources/json/fails/fail44.json new file mode 100644 index 0000000..4e963bb --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail44.json @@ -0,0 +1,3 @@ +{ + "a": -00001.55 +} diff --git a/fleece-core/src/test/resources/json/fails/fail45.json b/fleece-core/src/test/resources/json/fails/fail45.json new file mode 100644 index 0000000..ccc49ff --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail45.json @@ -0,0 +1,3 @@ +{ + "a": " before this word the is a u+0001 char" +} diff --git a/fleece-core/src/test/resources/json/fails/fail46.json b/fleece-core/src/test/resources/json/fails/fail46.json new file mode 100644 index 0000000..54b8681 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail46.json @@ -0,0 +1,3 @@ +{ + "a": "before this is a u+00031 char" +} diff --git a/fleece-core/src/test/resources/json/fails/fail47.json b/fleece-core/src/test/resources/json/fails/fail47.json new file mode 100644 index 0000000..13f6f1d --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail47.json @@ -0,0 +1 @@ +[1,] \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail48.json b/fleece-core/src/test/resources/json/fails/fail48.json new file mode 100644 index 0000000..e4d4ddd --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail48.json @@ -0,0 +1,6 @@ +[1, +2, +3, +4, +5, +] \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail49.json b/fleece-core/src/test/resources/json/fails/fail49.json new file mode 100644 index 0000000..3acaa8f --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail49.json @@ -0,0 +1 @@ +{"a":1,} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail50.json b/fleece-core/src/test/resources/json/fails/fail50.json new file mode 100644 index 0000000..c7e2925 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail50.json @@ -0,0 +1,4 @@ +{ +"a":1, +"b":2, +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail51.json b/fleece-core/src/test/resources/json/fails/fail51.json new file mode 100644 index 0000000..dec2b86 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail51.json @@ -0,0 +1 @@ +["\uDFAA (missing high surrogate)"] \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail52.json b/fleece-core/src/test/resources/json/fails/fail52.json new file mode 100644 index 0000000..eeea18b --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail52.json @@ -0,0 +1 @@ +["\uD888\u3210 (invalid second surrogate)"] \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail53.json b/fleece-core/src/test/resources/json/fails/fail53.json new file mode 100644 index 0000000..5819cae --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail53.json @@ -0,0 +1,11 @@ +{ + "0": 0, + "0": -0, + "1": 1, + "-1": -1, + "9": 9, + "-9": -9, + "10": 10, + "-10", -10, + "100": 100 +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail54.json b/fleece-core/src/test/resources/json/fails/fail54.json new file mode 100644 index 0000000..0fffc9b --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail54.json @@ -0,0 +1,12 @@ +{ + "0": 0, + "0": -0, + "1": 1, + "-1": -1, + "9": 9, + "-9": -9, + "10": 10, + "-10" -10, + "100": 100, + "-100", -100 +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail55.json b/fleece-core/src/test/resources/json/fails/fail55.json new file mode 100644 index 0000000..a3a4074 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail55.json @@ -0,0 +1,12 @@ +{ + "0": 0, + "0": -0, + "1": 1, + "-1": -1, + "9": 9, + "-9": -9, + "10": 10, + -10 -10, + "100": 100, + "-100", -100 +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail56.json b/fleece-core/src/test/resources/json/fails/fail56.json new file mode 100644 index 0000000..820d41e --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail56.json @@ -0,0 +1,11 @@ +{ + "0": 0, + "0": -0, + "1": 1, + "-1": -1, + "9": 9, + "-9": -9, + "10": 10, + "-10", "-10", + "100": 100 +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail57.json b/fleece-core/src/test/resources/json/fails/fail57.json new file mode 100644 index 0000000..ffe9c1c --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail57.json @@ -0,0 +1 @@ +["a":3] \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail58.json b/fleece-core/src/test/resources/json/fails/fail58.json new file mode 100644 index 0000000..270b5d5 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail58.json @@ -0,0 +1,3 @@ +{ + "0":["a":3] +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail59.json b/fleece-core/src/test/resources/json/fails/fail59.json new file mode 100644 index 0000000..81750b9 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail59.json @@ -0,0 +1 @@ +{ \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail60.json b/fleece-core/src/test/resources/json/fails/fail60.json new file mode 100644 index 0000000..17be25b --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail60.json @@ -0,0 +1 @@ +[ {}, [] } \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/fails/fail61.json b/fleece-core/src/test/resources/json/fails/fail61.json new file mode 100644 index 0000000..0b9fc5b --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail61.json @@ -0,0 +1,3 @@ +{ + "a": - +} diff --git a/fleece-core/src/test/resources/json/fails/fail62.json b/fleece-core/src/test/resources/json/fails/fail62.json new file mode 100644 index 0000000..ac5e00d --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail62.json @@ -0,0 +1,3 @@ +{ + "a": +} diff --git a/fleece-core/src/test/resources/json/fails/fail63.json b/fleece-core/src/test/resources/json/fails/fail63.json new file mode 100644 index 0000000..29bdc57 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail63.json @@ -0,0 +1,3 @@ +{ + "a" +} diff --git a/fleece-core/src/test/resources/json/fails/fail64.json b/fleece-core/src/test/resources/json/fails/fail64.json new file mode 100644 index 0000000..3edb81e --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail64.json @@ -0,0 +1,3 @@ +{ + "a": 1., +} diff --git a/fleece-core/src/test/resources/json/fails/fail65.json b/fleece-core/src/test/resources/json/fails/fail65.json new file mode 100644 index 0000000..3adb8bb --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail65.json @@ -0,0 +1,3 @@ +{ + "a": .5, +} diff --git a/fleece-core/src/test/resources/json/fails/fail66.json b/fleece-core/src/test/resources/json/fails/fail66.json new file mode 100644 index 0000000..129cac0 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail66.json @@ -0,0 +1,3 @@ +{ + "a": 1-, +} diff --git a/fleece-core/src/test/resources/json/fails/fail67.json b/fleece-core/src/test/resources/json/fails/fail67.json new file mode 100644 index 0000000..5df5fe3 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail67.json @@ -0,0 +1,3 @@ +{ + "a": 1.-, +} diff --git a/fleece-core/src/test/resources/json/fails/fail68.json b/fleece-core/src/test/resources/json/fails/fail68.json new file mode 100644 index 0000000..2c44166 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail68.json @@ -0,0 +1,3 @@ +{ + "a": 1.9e-1.2, +} diff --git a/fleece-core/src/test/resources/json/fails/fail69.json b/fleece-core/src/test/resources/json/fails/fail69.json new file mode 100644 index 0000000..349826b --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail69.json @@ -0,0 +1,3 @@ +{ + 12 +} diff --git a/fleece-core/src/test/resources/json/fails/fail70.json b/fleece-core/src/test/resources/json/fails/fail70.json new file mode 100644 index 0000000..0c90165 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail70.json @@ -0,0 +1,3 @@ +{ + null null +} diff --git a/fleece-core/src/test/resources/json/fails/fail71.json b/fleece-core/src/test/resources/json/fails/fail71.json new file mode 100644 index 0000000..819e165 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail71.json @@ -0,0 +1,3 @@ +{ + null +} diff --git a/fleece-core/src/test/resources/json/fails/fail72.json b/fleece-core/src/test/resources/json/fails/fail72.json new file mode 100644 index 0000000..02beb05 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/fail72.json @@ -0,0 +1,3 @@ +{ + "a","b" +} diff --git a/fleece-core/src/test/resources/json/fails/numbers.json b/fleece-core/src/test/resources/json/fails/numbers.json new file mode 100644 index 0000000..d300022 --- /dev/null +++ b/fleece-core/src/test/resources/json/fails/numbers.json @@ -0,0 +1,18 @@ +{ + "0": 0, + "0": -0, + "1": 1, + "-1": -1, + "9": 9, + "-9": -9, + "10": 10, + "-10", -10, + "100": 100, + "-100", -100, + "456": 456, + "-456": -456, + "123.12345": 123.12345, + "-123.12345": -123.12345, + "999999999999999999999999999999": 999999999999999999999999999999, + "-999999999999999999999999999999": -999999999999999999999999999999 +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/numbers.json b/fleece-core/src/test/resources/json/numbers.json index d300022..847a9f8 100644 --- a/fleece-core/src/test/resources/json/numbers.json +++ b/fleece-core/src/test/resources/json/numbers.json @@ -6,9 +6,9 @@ "9": 9, "-9": -9, "10": 10, - "-10", -10, + "-10": -10, "100": 100, - "-100", -100, + "-100": -100, "456": 456, "-456": -456, "123.12345": 123.12345, diff --git a/fleece-core/src/test/resources/json/simple2.json b/fleece-core/src/test/resources/json/simple2.json new file mode 100644 index 0000000..2578a23 --- /dev/null +++ b/fleece-core/src/test/resources/json/simple2.json @@ -0,0 +1,8 @@ +{ + "a": "b", + "c": 4, + "d": [ + 1, + -2 + ] +} \ No newline at end of file diff --git a/fleece-core/src/test/resources/json/simple_utf16le.json b/fleece-core/src/test/resources/json/simple_utf16le.json new file mode 100644 index 0000000000000000000000000000000000000000..c2e2137117176935e3a0c30e0b940d665c817815 GIT binary patch literal 136 zcmb