Revert "Merge pull request #888 from rikkarth/fix/887"

This reverts commit 14f71274f7, reversing
changes made to 054786e300.
This commit is contained in:
Sean Leary
2024-11-03 09:49:23 -06:00
parent 14f71274f7
commit ab1b9a3459
12 changed files with 189 additions and 503 deletions

View File

@@ -96,83 +96,53 @@ public class JSONArray implements Iterable<Object> {
*/
public JSONArray(JSONTokener x, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
this();
char nextChar = x.nextClean();
// check first character, if not '[' throw JSONException
if (nextChar != '[') {
if (x.nextClean() != '[') {
throw x.syntaxError("A JSONArray text must start with '['");
}
parseTokener(x, jsonParserConfiguration); // runs recursively
}
private void parseTokener(JSONTokener x, JSONParserConfiguration jsonParserConfiguration) {
boolean strictMode = jsonParserConfiguration.isStrictMode();
char cursor = x.nextClean();
switch (cursor) {
case 0:
throwErrorIfEoF(x);
break;
case ',':
cursor = x.nextClean();
throwErrorIfEoF(x);
if(strictMode && cursor == ']'){
throw x.syntaxError(getInvalidCharErrorMsg(cursor));
}
if (cursor == ']') {
break;
}
x.back();
parseTokener(x, jsonParserConfiguration);
break;
case ']':
if (strictMode) {
cursor = x.nextClean();
boolean isEoF = x.end();
if (isEoF) {
break;
}
if (x.getArrayLevel() == 0) {
throw x.syntaxError(getInvalidCharErrorMsg(cursor));
}
x.back();
}
break;
default:
x.back();
boolean currentCharIsQuote = x.getPrevious() == '"';
boolean quoteIsNotNextToValidChar = x.getPreviousChar() != ',' && x.getPreviousChar() != '[';
if (strictMode && currentCharIsQuote && quoteIsNotNextToValidChar) {
throw x.syntaxError(getInvalidCharErrorMsg(cursor));
}
this.myArrayList.add(x.nextValue(jsonParserConfiguration));
parseTokener(x, jsonParserConfiguration);
char nextChar = x.nextClean();
if (nextChar == 0) {
// array is unclosed. No ']' found, instead EOF
throw x.syntaxError("Expected a ',' or ']'");
}
}
if (nextChar != ']') {
x.back();
for (;;) {
if (x.nextClean() == ',') {
x.back();
this.myArrayList.add(JSONObject.NULL);
} else {
x.back();
this.myArrayList.add(x.nextValue(jsonParserConfiguration));
}
switch (x.nextClean()) {
case 0:
// array is unclosed. No ']' found, instead EOF
throw x.syntaxError("Expected a ',' or ']'");
case ',':
nextChar = x.nextClean();
if (nextChar == 0) {
// array is unclosed. No ']' found, instead EOF
throw x.syntaxError("Expected a ',' or ']'");
}
if (nextChar == ']') {
return;
}
x.back();
break;
case ']':
if (jsonParserConfiguration.isStrictMode()) {
nextChar = x.nextClean();
if (nextChar != 0) {
throw x.syntaxError("invalid character found after end of array: " + nextChar);
}
}
/**
* Throws JSONException if JSONTokener has reached end of file, usually when array is unclosed. No ']' found,
* instead EoF.
*
* @param x the JSONTokener being evaluated.
* @throws JSONException if JSONTokener has reached end of file.
*/
private void throwErrorIfEoF(JSONTokener x) {
if (x.end()) {
throw x.syntaxError(String.format("Expected a ',' or ']' but instead found '%s'", x.getPrevious()));
return;
default:
throw x.syntaxError("Expected a ',' or ']'");
}
}
}
}
@@ -1962,7 +1932,6 @@ public class JSONArray implements Iterable<Object> {
private void addAll(Object array, boolean wrap, int recursionDepth) {
addAll(array, wrap, recursionDepth, new JSONParserConfiguration());
}
/**
* Add an array's elements to the JSONArray.
*`
@@ -2009,6 +1978,7 @@ public class JSONArray implements Iterable<Object> {
"JSONArray initial value should be a string or collection or array.");
}
}
/**
* Create a new JSONException in a common format for incorrect conversions.
* @param idx index of the item
@@ -2037,7 +2007,4 @@ public class JSONArray implements Iterable<Object> {
, cause);
}
private static String getInvalidCharErrorMsg(char cursor) {
return String.format("invalid character '%s' found after end of array", cursor);
}
}

View File

@@ -2,8 +2,6 @@ package org.json;
import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/*
Public Domain.
@@ -33,8 +31,6 @@ public class JSONTokener {
private boolean usePrevious;
/** the number of characters read in the previous line. */
private long characterPreviousLine;
private final List<Character> smallCharMemory;
private int arrayLevel = 0;
/**
@@ -53,7 +49,6 @@ public class JSONTokener {
this.character = 1;
this.characterPreviousLine = 0;
this.line = 1;
this.smallCharMemory = new ArrayList<Character>(2);
}
@@ -191,46 +186,6 @@ public class JSONTokener {
return this.previous;
}
private void insertCharacterInCharMemory(Character c) {
boolean foundSameCharRef = checkForEqualCharRefInMicroCharMemory(c);
if(foundSameCharRef){
return;
}
if(smallCharMemory.size() < 2){
smallCharMemory.add(c);
return;
}
smallCharMemory.set(0, smallCharMemory.get(1));
smallCharMemory.remove(1);
smallCharMemory.add(c);
}
private boolean checkForEqualCharRefInMicroCharMemory(Character c) {
boolean isNotEmpty = !smallCharMemory.isEmpty();
if (isNotEmpty) {
Character lastChar = smallCharMemory.get(smallCharMemory.size() - 1);
return c.compareTo(lastChar) == 0;
}
// list is empty so there's no equal characters
return false;
}
/**
* Retrieves the previous char from memory.
*
* @return previous char stored in memory.
*/
public char getPreviousChar() {
return smallCharMemory.get(0);
}
public int getArrayLevel(){
return this.arrayLevel;
}
/**
* Get the last character read from the input or '\0' if nothing has been read yet.
* @return the last character read from the input.
@@ -308,6 +263,7 @@ public class JSONTokener {
return new String(chars);
}
/**
* Get the next char in the string, skipping whitespace.
* @throws JSONException Thrown if there is an error reading the source string.
@@ -317,7 +273,6 @@ public class JSONTokener {
for (;;) {
char c = this.next();
if (c == 0 || c > ' ') {
insertCharacterInCharMemory(c);
return c;
}
}
@@ -325,66 +280,77 @@ public class JSONTokener {
/**
* Return the characters up to the next close quote character. Backslash processing is done. The formal JSON format
* does not allow strings in single quotes, but an implementation is allowed to accept them.
*
* Return the characters up to the next close quote character.
* Backslash processing is done. The formal JSON format does not
* allow strings in single quotes, but an implementation is allowed to
* accept them.
* If strictMode is true, this implementation will not accept unbalanced quotes (e.g will not accept <code>"test'</code>).
* @param quote The quoting character, either
* <code>"</code>&nbsp;<small>(double quote)</small> or
* <code>'</code>&nbsp;<small>(single quote)</small>.
* @param strictMode If true, this implementation will not accept unbalanced quotes (e.g will not accept <code>"test'</code>).
* @return A String.
* @throws JSONException Unterminated string or unbalanced quotes if strictMode == true.
*/
public String nextString(char quote) throws JSONException {
public String nextString(char quote, boolean strictMode) throws JSONException {
char c;
StringBuilder sb = new StringBuilder();
for (;;) {
c = this.next();
switch (c) {
case 0:
case '\n':
case '\r':
throw this.syntaxError("Unterminated string. " +
case 0:
case '\n':
case '\r':
throw this.syntaxError("Unterminated string. " +
"Character with int code " + (int) c + " is not allowed within a quoted string.");
case '\\':
c = this.next();
switch (c) {
case 'b':
sb.append('\b');
break;
case 't':
sb.append('\t');
break;
case 'n':
sb.append('\n');
break;
case 'f':
sb.append('\f');
break;
case 'r':
sb.append('\r');
break;
case 'u':
String next = this.next(4);
try {
sb.append((char) Integer.parseInt(next, 16));
} catch (NumberFormatException e) {
throw this.syntaxError("Illegal escape. " +
"\\u must be followed by a 4 digit hexadecimal number. \\" + next
+ " is not valid.",
e);
}
break;
case '"':
case '\'':
case '\\':
case '/':
sb.append(c);
break;
default:
throw this.syntaxError("Illegal escape. Escape sequence \\" + c + " is not valid.");
case '\\':
c = this.next();
switch (c) {
case 'b':
sb.append('\b');
break;
case 't':
sb.append('\t');
break;
case 'n':
sb.append('\n');
break;
case 'f':
sb.append('\f');
break;
case 'r':
sb.append('\r');
break;
case 'u':
String next = this.next(4);
try {
sb.append((char)Integer.parseInt(next, 16));
} catch (NumberFormatException e) {
throw this.syntaxError("Illegal escape. " +
"\\u must be followed by a 4 digit hexadecimal number. \\" + next + " is not valid.", e);
}
break;
case '"':
case '\'':
case '\\':
case '/':
sb.append(c);
break;
default:
throw this.syntaxError("Illegal escape. Escape sequence \\" + c + " is not valid.");
}
break;
default:
if (strictMode && c == '\"' && quote != c) {
throw this.syntaxError(String.format(
"Field contains unbalanced quotes. Starts with %s but ends with double quote.", quote));
}
if (strictMode && c == '\'' && quote != c) {
throw this.syntaxError(String.format(
"Field contains unbalanced quotes. Starts with %s but ends with single quote.", quote));
}
if (c == quote) {
return sb.toString();
}
@@ -393,6 +359,7 @@ public class JSONTokener {
}
}
/**
* Get the text up but not including the specified character or the
* end of line, whichever comes first.
@@ -474,8 +441,7 @@ public class JSONTokener {
case '[':
this.back();
try {
this.arrayLevel++;
return new JSONArray(this, jsonParserConfiguration);
return new JSONArray(this);
} catch (StackOverflowError e) {
throw new JSONException("JSON Array or Object depth too large to process.", e);
}
@@ -516,24 +482,15 @@ public class JSONTokener {
}
}
/**
* Get the next simple value from the JSON input. Simple values include strings (wrapped in single or double
* quotes), numbers, booleans, and null. This method is called when the next character is not '{' or '['.
*
* @param c The starting character.
* @param jsonParserConfiguration The configuration object containing parsing options.
* @return The parsed simple value.
* @throws JSONException If there is a syntax error or the value does not adhere to the configuration rules.
*/
Object nextSimpleValue(char c, JSONParserConfiguration jsonParserConfiguration) {
boolean strictMode = jsonParserConfiguration.isStrictMode();
if (strictMode && c == '\'') {
if(strictMode && c == '\''){
throw this.syntaxError("Single quote wrap not allowed in strict mode");
}
if (c == '"' || c == '\'') {
return this.nextString(c);
return this.nextString(c, strictMode);
}
return parsedUnquotedText(c, strictMode);
@@ -560,21 +517,34 @@ public class JSONTokener {
String string = sb.toString().trim();
if (strictMode) {
boolean isBooleanOrNumeric = checkIfValueIsBooleanOrNumeric(string);
if (isBooleanOrNumeric) {
return string;
}
throw new JSONException(String.format("Value is not surrounded by quotes: %s", string));
}
if (string.isEmpty()) {
throw this.syntaxError("Missing value");
}
Object stringToValue = JSONObject.stringToValue(string);
return strictMode ? getValidNumberBooleanOrNullFromObject(stringToValue) : stringToValue;
return JSONObject.stringToValue(string);
}
private Object getValidNumberBooleanOrNullFromObject(Object value) {
if (value instanceof Number || value instanceof Boolean || value.equals(JSONObject.NULL)) {
return value;
private boolean checkIfValueIsBooleanOrNumeric(Object valueToValidate) {
String stringToValidate = valueToValidate.toString();
if (stringToValidate.equals("true") || stringToValidate.equals("false")) {
return true;
}
throw this.syntaxError(String.format("Value '%s' is not surrounded by quotes", value));
try {
Double.parseDouble(stringToValidate);
return true;
} catch (NumberFormatException e) {
return false;
}
}
/**