in core/src/main/java/com/alibaba/fastjson2/JSONReader.java [2537:4744]
public List readArray(Type itemType) {
if (nextIfNull()) {
return null;
}
List list = new ArrayList();
if (ch == '[') {
next();
boolean fieldBased = (context.features & Feature.FieldBased.mask) != 0;
ObjectReader objectReader = context.provider.getObjectReader(itemType, fieldBased);
for (int i = 0; !nextIfArrayEnd(); i++) {
int mark = offset;
Object item;
if (isReference()) {
String reference = readReference();
if ("..".equals(reference)) {
item = list;
} else {
item = null;
addResolveTask(list, i, JSONPath.of(reference));
}
} else {
item = objectReader.readObject(this, null, null, 0);
}
list.add(item);
if (mark == offset || ch == '}' || ch == EOI) {
throw new JSONException("illegal input : " + ch + ", offset " + getOffset());
}
}
} else if (ch == '"' || ch == '\'' || ch == '{') {
String str = readString();
if (str != null && !str.isEmpty()) {
list.add(str);
}
} else {
throw new JSONException(info("syntax error"));
}
if (comma = (ch == ',')) {
next();
}
return list;
}
public List readList(Type[] types) {
if (nextIfNull()) {
return null;
}
if (!nextIfArrayStart()) {
throw new JSONException("syntax error : " + ch);
}
int i = 0, max = types.length;
List list = new ArrayList(max);
for (Object item; !nextIfArrayEnd() && i < max; list.add(item)) {
int mark = offset;
item = read(types[i++]);
if (mark == offset || ch == '}' || ch == EOI) {
throw new JSONException("illegal input : " + ch + ", offset " + getOffset());
}
}
if (i != max) {
throw new JSONException(info("element length mismatch"));
}
if (comma = (ch == ',')) {
next();
}
return list;
}
public final Object[] readArray(Type[] types) {
if (nextIfNull()) {
return null;
}
if (!nextIfArrayStart()) {
throw new JSONException(info("syntax error"));
}
int i = 0, max = types.length;
Object[] list = new Object[max];
for (Object item; !nextIfArrayEnd() && i < max; list[i++] = item) {
int mark = offset;
item = read(types[i]);
if (mark == offset || ch == '}' || ch == EOI) {
throw new JSONException("illegal input : " + ch + ", offset " + getOffset());
}
}
if (i != max) {
throw new JSONException(info("element length mismatch"));
}
if (comma = (ch == ',')) {
next();
}
return list;
}
public final void readArray(List list, Type itemType) {
readArray((Collection) list, itemType);
}
public void readArray(Collection list, Type itemType) {
if (nextIfArrayStart()) {
while (!nextIfArrayEnd()) {
Object item = read(itemType);
list.add(item);
}
return;
}
if (isString()) {
String str = readString();
if (itemType == String.class) {
list.add(str);
} else {
Function typeConvert = context.getProvider().getTypeConvert(String.class, itemType);
if (typeConvert == null) {
throw new JSONException(info("not support input " + str));
}
if (str.indexOf(',') != -1) {
String[] items = str.split(",");
for (String strItem : items) {
Object item = typeConvert.apply(strItem);
list.add(item);
}
} else {
Object item = typeConvert.apply(str);
list.add(item);
}
}
} else {
Object item = read(itemType);
list.add(item);
}
if (comma = (ch == ',')) {
next();
}
}
public final JSONArray readJSONArray() {
JSONArray array = new JSONArray();
read(array);
return array;
}
public final JSONObject readJSONObject() {
JSONObject object = new JSONObject();
read(object, 0L);
return object;
}
public List readArray() {
next();
level++;
if (level >= context.maxLevel) {
throw new JSONException("level too large : " + level);
}
int i = 0;
List<Object> list = null;
Object first = null, second = null;
_for:
for (; ; ++i) {
Object val;
switch (ch) {
case ']':
next();
break _for;
case '[':
val = readArray();
break;
case '{':
if (context.autoTypeBeforeHandler != null || (context.features & Feature.SupportAutoType.mask) != 0) {
val = ObjectReaderImplObject.INSTANCE.readObject(this, null, null, 0);
} else if (isReference()) {
val = JSONPath.of(readReference());
} else {
val = readObject();
}
break;
case '\'':
case '"':
val = readString();
break;
case '-':
case '+':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
readNumber0();
val = getNumber();
break;
case 't':
case 'f':
val = readBoolValue();
break;
case 'n': {
readNull();
val = null;
break;
}
case '/':
skipComment();
--i;
continue;
default:
throw new JSONException(info());
}
if (i == 0) {
first = val;
} else if (i == 1) {
second = val;
} else if (i == 2) {
if (context.arraySupplier != null) {
list = context.arraySupplier.get();
} else {
list = new JSONArray();
}
add(list, 0, first);
add(list, 1, second);
add(list, i, val);
} else {
add(list, i, val);
}
}
if (list == null) {
if (context.arraySupplier != null) {
list = context.arraySupplier.get();
} else {
if (context.isEnabled(Feature.UseNativeObject)) {
list = i == 2 ? new ArrayList(2) : new ArrayList(1);
} else {
list = i == 2 ? new JSONArray(2) : new JSONArray(1);
}
}
if (i == 1) {
add(list, 0, first);
} else if (i == 2) {
add(list, 0, first);
add(list, 1, second);
}
}
if (comma = (ch == ',')) {
next();
}
level--;
return list;
}
private void add(List<Object> list, int i, Object val) {
if (val instanceof JSONPath) {
addResolveTask(list, i, (JSONPath) val);
list.add(null);
} else {
list.add(val);
}
}
public final BigInteger getBigInteger() {
Number number = getNumber();
if (number == null) {
return null;
}
if (number instanceof BigInteger) {
return (BigInteger) number;
}
return BigInteger.valueOf(number.longValue());
}
public final BigDecimal getBigDecimal() {
if (wasNull) {
return null;
}
switch (valueType) {
case JSON_TYPE_INT: {
if (mag1 == 0 && mag2 == 0 && mag3 >= 0) {
return BigDecimal.valueOf(negative ? -mag3 : mag3);
}
int[] mag;
if (mag0 == 0) {
if (mag1 == 0) {
long v3 = mag3 & 0XFFFFFFFFL;
long v2 = mag2 & 0XFFFFFFFFL;
if (v2 <= Integer.MAX_VALUE) {
long v23 = (v2 << 32) + (v3);
return BigDecimal.valueOf(negative ? -v23 : v23);
}
mag = new int[]{mag2, mag3};
} else {
mag = new int[]{mag1, mag2, mag3};
}
} else {
mag = new int[]{mag0, mag1, mag2, mag3};
}
int signum = negative ? -1 : 1;
BigInteger bigInt = BIG_INTEGER_CREATOR.apply(signum, mag);
return new BigDecimal(bigInt);
}
case JSON_TYPE_DEC: {
BigDecimal decimal = null;
if (exponent == 0 && mag0 == 0 && mag1 == 0) {
if (mag2 == 0 && mag3 >= 0) {
int unscaledVal = negative ? -mag3 : mag3;
decimal = BigDecimal.valueOf(unscaledVal, scale);
} else {
long v3 = mag3 & 0XFFFFFFFFL;
long v2 = mag2 & 0XFFFFFFFFL;
if (v2 <= Integer.MAX_VALUE) {
long v23 = (v2 << 32) + (v3);
long unscaledVal = negative ? -v23 : v23;
decimal = BigDecimal.valueOf(unscaledVal, scale);
}
}
}
if (decimal == null) {
int[] mag = mag0 == 0
? mag1 == 0
? mag2 == 0
? new int[]{mag3}
: new int[]{mag2, mag3}
: new int[]{mag1, mag2, mag3}
: new int[]{mag0, mag1, mag2, mag3};
int signum = negative ? -1 : 1;
BigInteger bigInt = BIG_INTEGER_CREATOR.apply(signum, mag);
decimal = new BigDecimal(bigInt, scale);
}
if (exponent != 0) {
String doubleStr = decimal.toPlainString() + "E" + exponent;
double doubleValue = Double.parseDouble(doubleStr);
return toBigDecimal(doubleValue);
}
return decimal;
}
case JSON_TYPE_BIG_DEC: {
return toBigDecimal(stringValue);
}
case JSON_TYPE_BOOL:
return boolValue ? BigDecimal.ONE : BigDecimal.ZERO;
case JSON_TYPE_STRING: {
try {
return toBigDecimal(stringValue);
} catch (NumberFormatException ex) {
throw new JSONException(info("read decimal error, value " + stringValue), ex);
}
}
case JSON_TYPE_OBJECT: {
JSONObject object = (JSONObject) complex;
BigDecimal decimal = object.getBigDecimal("value");
if (decimal == null) {
decimal = object.getBigDecimal("$numberDecimal");
}
if (decimal != null) {
return decimal;
}
throw new JSONException("TODO : " + valueType);
}
default:
throw new JSONException("TODO : " + valueType);
}
}
public final Number getNumber() {
if (wasNull) {
return null;
}
switch (valueType) {
case JSON_TYPE_INT:
case JSON_TYPE_INT64: {
if (mag0 == 0 && mag1 == 0 && mag2 == 0 && mag3 != Integer.MIN_VALUE) {
int intValue;
if (negative) {
if (mag3 < 0) {
long longValue = -(mag3 & 0xFFFFFFFFL);
if ((context.features & Feature.UseBigIntegerForInts.mask) != 0) {
return BigInteger.valueOf(longValue);
}
return longValue;
}
intValue = -mag3;
} else {
if (mag3 < 0) {
long longValue = mag3 & 0xFFFFFFFFL;
if ((context.features & Feature.UseBigIntegerForInts.mask) != 0) {
return BigInteger.valueOf(longValue);
}
return longValue;
}
intValue = mag3;
}
if ((context.features & Feature.UseBigIntegerForInts.mask) != 0) {
return BigInteger.valueOf(intValue);
}
if ((context.features & Feature.UseLongForInts.mask) != 0) {
return (long) intValue;
}
if (valueType == JSON_TYPE_INT64) {
return (long) intValue;
}
return intValue;
}
int[] mag;
if (mag0 == 0) {
if (mag1 == 0) {
long v3 = mag3 & 0XFFFFFFFFL;
long v2 = mag2 & 0XFFFFFFFFL;
if (v2 <= Integer.MAX_VALUE) {
long v23 = (v2 << 32) + (v3);
long longValue = negative ? -v23 : v23;
if ((context.features & Feature.UseBigIntegerForInts.mask) != 0) {
return BigInteger.valueOf(longValue);
}
return longValue;
}
mag = new int[]{mag2, mag3};
} else {
mag = new int[]{mag1, mag2, mag3};
}
} else {
mag = new int[]{mag0, mag1, mag2, mag3};
}
int signum = negative ? -1 : 1;
BigInteger integer = BIG_INTEGER_CREATOR.apply(signum, mag);
if ((context.features & Feature.UseLongForInts.mask) != 0) {
return integer.longValue();
}
return integer;
}
case JSON_TYPE_INT16: {
if (mag0 == 0 && mag1 == 0 && mag2 == 0 && mag3 >= 0) {
int intValue = negative ? -mag3 : mag3;
return (short) intValue;
}
throw new JSONException(info("shortValue overflow"));
}
case JSON_TYPE_INT8: {
if (mag0 == 0 && mag1 == 0 && mag2 == 0 && mag3 >= 0) {
int intValue = negative ? -mag3 : mag3;
return (byte) intValue;
}
throw new JSONException(info("shortValue overflow"));
}
case JSON_TYPE_DEC: {
BigDecimal decimal = null;
if (mag0 == 0 && mag1 == 0) {
if (mag2 == 0 && mag3 >= 0) {
int unscaledVal = negative ? -mag3 : mag3;
decimal = BigDecimal.valueOf(unscaledVal, scale);
} else {
long v3 = mag3 & 0XFFFFFFFFL;
long v2 = mag2 & 0XFFFFFFFFL;
if (v2 <= Integer.MAX_VALUE) {
long v23 = (v2 << 32) + v3;
long unscaledVal = negative ? -v23 : v23;
decimal = BigDecimal.valueOf(unscaledVal, scale);
}
}
}
if (decimal == null) {
int[] mag = mag0 == 0
? mag1 == 0
? new int[]{mag2, mag3}
: new int[]{mag1, mag2, mag3}
: new int[]{mag0, mag1, mag2, mag3};
int signum = negative ? -1 : 1;
BigInteger bigInt = BIG_INTEGER_CREATOR.apply(signum, mag);
int adjustedScale = scale - exponent;
decimal = new BigDecimal(bigInt, adjustedScale);
if (exponent != 0 && (context.features & (Feature.UseBigDecimalForDoubles.mask | Feature.UseBigDecimalForFloats.mask)) == 0) {
return decimal.doubleValue();
}
}
if (exponent != 0) {
String decimalStr = decimal.toPlainString();
if ((context.features & (Feature.UseBigDecimalForDoubles.mask | Feature.UseBigDecimalForFloats.mask)) == 0) {
return Double.parseDouble(
decimalStr + "E" + exponent);
}
return decimal.signum() == 0 ? BigDecimal.ZERO : new BigDecimal(decimalStr + "E" + exponent);
}
if ((context.features & Feature.UseDoubleForDecimals.mask) != 0) {
return decimal.doubleValue();
}
return decimal;
}
case JSON_TYPE_BIG_DEC: {
if (scale > 0) {
if (scale > defaultDecimalMaxScale) {
throw new JSONException("scale overflow : " + scale);
}
return toBigDecimal(stringValue);
} else {
return new BigInteger(stringValue);
}
}
case JSON_TYPE_FLOAT:
case JSON_TYPE_DOUBLE: {
int[] mag = mag0 == 0
? mag1 == 0
? mag2 == 0
? new int[]{mag3}
: new int[]{mag2, mag3}
: new int[]{mag1, mag2, mag3}
: new int[]{mag0, mag1, mag2, mag3};
int signum = negative ? -1 : 1;
BigInteger bigInt = BIG_INTEGER_CREATOR.apply(signum, mag);
BigDecimal decimal = new BigDecimal(bigInt, scale);
if (valueType == JSON_TYPE_FLOAT) {
if (exponent != 0) {
return Float.parseFloat(
decimal + "E" + exponent);
}
return decimal.floatValue();
}
if (exponent != 0) {
return Double.parseDouble(
decimal + "E" + exponent);
}
return decimal.doubleValue();
}
case JSON_TYPE_BOOL:
return boolValue ? 1 : 0;
case JSON_TYPE_NULL:
return null;
case JSON_TYPE_STRING: {
return toInt64(stringValue);
}
case JSON_TYPE_OBJECT: {
return toNumber((Map) complex);
}
case JSON_TYPE_ARRAY: {
return toNumber((List) complex);
}
default:
throw new JSONException("TODO : " + valueType);
}
}
@Override
public abstract void close();
protected final int toInt32(String val) {
if (IOUtils.isNumber(val) || val.lastIndexOf(',') == val.length() - 4) {
return TypeUtils.toIntValue(val);
}
throw error("parseInt error, value : " + val);
}
protected final long toInt64(String val) {
if (IOUtils.isNumber(val)
|| val.lastIndexOf(',') == val.length() - 4) {
return TypeUtils.toLongValue(val);
}
if (val.length() > 10 && val.length() < 40) {
try {
return DateUtils.parseMillis(val, context.zoneId);
} catch (DateTimeException | JSONException | NullPointerException ignored) {
// ignored
}
}
throw error("parseLong error, value : " + val);
}
protected final long toLong(Map map) {
Object val = map.get("val");
if (val instanceof Number) {
return ((Number) val).intValue();
}
throw error("parseLong error, value : " + map);
}
protected final int toInt(List list) {
if (list.size() == 1) {
Object val = list.get(0);
if (val instanceof Number) {
return ((Number) val).intValue();
}
if (val instanceof String) {
return Integer.parseInt((String) val);
}
}
throw error("parseLong error, field : value " + list);
}
protected final Number toNumber(Map map) {
Object val = map.get("val");
if (val instanceof Number) {
return (Number) val;
}
return null;
}
protected final BigDecimal decimal(JSONObject object) {
BigDecimal decimal = object.getBigDecimal("value");
if (decimal == null) {
decimal = object.getBigDecimal("$numberDecimal");
}
if (decimal != null) {
return decimal;
}
throw error("can not cast to decimal " + object);
}
protected final Number toNumber(List list) {
if (list.size() == 1) {
Object val = list.get(0);
if (val instanceof Number) {
return (Number) val;
}
if (val instanceof String) {
return toBigDecimal((String) val);
}
}
return null;
}
protected final String toString(List array) {
JSONWriter writer = JSONWriter.of();
writer.setRootObject(array);
writer.write(array);
return writer.toString();
}
protected final String toString(Map object) {
JSONWriter writer = JSONWriter.of();
writer.setRootObject(object);
writer.write(object);
return writer.toString();
}
public static JSONReader of(byte[] utf8Bytes) {
return of(utf8Bytes, 0, utf8Bytes.length, StandardCharsets.UTF_8, createReadContext());
}
@Deprecated
public static JSONReader of(Context context, byte[] utf8Bytes) {
return JSONReaderUTF8.of(utf8Bytes, 0, utf8Bytes.length, context);
}
public static JSONReader of(byte[] utf8Bytes, Context context) {
return JSONReaderUTF8.of(utf8Bytes, 0, utf8Bytes.length, context);
}
public static JSONReader of(char[] chars) {
return ofUTF16(
null,
chars,
0,
chars.length, createReadContext());
}
@Deprecated
public static JSONReader of(Context context, char[] chars) {
return ofUTF16(null, chars, 0, chars.length, context);
}
public static JSONReader of(char[] chars, Context context) {
return ofUTF16(null, chars, 0, chars.length, context);
}
public static JSONReader ofJSONB(byte[] jsonbBytes) {
return new JSONReaderJSONB(
JSONFactory.createReadContext(),
jsonbBytes,
0,
jsonbBytes.length);
}
@Deprecated
public static JSONReader ofJSONB(Context context, byte[] jsonbBytes) {
return new JSONReaderJSONB(
context,
jsonbBytes,
0,
jsonbBytes.length);
}
public static JSONReader ofJSONB(byte[] jsonbBytes, Context context) {
return new JSONReaderJSONB(
context,
jsonbBytes,
0,
jsonbBytes.length);
}
public static JSONReader ofJSONB(InputStream in, Context context) {
return new JSONReaderJSONB(context, in);
}
public static JSONReader ofJSONB(byte[] jsonbBytes, Feature... features) {
Context context = JSONFactory.createReadContext();
context.config(features);
return new JSONReaderJSONB(
context,
jsonbBytes,
0,
jsonbBytes.length);
}
public static JSONReader ofJSONB(byte[] bytes, int offset, int length) {
return new JSONReaderJSONB(
JSONFactory.createReadContext(),
bytes,
offset,
length);
}
public static JSONReader ofJSONB(byte[] bytes, int offset, int length, Context context) {
return new JSONReaderJSONB(context, bytes, offset, length);
}
public static JSONReader ofJSONB(byte[] bytes, int offset, int length, SymbolTable symbolTable) {
return new JSONReaderJSONB(
JSONFactory.createReadContext(symbolTable),
bytes,
offset,
length);
}
public static JSONReader of(byte[] bytes, int offset, int length, Charset charset) {
Context context = JSONFactory.createReadContext();
if (charset == StandardCharsets.UTF_8) {
return JSONReaderUTF8.of(bytes, offset, length, context);
}
if (charset == StandardCharsets.UTF_16) {
return ofUTF16(bytes, offset, length, context);
}
if (charset == StandardCharsets.US_ASCII || charset == StandardCharsets.ISO_8859_1) {
return JSONReaderASCII.of(context, null, bytes, offset, length);
}
throw new JSONException("not support charset " + charset);
}
private static JSONReader ofUTF16(byte[] bytes, int offset, int length, Context ctx) {
return new JSONReaderUTF16(ctx, bytes, offset, length);
}
private static JSONReader ofUTF16(String str, char[] chars, int offset, int length, Context ctx) {
return new JSONReaderUTF16(ctx, str, chars, offset, length);
}
public static JSONReader of(byte[] bytes, int offset, int length, Charset charset, Context context) {
if (charset == StandardCharsets.UTF_8) {
return JSONReaderUTF8.of(bytes, offset, length, context);
}
if (charset == StandardCharsets.UTF_16) {
return ofUTF16(bytes, offset, length, context);
}
if (charset == StandardCharsets.US_ASCII || charset == StandardCharsets.ISO_8859_1) {
return JSONReaderASCII.of(context, null, bytes, offset, length);
}
throw new JSONException("not support charset " + charset);
}
public static JSONReader of(byte[] bytes, int offset, int length) {
return of(bytes, offset, length, StandardCharsets.UTF_8, createReadContext());
}
public static JSONReader of(byte[] bytes, int offset, int length, Context context) {
return new JSONReaderUTF8(context, bytes, offset, length);
}
public static JSONReader of(char[] chars, int offset, int length) {
return ofUTF16(null, chars, offset, length, createReadContext());
}
public static JSONReader of(char[] chars, int offset, int length, Context context) {
return ofUTF16(null, chars, offset, length, context);
}
public static JSONReader of(URL url, Context context) throws IOException {
try (InputStream is = url.openStream()) {
return of(is, StandardCharsets.UTF_8, context);
}
}
public static JSONReader of(InputStream is, Charset charset) {
Context context = JSONFactory.createReadContext();
return of(is, charset, context);
}
public static JSONReader of(InputStream is, Charset charset, Context context) {
if (is == null) {
throw new JSONException("inputStream is null");
}
if (charset == StandardCharsets.UTF_8 || charset == null) {
return new JSONReaderUTF8(context, is);
}
if (charset == StandardCharsets.UTF_16) {
return new JSONReaderUTF16(context, is);
}
if (charset == StandardCharsets.US_ASCII) {
return JSONReaderASCII.of(context, is);
}
return JSONReader.of(new InputStreamReader(is, charset), context);
}
public static JSONReader of(Reader is) {
return new JSONReaderUTF16(
JSONFactory.createReadContext(),
is
);
}
public static JSONReader of(Reader is, Context context) {
return new JSONReaderUTF16(
context,
is
);
}
public static JSONReader of(ByteBuffer buffer, Charset charset) {
Context context = JSONFactory.createReadContext();
if (charset == StandardCharsets.UTF_8 || charset == null) {
return new JSONReaderUTF8(context, buffer);
}
throw new JSONException("not support charset " + charset);
}
public static JSONReader of(ByteBuffer buffer, Charset charset, Context context) {
if (charset == StandardCharsets.UTF_8 || charset == null) {
return new JSONReaderUTF8(context, buffer);
}
throw new JSONException("not support charset " + charset);
}
@Deprecated
public static JSONReader of(Context context, String str) {
return of(str, context);
}
public static JSONReader of(String str) {
return of(str, JSONFactory.createReadContext());
}
public static JSONReader of(String str, Context context) {
if (str == null || context == null) {
throw new NullPointerException();
}
if (STRING_VALUE != null && STRING_CODER != null) {
try {
final int LATIN1 = 0;
int coder = STRING_CODER.applyAsInt(str);
if (coder == LATIN1) {
byte[] bytes = STRING_VALUE.apply(str);
return JSONReaderASCII.of(context, str, bytes, 0, bytes.length);
}
} catch (Exception e) {
throw new JSONException("unsafe get String.coder error");
}
}
final int length = str.length();
char[] chars;
if (JVM_VERSION == 8) {
chars = JDKUtils.getCharArray(str);
} else {
chars = str.toCharArray();
}
return ofUTF16(str, chars, 0, length, context);
}
public static JSONReader of(String str, int offset, int length) {
return of(str, offset, length, JSONFactory.createReadContext());
}
public static JSONReader of(String str, int offset, int length, Context context) {
if (str == null || context == null) {
throw new NullPointerException();
}
if (STRING_VALUE != null && STRING_CODER != null) {
try {
final int LATIN1 = 0;
int coder = STRING_CODER.applyAsInt(str);
if (coder == LATIN1) {
byte[] bytes = STRING_VALUE.apply(str);
return JSONReaderASCII.of(context, str, bytes, offset, length);
}
} catch (Exception e) {
throw new JSONException("unsafe get String.coder error");
}
}
char[] chars;
if (JVM_VERSION == 8) {
chars = JDKUtils.getCharArray(str);
} else {
chars = str.toCharArray();
}
return ofUTF16(str, chars, offset, length, context);
}
final void bigInt(char[] chars, int off, int len) {
int cursor = off, numDigits;
numDigits = len - cursor;
if (scale > 0) {
numDigits--;
}
if (numDigits > 38) {
throw new JSONException("number too large : " + new String(chars, off, numDigits));
}
// Process first (potentially short) digit group
int firstGroupLen = numDigits % 9;
if (firstGroupLen == 0) {
firstGroupLen = 9;
}
{
int start = cursor;
int end = cursor += firstGroupLen;
char c = chars[start++];
if (c == '.') {
c = chars[start++];
cursor++;
// end++;
}
int result = c - '0';
for (int index = start; index < end; index++) {
c = chars[index];
if (c == '.') {
c = chars[++index];
cursor++;
if (end < len) {
end++;
}
}
int nextVal = c - '0';
result = 10 * result + nextVal;
}
mag3 = result;
}
// Process remaining digit groups
while (cursor < len) {
int groupVal;
{
int start = cursor;
int end = cursor += 9;
char c = chars[start++];
if (c == '.') {
c = chars[start++];
cursor++;
end++;
}
int result = c - '0';
for (int index = start; index < end; index++) {
c = chars[index];
if (c == '.') {
c = chars[++index];
cursor++;
end++;
}
int nextVal = c - '0';
result = 10 * result + nextVal;
}
groupVal = result;
}
// destructiveMulAdd
long ylong = 1000000000 & 0XFFFFFFFFL;
long product;
long carry = 0;
for (int i = 3; i >= 0; i--) {
switch (i) {
case 0:
product = ylong * (mag0 & 0XFFFFFFFFL) + carry;
mag0 = (int) product;
break;
case 1:
product = ylong * (mag1 & 0XFFFFFFFFL) + carry;
mag1 = (int) product;
break;
case 2:
product = ylong * (mag2 & 0XFFFFFFFFL) + carry;
mag2 = (int) product;
break;
case 3:
product = ylong * (mag3 & 0XFFFFFFFFL) + carry;
mag3 = (int) product;
break;
default:
throw new ArithmeticException("BigInteger would overflow supported range");
}
carry = product >>> 32;
}
long zlong = groupVal & 0XFFFFFFFFL;
long sum = (mag3 & 0XFFFFFFFFL) + zlong;
mag3 = (int) sum;
// Perform the addition
carry = sum >>> 32;
for (int i = 2; i >= 0; i--) {
switch (i) {
case 0:
sum = (mag0 & 0XFFFFFFFFL) + carry;
mag0 = (int) sum;
break;
case 1:
sum = (mag1 & 0XFFFFFFFFL) + carry;
mag1 = (int) sum;
break;
case 2:
sum = (mag2 & 0XFFFFFFFFL) + carry;
mag2 = (int) sum;
break;
case 3:
sum = (mag3 & 0XFFFFFFFFL) + carry;
mag3 = (int) sum;
break;
default:
throw new ArithmeticException("BigInteger would overflow supported range");
}
carry = sum >>> 32;
}
}
}
final void bigInt(byte[] chars, int off, int len) {
int cursor = off, numDigits;
numDigits = len - cursor;
if (scale > 0) {
numDigits--;
}
if (numDigits > 38) {
throw new JSONException("number too large : " + new String(chars, off, numDigits));
}
// Process first (potentially short) digit group
int firstGroupLen = numDigits % 9;
if (firstGroupLen == 0) {
firstGroupLen = 9;
}
{
int start = cursor;
int end = cursor += firstGroupLen;
char c = (char) chars[start++];
if (c == '.') {
c = (char) chars[start++];
cursor++;
// end++;
}
int result = c - '0';
for (int index = start; index < end; index++) {
c = (char) chars[index];
if (c == '.') {
c = (char) chars[++index];
cursor++;
if (end < len) {
end++;
}
}
int nextVal = c - '0';
result = 10 * result + nextVal;
}
mag3 = result;
}
// Process remaining digit groups
while (cursor < len) {
int groupVal;
{
int start = cursor;
int end = cursor += 9;
char c = (char) chars[start++];
if (c == '.') {
c = (char) chars[start++];
cursor++;
end++;
}
int result = c - '0';
for (int index = start; index < end; index++) {
c = (char) chars[index];
if (c == '.') {
c = (char) chars[++index];
cursor++;
end++;
}
int nextVal = c - '0';
result = 10 * result + nextVal;
}
groupVal = result;
}
// destructiveMulAdd
long ylong = 1000000000 & 0XFFFFFFFFL;
long zlong = groupVal & 0XFFFFFFFFL;
long product;
long carry = 0;
for (int i = 3; i >= 0; i--) {
switch (i) {
case 0:
product = ylong * (mag0 & 0XFFFFFFFFL) + carry;
mag0 = (int) product;
break;
case 1:
product = ylong * (mag1 & 0XFFFFFFFFL) + carry;
mag1 = (int) product;
break;
case 2:
product = ylong * (mag2 & 0XFFFFFFFFL) + carry;
mag2 = (int) product;
break;
case 3:
product = ylong * (mag3 & 0XFFFFFFFFL) + carry;
mag3 = (int) product;
break;
default:
throw new ArithmeticException("BigInteger would overflow supported range");
}
carry = product >>> 32;
}
long sum = (mag3 & 0XFFFFFFFFL) + zlong;
mag3 = (int) sum;
// Perform the addition
carry = sum >>> 32;
for (int i = 2; i >= 0; i--) {
switch (i) {
case 0:
sum = (mag0 & 0XFFFFFFFFL) + carry;
mag0 = (int) sum;
break;
case 1:
sum = (mag1 & 0XFFFFFFFFL) + carry;
mag1 = (int) sum;
break;
case 2:
sum = (mag2 & 0XFFFFFFFFL) + carry;
mag2 = (int) sum;
break;
case 3:
sum = (mag3 & 0XFFFFFFFFL) + carry;
mag3 = (int) sum;
break;
default:
throw new ArithmeticException("BigInteger would overflow supported range");
}
carry = sum >>> 32;
}
}
}
public interface AutoTypeBeforeHandler
extends Filter {
default Class<?> apply(long typeNameHash, Class<?> expectClass, long features) {
return null;
}
Class<?> apply(String typeName, Class<?> expectClass, long features);
}
public static AutoTypeBeforeHandler autoTypeFilter(String... names) {
return new ContextAutoTypeBeforeHandler(names);
}
public static AutoTypeBeforeHandler autoTypeFilter(boolean includeBasic, String... names) {
return new ContextAutoTypeBeforeHandler(includeBasic, names);
}
public static AutoTypeBeforeHandler autoTypeFilter(Class... types) {
return new ContextAutoTypeBeforeHandler(types);
}
public static AutoTypeBeforeHandler autoTypeFilter(boolean includeBasic, Class... types) {
return new ContextAutoTypeBeforeHandler(includeBasic, types);
}
public static final class Context {
String dateFormat;
boolean formatComplex;
boolean formatyyyyMMddhhmmss19;
boolean formatyyyyMMddhhmmssT19;
boolean yyyyMMddhhmm16;
boolean formatyyyyMMdd8;
boolean formatMillis;
boolean formatUnixTime;
boolean formatISO8601;
boolean formatHasDay;
boolean formatHasHour;
boolean useSimpleFormatter;
int maxLevel = 2048;
int bufferSize = 1024 * 512;
DateTimeFormatter dateFormatter;
ZoneId zoneId;
long features;
Locale locale;
TimeZone timeZone;
Supplier<Map> objectSupplier;
Supplier<List> arraySupplier;
AutoTypeBeforeHandler autoTypeBeforeHandler;
ExtraProcessor extraProcessor;
final ObjectReaderProvider provider;
final SymbolTable symbolTable;
public Context(ObjectReaderProvider provider) {
this.features = defaultReaderFeatures;
this.provider = provider;
this.objectSupplier = JSONFactory.defaultObjectSupplier;
this.arraySupplier = JSONFactory.defaultArraySupplier;
this.symbolTable = null;
this.zoneId = defaultReaderZoneId;
String format = defaultReaderFormat;
if (format != null) {
setDateFormat(format);
}
}
public Context(ObjectReaderProvider provider, long features) {
this.features = features;
this.provider = provider;
this.objectSupplier = JSONFactory.defaultObjectSupplier;
this.arraySupplier = JSONFactory.defaultArraySupplier;
this.symbolTable = null;
this.zoneId = defaultReaderZoneId;
String format = defaultReaderFormat;
if (format != null) {
setDateFormat(format);
}
}
public Context(Feature... features) {
this.features = defaultReaderFeatures;
this.provider = JSONFactory.getDefaultObjectReaderProvider();
this.objectSupplier = JSONFactory.defaultObjectSupplier;
this.arraySupplier = JSONFactory.defaultArraySupplier;
this.symbolTable = null;
this.zoneId = defaultReaderZoneId;
String format = defaultReaderFormat;
if (format != null) {
setDateFormat(format);
}
for (Feature feature : features) {
this.features |= feature.mask;
}
}
public Context(String dateFormat, Feature... features) {
this.features = defaultReaderFeatures;
this.provider = JSONFactory.getDefaultObjectReaderProvider();
this.objectSupplier = JSONFactory.defaultObjectSupplier;
this.arraySupplier = JSONFactory.defaultArraySupplier;
this.symbolTable = null;
this.zoneId = defaultReaderZoneId;
String format = defaultReaderFormat;
if (format != null) {
setDateFormat(format);
}
for (Feature feature : features) {
this.features |= feature.mask;
}
setDateFormat(dateFormat);
}
public Context(ObjectReaderProvider provider, Feature... features) {
this.features = defaultReaderFeatures;
this.provider = provider;
this.objectSupplier = JSONFactory.defaultObjectSupplier;
this.arraySupplier = JSONFactory.defaultArraySupplier;
this.symbolTable = null;
this.zoneId = defaultReaderZoneId;
String format = defaultReaderFormat;
if (format != null) {
setDateFormat(format);
}
for (Feature feature : features) {
this.features |= feature.mask;
}
}
public Context(ObjectReaderProvider provider, Filter filter, Feature... features) {
this.features = defaultReaderFeatures;
this.provider = provider;
this.objectSupplier = JSONFactory.defaultObjectSupplier;
this.arraySupplier = JSONFactory.defaultArraySupplier;
this.symbolTable = null;
this.zoneId = defaultReaderZoneId;
config(filter);
String format = defaultReaderFormat;
if (format != null) {
setDateFormat(format);
}
for (Feature feature : features) {
this.features |= feature.mask;
}
}
public Context(ObjectReaderProvider provider, SymbolTable symbolTable) {
this.features = defaultReaderFeatures;
this.provider = provider;
this.symbolTable = symbolTable;
this.zoneId = defaultReaderZoneId;
String format = defaultReaderFormat;
if (format != null) {
setDateFormat(format);
}
}
public Context(ObjectReaderProvider provider, SymbolTable symbolTable, Feature... features) {
this.features = defaultReaderFeatures;
this.provider = provider;
this.symbolTable = symbolTable;
this.zoneId = defaultReaderZoneId;
String format = defaultReaderFormat;
if (format != null) {
setDateFormat(format);
}
for (Feature feature : features) {
this.features |= feature.mask;
}
}
public Context(ObjectReaderProvider provider, SymbolTable symbolTable, Filter[] filters, Feature... features) {
this.features = defaultReaderFeatures;
this.provider = provider;
this.symbolTable = symbolTable;
this.zoneId = defaultReaderZoneId;
config(filters);
String format = defaultReaderFormat;
if (format != null) {
setDateFormat(format);
}
for (Feature feature : features) {
this.features |= feature.mask;
}
}
public boolean isFormatUnixTime() {
return formatUnixTime;
}
public boolean isFormatyyyyMMddhhmmss19() {
return formatyyyyMMddhhmmss19;
}
public boolean isFormatyyyyMMddhhmmssT19() {
return formatyyyyMMddhhmmssT19;
}
public boolean isFormatyyyyMMdd8() {
return formatyyyyMMdd8;
}
public boolean isFormatMillis() {
return formatMillis;
}
public boolean isFormatISO8601() {
return formatISO8601;
}
public boolean isFormatHasHour() {
return formatHasHour;
}
public ObjectReader getObjectReader(Type type) {
boolean fieldBased = (features & Feature.FieldBased.mask) != 0;
return provider.getObjectReader(type, fieldBased);
}
public ObjectReaderProvider getProvider() {
return provider;
}
public ObjectReader getObjectReaderAutoType(long hashCode) {
return provider.getObjectReader(hashCode);
}
public ObjectReader getObjectReaderAutoType(String typeName, Class expectClass) {
if (autoTypeBeforeHandler != null) {
Class<?> autoTypeClass = autoTypeBeforeHandler.apply(typeName, expectClass, features);
if (autoTypeClass != null) {
boolean fieldBased = (features & Feature.FieldBased.mask) != 0;
return provider.getObjectReader(autoTypeClass, fieldBased);
}
}
return provider.getObjectReader(typeName, expectClass, features);
}
public AutoTypeBeforeHandler getContextAutoTypeBeforeHandler() {
return autoTypeBeforeHandler;
}
public ObjectReader getObjectReaderAutoType(String typeName, Class expectClass, long features) {
if (autoTypeBeforeHandler != null) {
Class<?> autoTypeClass = autoTypeBeforeHandler.apply(typeName, expectClass, features);
if (autoTypeClass != null) {
boolean fieldBased = (features & Feature.FieldBased.mask) != 0;
return provider.getObjectReader(autoTypeClass, fieldBased);
}
}
return provider.getObjectReader(typeName, expectClass, this.features | features);
}
public ExtraProcessor getExtraProcessor() {
return extraProcessor;
}
public void setExtraProcessor(ExtraProcessor extraProcessor) {
this.extraProcessor = extraProcessor;
}
public Supplier<Map> getObjectSupplier() {
return objectSupplier;
}
public void setObjectSupplier(Supplier<Map> objectSupplier) {
this.objectSupplier = objectSupplier;
}
public Supplier<List> getArraySupplier() {
return arraySupplier;
}
public void setArraySupplier(Supplier<List> arraySupplier) {
this.arraySupplier = arraySupplier;
}
public DateTimeFormatter getDateFormatter() {
if (dateFormatter == null && dateFormat != null && !formatMillis && !formatISO8601 && !formatUnixTime) {
dateFormatter = locale == null
? DateTimeFormatter.ofPattern(dateFormat)
: DateTimeFormatter.ofPattern(dateFormat, locale);
}
return dateFormatter;
}
public void setDateFormatter(DateTimeFormatter dateFormatter) {
this.dateFormatter = dateFormatter;
}
public String getDateFormat() {
return dateFormat;
}
public void setDateFormat(String format) {
if (format != null) {
if (format.isEmpty()) {
format = null;
}
}
boolean formatUnixTime = false, formatISO8601 = false, formatMillis = false, hasDay = false, hasHour = false, useSimpleFormatter = false;
if (format != null) {
switch (format) {
case "unixtime":
formatUnixTime = true;
break;
case "iso8601":
formatISO8601 = true;
break;
case "millis":
formatMillis = true;
break;
case "yyyyMMddHHmmssSSSZ":
useSimpleFormatter = true;
case "yyyy-MM-dd HH:mm:ss":
case "yyyy-MM-ddTHH:mm:ss":
formatyyyyMMddhhmmss19 = true;
hasDay = true;
hasHour = true;
break;
case "yyyy-MM-dd'T'HH:mm:ss":
formatyyyyMMddhhmmssT19 = true;
hasDay = true;
hasHour = true;
break;
case "yyyyMMdd":
case "yyyy-MM-dd":
formatyyyyMMdd8 = true;
hasDay = true;
hasHour = false;
break;
case "yyyy-MM-dd HH:mm":
yyyyMMddhhmm16 = true;
break;
default:
hasDay = format.indexOf('d') != -1;
hasHour = format.indexOf('H') != -1
|| format.indexOf('h') != -1
|| format.indexOf('K') != -1
|| format.indexOf('k') != -1;
break;
}
this.formatComplex = !(formatyyyyMMddhhmmss19 | formatyyyyMMddhhmmssT19 | formatyyyyMMdd8 | formatISO8601);
// this.yyyyMMddhhmm16 = "yyyy-MM-dd HH:mm".equals(format);
}
if (!Objects.equals(this.dateFormat, format)) {
this.dateFormatter = null;
}
this.dateFormat = format;
this.formatUnixTime = formatUnixTime;
this.formatMillis = formatMillis;
this.formatISO8601 = formatISO8601;
this.formatHasDay = hasDay;
this.formatHasHour = hasHour;
this.useSimpleFormatter = useSimpleFormatter;
}
public ZoneId getZoneId() {
if (zoneId == null) {
zoneId = DateUtils.DEFAULT_ZONE_ID;
}
return zoneId;
}
public long getFeatures() {
return features;
}
/**
* @since 2.0.51
*/
public void setFeatures(long features) {
this.features = features;
}
public void setZoneId(ZoneId zoneId) {
this.zoneId = zoneId;
}
public int getMaxLevel() {
return maxLevel;
}
public void setMaxLevel(int maxLevel) {
this.maxLevel = maxLevel;
}
public int getBufferSize() {
return bufferSize;
}
public Context setBufferSize(int bufferSize) {
if (bufferSize < 0) {
throw new IllegalArgumentException("buffer size can not be less than zero");
}
this.bufferSize = bufferSize;
return this;
}
public Locale getLocale() {
return locale;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
public TimeZone getTimeZone() {
return timeZone;
}
public void setTimeZone(TimeZone timeZone) {
this.timeZone = timeZone;
}
public void config(Feature... features) {
for (int i = 0; i < features.length; i++) {
this.features |= features[i].mask;
}
}
public void config(Filter filter, Feature... features) {
if (filter instanceof AutoTypeBeforeHandler) {
autoTypeBeforeHandler = (AutoTypeBeforeHandler) filter;
}
if (filter instanceof ExtraProcessor) {
extraProcessor = (ExtraProcessor) filter;
}
for (Feature feature : features) {
this.features |= feature.mask;
}
}
public void config(Filter filter) {
if (filter instanceof AutoTypeBeforeHandler) {
autoTypeBeforeHandler = (AutoTypeBeforeHandler) filter;
}
if (filter instanceof ExtraProcessor) {
extraProcessor = (ExtraProcessor) filter;
}
}
public void config(Filter[] filters, Feature... features) {
for (Filter filter : filters) {
if (filter instanceof AutoTypeBeforeHandler) {
autoTypeBeforeHandler = (AutoTypeBeforeHandler) filter;
}
if (filter instanceof ExtraProcessor) {
extraProcessor = (ExtraProcessor) filter;
}
}
for (Feature feature : features) {
this.features |= feature.mask;
}
}
public void config(Filter[] filters) {
for (Filter filter : filters) {
if (filter instanceof AutoTypeBeforeHandler) {
autoTypeBeforeHandler = (AutoTypeBeforeHandler) filter;
}
if (filter instanceof ExtraProcessor) {
extraProcessor = (ExtraProcessor) filter;
}
}
}
public boolean isEnabled(Feature feature) {
return (this.features & feature.mask) != 0;
}
public void config(Feature feature, boolean state) {
if (state) {
features |= feature.mask;
} else {
features &= ~feature.mask;
}
}
}
protected static final long MASK_FIELD_BASED = 1L;
protected static final long MASK_IGNORE_NONE_SERIALIZABLE = 1L << 1;
protected static final long MASK_ERROR_ON_NONE_SERIALIZABLE = 1L << 2;
protected static final long MASK_SUPPORT_ARRAY_TO_BEAN = 1L << 3;
protected static final long MASK_INIT_STRING_FIELD_AS_EMPTY = 1L << 4;
protected static final long MASK_SUPPORT_AUTO_TYPE = 1L << 5;
protected static final long MASK_SUPPORT_SMART_MATCH = 1L << 6;
protected static final long MASK_TRIM_STRING = 1L << 14;
protected static final long MASK_ALLOW_UN_QUOTED_FIELD_NAMES = 1L << 17;
protected static final long MASK_EMPTY_STRING_AS_NULL = 1L << 27;
protected static final long MASK_DISABLE_SINGLE_QUOTE = 1L << 31L;
protected static final long MASK_DISABLE_REFERENCE_DETECT = 1L << 33;
public enum Feature {
FieldBased(MASK_FIELD_BASED),
IgnoreNoneSerializable(MASK_IGNORE_NONE_SERIALIZABLE),
/**
* @since 2.0.14
*/
ErrorOnNoneSerializable(MASK_ERROR_ON_NONE_SERIALIZABLE),
SupportArrayToBean(MASK_SUPPORT_ARRAY_TO_BEAN),
InitStringFieldAsEmpty(MASK_INIT_STRING_FIELD_AS_EMPTY),
/**
* It is not safe to explicitly turn on autoType, it is recommended to use AutoTypeBeforeHandler
*/
@Deprecated
SupportAutoType(MASK_SUPPORT_AUTO_TYPE),
SupportSmartMatch(MASK_SUPPORT_SMART_MATCH),
UseNativeObject(1 << 7),
SupportClassForName(1 << 8),
IgnoreSetNullValue(1 << 9),
UseDefaultConstructorAsPossible(1 << 10),
UseBigDecimalForFloats(1 << 11),
UseBigDecimalForDoubles(1 << 12),
ErrorOnEnumNotMatch(1 << 13),
TrimString(MASK_TRIM_STRING),
ErrorOnNotSupportAutoType(1 << 15),
DuplicateKeyValueAsArray(1 << 16),
AllowUnQuotedFieldNames(MASK_ALLOW_UN_QUOTED_FIELD_NAMES),
NonStringKeyAsString(1 << 18),
/**
* @since 2.0.13
*/
Base64StringAsByteArray(1 << 19),
/**
* @since 2.0.16
*/
IgnoreCheckClose(1 << 20),
/**
* @since 2.0.20
*/
ErrorOnNullForPrimitives(1 << 21),
/**
* @since 2.0.20
*/
NullOnError(1 << 22),
/**
* @since 2.0.21
*/
IgnoreAutoTypeNotMatch(1 << 23),
/**
* @since 2.0.24
*/
NonZeroNumberCastToBooleanAsTrue(1 << 24),
/**
* @since 2.0.40
*/
IgnoreNullPropertyValue(1 << 25),
/**
* @since 2.0.42
*/
ErrorOnUnknownProperties(1 << 26),
/**
* empty string "" convert to null
*
* @since 2.0.48
*/
EmptyStringAsNull(MASK_EMPTY_STRING_AS_NULL),
/**
* @since 2.0.48
*/
NonErrorOnNumberOverflow(1 << 28),
/**
* Feature that determines whether JSON integral (non-floating-point)
* numbers are to be deserialized into {@link java.math.BigInteger}s
* if only generic type description (either {@link Object} or
* {@link Number}, or within untyped {@link java.util.Map}
* or {@link java.util.Collection} context) is available.
* If enabled such values will be deserialized as
* {@link java.math.BigInteger}s;
* if disabled, will be deserialized as "smallest" available type,
* which is either {@link Integer}, {@link Long} or
* {@link java.math.BigInteger}, depending on number of digits.
* <p>
* Feature is disabled by default, meaning that "untyped" integral
* numbers will by default be deserialized using whatever
* is the most compact integral type, to optimize efficiency.
* @since 2.0.51
*/
UseBigIntegerForInts(1 << 29),
/**
* Feature that determines how "small" JSON integral (non-floating-point)
* numbers -- ones that fit in 32-bit signed integer (`int`) -- are bound
* when target type is loosely typed as {@link Object} or {@link Number}
* (or within untyped {@link java.util.Map} or {@link java.util.Collection} context).
* If enabled, such values will be deserialized as {@link java.lang.Long};
* if disabled, they will be deserialized as "smallest" available type,
* {@link Integer}.
*<p>
* Note: if {@link #UseBigIntegerForInts} is enabled, it has precedence
* over this setting, forcing use of {@link java.math.BigInteger} for all
* integral values.
*<p>
* Feature is disabled by default, meaning that "untyped" integral
* numbers will by default be deserialized using {@link java.lang.Integer}
* if value fits.
*
* @since 2.0.51
*/
UseLongForInts(1 << 30),
/**
* Feature that disables the support for single quote.
* @since 2.0.53
*/
DisableSingleQuote(MASK_DISABLE_SINGLE_QUOTE),
/**
* @since 2.0.53
*/
UseDoubleForDecimals(1L << 32L),
/**
* @since 2.0.56
*/
DisableReferenceDetect(MASK_DISABLE_REFERENCE_DETECT);
public final long mask;
Feature(long mask) {
this.mask = mask;
}
public static long of(Feature[] features) {
if (features == null) {
return 0;
}
long value = 0;
for (Feature feature : features) {
value |= feature.mask;
}
return value;
}
public boolean isEnabled(long features) {
return (features & mask) != 0;
}
public static boolean isEnabled(long features, Feature feature) {
return (features & feature.mask) != 0;
}
}
static final class ResolveTask {
final FieldReader fieldReader;
final Object object;
final Object name;
final JSONPath reference;
ResolveTask(FieldReader fieldReader, Object object, Object name, JSONPath reference) {
this.fieldReader = fieldReader;
this.object = object;
this.name = name;
this.reference = reference;
}
@Override
public String toString() {
return reference.toString();
}
}
public SavePoint mark() {
return new SavePoint(this.offset, this.ch);
}
public void reset(SavePoint savePoint) {
this.offset = savePoint.offset;
this.ch = (char) savePoint.current;
}
final boolean checkNameBegin(int quote) {
long features = context.features;
if (quote == '\'' && ((features & MASK_DISABLE_SINGLE_QUOTE) != 0)) {
throw notSupportName();
}
if (quote != '"' && quote != '\'') {
if ((features & MASK_ALLOW_UN_QUOTED_FIELD_NAMES) != 0) {
readFieldNameHashCodeUnquote();
return true;
}
throw notSupportName();
}
return false;
}
final JSONException notSupportName() {
return new JSONException(info("not support unquoted name"));
}
final JSONException valueError() {
return new JSONException(info("illegal value"));
}
final JSONException error(String message) {
return new JSONException(info(message));
}
final JSONException error(String message, Exception cause) {
return new JSONException(info(message), cause);
}
final JSONException error() {
throw new JSONValidException("error, offset " + offset + ", char " + (char) ch);
}
final JSONException error(int offset, int ch) {
throw new JSONValidException("error, offset " + offset + ", char " + (char) ch);
}
static JSONException syntaxError(int ch) {
return new JSONException("syntax error, expect ',', but '" + (char) ch + "'");
}
static JSONException syntaxError(int offset, int ch) {
return new JSONException("syntax error, offset " + offset + ", char " + (char) ch);
}
static JSONException numberError(int offset, int ch) {
return new JSONException("illegal number, offset " + offset + ", char " + (char) ch);
}
JSONException numberError() {
return new JSONException("illegal number, offset " + offset + ", char " + ch);
}
public final String info() {
return info(null);
}
public String info(String message) {
if (message == null || message.isEmpty()) {
return "offset " + offset;
}
return message + ", offset " + offset;
}
static boolean isFirstIdentifier(int ch) {
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_' || ch == '$' || (ch >= '0' && ch <= '9') || ch > 0x7F;
}
public static class SavePoint {
protected final int offset;
protected final int current;
protected SavePoint(int offset, int current) {
this.offset = offset;
this.current = current;
}
}
static final class BigIntegerCreator
implements BiFunction<Integer, int[], BigInteger> {
static final BiFunction<Integer, int[], BigInteger> BIG_INTEGER_CREATOR;
static {
BiFunction<Integer, int[], BigInteger> bigIntegerCreator = null;
if (!ANDROID && !GRAAL) {
try {
MethodHandles.Lookup caller = JDKUtils.trustedLookup(BigInteger.class);
MethodHandle handle = caller.findConstructor(
BigInteger.class, MethodType.methodType(void.class, int.class, int[].class)
);
CallSite callSite = LambdaMetafactory.metafactory(
caller,
"apply",
MethodType.methodType(BiFunction.class),
handle.type().generic(),
handle,
MethodType.methodType(BigInteger.class, Integer.class, int[].class)
);
bigIntegerCreator = (BiFunction<Integer, int[], BigInteger>) callSite.getTarget().invokeExact();
} catch (Throwable ignored) {
// ignored
}
}
if (bigIntegerCreator == null) {
bigIntegerCreator = new BigIntegerCreator();
}
BIG_INTEGER_CREATOR = bigIntegerCreator;
}
@Override
public BigInteger apply(Integer integer, int[] mag) {
int signum = integer;
final int bitLength;
if (mag.length == 0) {
bitLength = 0; // offset by one to initialize
} else {
// Calculate the bit length of the magnitude
int bitLengthForInt = 32 - Integer.numberOfLeadingZeros(mag[0]);
int magBitLength = ((mag.length - 1) << 5) + bitLengthForInt;
if (signum < 0) {
// Check if magnitude is a power of two
boolean pow2 = (Integer.bitCount(mag[0]) == 1);
for (int i = 1; i < mag.length && pow2; i++) {
pow2 = (mag[i] == 0);
}
bitLength = (pow2 ? magBitLength - 1 : magBitLength);
} else {
bitLength = magBitLength;
}
}
int byteLen = bitLength / 8 + 1;
byte[] bytes = new byte[byteLen];
for (int i = byteLen - 1, bytesCopied = 4, nextInt = 0, intIndex = 0; i >= 0; i--) {
if (bytesCopied == 4) {
// nextInt = getInt(intIndex++
int n = intIndex++;
if (n < 0) {
nextInt = 0;
} else if (n >= mag.length) {
nextInt = signum < 0 ? -1 : 0;
} else {
int magInt = mag[mag.length - n - 1];
if (signum >= 0) {
nextInt = magInt;
} else {
int firstNonzeroIntNum;
{
int j;
int mlen = mag.length;
for (j = mlen - 1; j >= 0 && mag[j] == 0; j--) {
// empty
}
firstNonzeroIntNum = mlen - j - 1;
}
if (n <= firstNonzeroIntNum) {
nextInt = -magInt;
} else {
nextInt = ~magInt;
}
}
}
bytesCopied = 1;
} else {
nextInt >>>= 8;
bytesCopied++;
}
bytes[i] = (byte) nextInt;
}
return new BigInteger(bytes);
}
}
public ObjectReader getObjectReaderAutoType(long typeHash, Class expectClass, long features) {
ObjectReader autoTypeObjectReader = context.getObjectReaderAutoType(typeHash);
if (autoTypeObjectReader != null) {
return autoTypeObjectReader;
}
String typeName = getString();
if (context.autoTypeBeforeHandler != null) {
Class<?> autoTypeClass = context.autoTypeBeforeHandler.apply(typeName, expectClass, features);
if (autoTypeClass != null) {
boolean fieldBased = (features & Feature.FieldBased.mask) != 0;
return context.provider.getObjectReader(autoTypeClass, fieldBased);
}
}
return context.provider.getObjectReader(typeName, expectClass, context.features | features);
}
protected final String readStringNotMatch() {
switch (ch) {
case '[':
List array = readArray();
if (array.size() == 1) {
Object item = array.get(0);
if (item == null) {
return null;
}
if (item instanceof String) {
return item.toString();
}
}
return toString(array);
case '{':
return toString(
readObject());
case '-':
case '+':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
readNumber0();
Number number = getNumber();
return number.toString();
case 't':
case 'f':
boolValue = readBoolValue();
return boolValue ? "true" : "false";
case 'n': {
readNull();
return null;
}
default:
throw new JSONException(info("illegal input : " + ch));
}
}
protected static String stringValue(String str, long features) {
if ((features & MASK_TRIM_STRING) != 0) {
str = str.trim();
}
if ((features & MASK_EMPTY_STRING_AS_NULL) != 0 && str.isEmpty()) {
return null;
}
return str;
}
}