package com.owlike.genson.stream;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import static com.
owlike.
genson.
stream.
ValueType.
*;
public class JsonReader
implements ObjectReader
{
protected final static int[] SKIPPED_TOKENS
;
static {
SKIPPED_TOKENS =
new int[128
];
SKIPPED_TOKENS
['\t'] =
1;
SKIPPED_TOKENS
['\b'] =
1;
SKIPPED_TOKENS
['\n'] =
1;
SKIPPED_TOKENS
['\r'] =
1;
SKIPPED_TOKENS
['\f'] =
1;
SKIPPED_TOKENS
[' '] =
1;
}
private final static boolean[] _NEXT_TOKEN =
new boolean[128
];
static {
_NEXT_TOKEN
[','] =
true;
_NEXT_TOKEN
['"'] =
true;
_NEXT_TOKEN
['{'] =
true;
_NEXT_TOKEN
['['] =
true;
_NEXT_TOKEN
['n'] =
true;
_NEXT_TOKEN
['N'] =
true;
_NEXT_TOKEN
['-'] =
true;
_NEXT_TOKEN
['t'] =
true;
_NEXT_TOKEN
['f'] =
true;
_NEXT_TOKEN
['T'] =
true;
_NEXT_TOKEN
['F'] =
true;
for (int i =
48; i
< 58; i++
)
_NEXT_TOKEN
[i
] =
true;
}
private final static char[] _END_OF_LINE =
new char[]{'\n'};
private final static char[] _END_OF_BLOCK_COMMENT =
new char[]{'*',
'/'};
/*
* Recupere dans Jackson
*/
private final static int[] sHexValues =
new int[128
];
static {
1.5.0/docs/api/java/util/Arrays.html">Arrays.
fill(sHexValues, -1
);
for (int i =
0; i
< 10; ++i
) {
sHexValues
['0' + i
] = i
;
}
for (int i =
0; i
< 6; ++i
) {
sHexValues
['a' + i
] = 10 + i
;
sHexValues
['A' + i
] = 10 + i
;
}
}
private final static double[] _POWS =
new double[309
];
static {
for (int i =
0; i
< _POWS.
length; i++
)
_POWS
[i
] =
1.5.0/docs/api/java/lang/Math.html">Math.
pow(10, i
);
}
private final 1.5.0/docs/api/java/io/Reader.html">Reader reader
;
private final boolean strictDoubleParse
;
private final boolean readMetadata
;
private final char[] _buffer =
new char[2048
];
private int _col
;
private int _row
;
private int _cursor
;
private int _buflen
;
private char[] _stringBuffer =
new char[16
];
private int _stringBufferTail =
0;
private int _stringBufferLength = _stringBuffer.
length;
private 1.5.0/docs/api/java/lang/String.html">String currentName
;
private 1.5.0/docs/api/java/lang/String.html">String _stringValue
;
protected long _intValue
;
protected double _doubleValue
;
private int _numberLen =
0;
private 1.5.0/docs/api/java/lang/Boolean.html">Boolean _booleanValue
;
private ValueType valueType
;
private boolean _first =
true;
private boolean _metadata_readen =
false;
private Map
<String, String
> _metadata =
new HashMap
<String, String
>(5
);
private final Deque
<JsonType
> _ctx =
new ArrayDeque
<JsonType
>(10
);
{
_ctx.
push(JsonType.
EMPTY);
}
public JsonReader
(1.5.0/docs/api/java/lang/String.html">String source
) {
this(new 1.5.0/docs/api/java/io/StringReader.html">StringReader(source
),
false,
false);
}
public JsonReader
(1.5.0/docs/api/java/io/Reader.html">Reader reader,
boolean strictDoubleParse,
boolean readMetadata
) {
this.
reader = reader
;
this.
strictDoubleParse = strictDoubleParse
;
this.
readMetadata = readMetadata
;
char token =
(char) readNextToken
(false);
if ('[' == token
) valueType = ARRAY
;
else if ('{' == token
) valueType = OBJECT
;
else {
// ok lets try to read next
if (_buflen
> 0
) {
try {
valueType = consumeValue
();
} catch (JsonStreamException jse
) {
try {
// we must cheat because consumeString attends the current token to be "
// and will increment the cursor
_cursor = -
1;
_col = -
1;
_stringValue = consumeString
('"');
valueType = STRING
;
} catch (1.5.0/docs/api/java/lang/RuntimeException.html">RuntimeException re
) {
throw re
;
}
}
if (valueOf
(valueType.
name()) ==
null)
throw new JsonStreamException
(
"Failed to instanciate reader, first character was " +
(char) token
+
" when possible characters are [ and {");
} else valueType = NULL
;
}
}
public void close
() {
try {
reader.
close();
} catch (1.5.0/docs/api/java/io/IOException.html">IOException e
) {
throw new JsonStreamException
(e
);
}
}
public ObjectReader beginArray
() {
begin
('[', JsonType.
ARRAY);
valueType = ARRAY
;
if (_metadata_readen
) _metadata.
clear();
return this;
}
public ObjectReader beginObject
() {
if (!_metadata_readen
) {
begin
('{', JsonType.
OBJECT);
valueType = OBJECT
;
if (readMetadata
) {
_metadata.
clear();
readMetadata
();
}
}
return this;
}
public ObjectReader nextObjectMetadata
() {
return beginObject
();
}
public ObjectReader endArray
() {
end
(']', JsonType.
ARRAY);
return this;
}
public ObjectReader endObject
() {
end
('}', JsonType.
OBJECT);
_metadata.
clear();
_metadata_readen =
false;
return this;
}
public 1.5.0/docs/api/java/lang/String.html">String name
() {
if (enclosingType
() != JsonType.
OBJECT)
throw new JsonStreamException
("Only json objects have names, actual type is "
+ valueType
);
return currentName
;
}
public 1.5.0/docs/api/java/lang/String.html">String valueAsString
() {
if (STRING == valueType
) return _stringValue
;
if (INTEGER == valueType
) return "" + _intValue
;
if (DOUBLE == valueType
) return "" + _doubleValue
;
if (NULL == valueType
) return null;
if (BOOLEAN == valueType
) {
return _booleanValue.
toString();
}
throw new JsonStreamException
("Readen value can not be converted to String");
}
public int valueAsInt
() {
if (INTEGER == valueType
) {
int value =
(int) _intValue
;
if (value
!= _intValue
) throwNumberFormatException
("an int",
"overflowing long value " + _intValue
);
return value
;
} else if (DOUBLE == valueType
) {
int value =
(int) _doubleValue
;
long longValue =
(long) _doubleValue
;
// lets accept only if the integer part is the same and ignore the decimals
if (value
!= longValue
) {
throwNumberFormatException
("an int",
"overflowing double value " + _doubleValue
);
}
return value
;
} else if (STRING == valueType
) return 1.5.0/docs/api/java/lang/Integer.html">Integer.
parseInt(_stringValue
);
throw new JsonStreamException
("Expected a int but value is of type " + valueType
);
}
public long valueAsLong
() {
if (INTEGER == valueType
) {
return _intValue
;
} else if (DOUBLE == valueType
) {
if (1.5.0/docs/api/java/lang/Long.html">Long.
MIN_VALUE > _doubleValue || _doubleValue
> 1.5.0/docs/api/java/lang/Long.html">Long.
MAX_VALUE) {
throwNumberFormatException
("a long",
"overflowing double value " + _doubleValue
);
}
return (long) _doubleValue
;
} else if (STRING == valueType
) return 1.5.0/docs/api/java/lang/Long.html">Long.
parseLong(_stringValue
);
throw new JsonStreamException
("Expected a long but value is of type " + valueType
);
}
public double valueAsDouble
() {
if (DOUBLE == valueType
) {
return _doubleValue
;
} else if (INTEGER == valueType
) {
// for the moment lets do that even if there is some precision loss...
return 1.5.0/docs/api/java/lang/Long.html">Long.
valueOf(_intValue
).
doubleValue();
} else if (STRING == valueType
) return 1.5.0/docs/api/java/lang/Double.html">Double.
parseDouble(_stringValue
);
throw new JsonStreamException
("Expected a double but value is of type " + valueType
);
}
public short valueAsShort
() {
if (INTEGER == valueType
) {
short value =
(short) _intValue
;
if (value
!= _intValue
) throwNumberFormatException
("a short",
"overflowing long value " + _intValue
);
return value
;
} else if (DOUBLE == valueType
) {
short value =
(short) _doubleValue
;
long longValue =
(long) _doubleValue
;
// lets accept only if the integer part is the same and ignore the decimals
if (value
!= longValue
) {
throwNumberFormatException
("a short",
"overflowing double value " + _doubleValue
);
}
return value
;
} else if (STRING == valueType
) return 1.5.0/docs/api/java/lang/Short.html">Short.
parseShort(_stringValue
);
throw new JsonStreamException
("Expected a short but value is of type " + valueType
);
}
public float valueAsFloat
() {
if (DOUBLE == valueType
) {
return (float) _doubleValue
;
} else if (INTEGER == valueType
) {
// same as for doubles, for the moment lets do that even if there is some precision
// loss...
return 1.5.0/docs/api/java/lang/Long.html">Long.
valueOf(_intValue
).
floatValue();
} else if (STRING == valueType
) return 1.5.0/docs/api/java/lang/Float.html">Float.
parseFloat(_stringValue
);
throw new JsonStreamException
("Expected a float but value is of type " + valueType
);
}
public boolean valueAsBoolean
() {
if (BOOLEAN == valueType
) {
return _booleanValue
;
}
if (STRING == valueType
) return 1.5.0/docs/api/java/lang/Boolean.html">Boolean.
parseBoolean(_stringValue
);
throw new JsonStreamException
("Readen value is not of type boolean");
}
public byte[] valueAsByteArray
() {
if (STRING == valueType
) return Base64.
decodeFast(_stringValue
);
if (NULL == valueType
) return null;
throw new JsonStreamException
("Expected a String to convert to byte array found "
+ valueType
);
}
public 1.5.0/docs/api/java/lang/String.html">String metadata
(1.5.0/docs/api/java/lang/String.html">String name
) {
if (!_metadata_readen
) nextObjectMetadata
();
return _metadata.
get(name
);
}
public ValueType getValueType
() {
return valueType
;
}
public ObjectReader skipValue
() {
if (ARRAY == valueType || OBJECT == valueType
) {
int balance =
0;
do {
if (ARRAY == valueType
) {
beginArray
();
balance++
;
} else if (OBJECT == valueType
) {
beginObject
();
balance++
;
}
while (hasNext
()) {
next
();
skipValue
();
}
JsonType type = _ctx.
peek();
if (JsonType.
ARRAY == type
) {
endArray
();
balance--
;
} else if (JsonType.
OBJECT == type
) {
endObject
();
balance--
;
}
} while (balance
> 0
);
}
return this;
}
public boolean hasNext
() {
int token = readNextToken
(false);
if (token == -1
) return false;
if (token
< 128
) {
if (_first || _ctx.
size() == 1
) return _NEXT_TOKEN
[token
];
else if (token ==
',') return true;
}
return false;
}
public ValueType next
() {
_metadata_readen =
false;
_first =
false;
char ctoken =
(char) readNextToken
(false);
if (ctoken ==
',') {
_cursor++
;
ctoken =
(char) readNextToken
(false);
} else if (JsonType.
ARRAY == _ctx.
peek()) {
if (ctoken ==
'[') {
valueType = ARRAY
;
return valueType
;
}
if (ctoken ==
'{') {
valueType = OBJECT
;
return valueType
;
}
}
if (JsonType.
OBJECT == _ctx.
peek()) {
currentName = consumeString
(ctoken
);
if (readNextToken
(true) !=
':') newWrongTokenException
(":", _cursor - 1
);
}
valueType = consumeValue
();
return valueType
;
}
@
1.5.0/docs/api/java/lang/Override.html">Override
public JsonType enclosingType
() {
return _ctx.
peek();
}
protected final ValueType consumeValue
() {
char ctoken =
(char) readNextToken
(false);
if (ctoken ==
'"') {
_stringValue = consumeString
(ctoken
);
return STRING
;
} else if (ctoken ==
'[') return ARRAY
;
else if (ctoken ==
'{') return OBJECT
;
else return consumeLiteral
();
}
protected final void readMetadata
() {
_metadata_readen =
true;
while (true) {
char ctoken =
(char) readNextToken
(false);
if ('"' != ctoken
) return;
ensureBufferHas
(2,
true);
if ('@' == _buffer
[_cursor + 1
]) {
_cursor++
;
// we cheat here...
1.5.0/docs/api/java/lang/String.html">String key = consumeString
(ctoken
);
if (readNextToken
(true) !=
':') newWrongTokenException
(":", _cursor - 1
);
1.5.0/docs/api/java/lang/String.html">String value = consumeString
((char) readNextToken
(false));
_metadata.
put(key, value
);
if (readNextToken
(false) ==
',') {
_cursor++
;
}
} else return;
}
}
protected final void begin
(int character, JsonType type
) {
int token = readNextToken
(true);
// seems unecessary as it is done in readNextToken
// checkIllegalEnd(token);
if (character == token
) {
_ctx.
push(type
);
} else newWrongTokenException
("" +
(char) character, _cursor - 1
);
_first =
true;
}
protected final void end
(int character, JsonType type
) {
int token = readNextToken
(true);
// seems unecessary as it is done in readNextToken
// checkIllegalEnd(token);
if (character == token
&& type == _ctx.
peek()) {
_ctx.
pop();
} else newWrongTokenException
("" +
(char) character, _cursor - 1
);
_first =
false;
}
protected final 1.5.0/docs/api/java/lang/String.html">String consumeString
(int token
) {
if (token
!=
'"') newMisplacedTokenException
(_cursor
);
_cursor++
;
boolean buffered =
false;
while (true) {
if (fillBuffer
(true) < 0) {
// TODO ugly to copy, by the way ensure we don't have the same problem elsewhere
1.5.0/docs/api/java/lang/String.html">String name =
new 1.5.0/docs/api/java/lang/String.html">String(_stringBuffer, 0, _stringBufferTail
);
_stringBufferTail =
0;
return name
;
}
int i = _cursor
;
for (; i
< _buflen
; ) {
if (_buffer
[i
] ==
'"') {
if (buffered
) {
writeToStringBuffer
(_buffer, _cursor, i - _cursor
);
_cursor = i +
1;
1.5.0/docs/api/java/lang/String.html">String name =
new 1.5.0/docs/api/java/lang/String.html">String(_stringBuffer, 0, _stringBufferTail
);
_stringBufferTail =
0;
return name
;
} else {
1.5.0/docs/api/java/lang/String.html">String name =
new 1.5.0/docs/api/java/lang/String.html">String(_buffer, _cursor, i - _cursor
);
_cursor = i +
1;
return name
;
}
} else if (_buffer
[i
] ==
'\\') {
buffered =
true;
writeToStringBuffer
(_buffer, _cursor, i - _cursor
);
_cursor = i +
1;
if (_stringBufferLength
<=
(_stringBufferTail + 1
)) expandStringBuffer
(16
);
_stringBuffer
[_stringBufferTail++
] = readEscaped
();
i = _cursor
;
} else i++
;
}
buffered =
true;
writeToStringBuffer
(_buffer, _cursor, i - _cursor
);
_cursor = i +
1;
}
}
/**
* Reads the next literal value into _booleanValue, _doubleValue or _intValue and returns the
* type of the readed literal, possible values are : INTEGER, DOUBLE, BOOLEAN, NULL. When
* calling this method the _cursor must be positioned on the first character of the value in the
* _buffer, you can ensure that by calling {@link #readNextToken(boolean)}.
*/
protected final ValueType consumeLiteral
() {
int token = _buffer
[_cursor
];
if ((token
> 47
&& token
< 58
) || token == 45
) {
return consumeNumber
();
} else {
ensureBufferHas
(4,
true);
if ((_buffer
[_cursor
] ==
'N' || _buffer
[_cursor
] ==
'n')
&& (_buffer
[_cursor +
1] ==
'U' || _buffer
[_cursor +
1] ==
'u')
&& (_buffer
[_cursor +
2] ==
'L' || _buffer
[_cursor +
2] ==
'l')
&& (_buffer
[_cursor +
3] ==
'L' || _buffer
[_cursor +
3] ==
'l')) {
_cursor +=
4;
return NULL
;
}
if ((_buffer
[_cursor
] ==
'T' || _buffer
[_cursor
] ==
't')
&& (_buffer
[_cursor +
1] ==
'R' || _buffer
[_cursor +
1] ==
'r')
&& (_buffer
[_cursor +
2] ==
'U' || _buffer
[_cursor +
2] ==
'u')
&& (_buffer
[_cursor +
3] ==
'E' || _buffer
[_cursor +
3] ==
'e')) {
_booleanValue =
true;
_cursor +=
4;
return BOOLEAN
;
}
ensureBufferHas
(5,
true);
if ((_buffer
[_cursor
] ==
'F' || _buffer
[_cursor
] ==
'f')
&& (_buffer
[_cursor +
1] ==
'A' || _buffer
[_cursor +
1] ==
'a')
&& (_buffer
[_cursor +
2] ==
'L' || _buffer
[_cursor +
2] ==
'l')
&& (_buffer
[_cursor +
3] ==
'S' || _buffer
[_cursor +
3] ==
's')
&& (_buffer
[_cursor +
4] ==
'E' || _buffer
[_cursor +
4] ==
'e')) {
_booleanValue =
false;
_cursor +=
5;
return BOOLEAN
;
} else {
throw new JsonStreamException.
Builder().
message(
"Illegal character around row " + _row +
" and column " +
(_cursor - _col
)
+
" awaited for literal (number, boolean or null) but read '"
+ _buffer
[_cursor
] +
"'!").
create();
}
}
}
private ValueType consumeNumber
() {
// lets fill the buffer and handle differently overflowing values
if ((_buflen - _cursor
) < 378
) ensureBufferHas
(_buflen,
false);
int begin = _cursor
;
int cur
;
boolean negative
;
// check the sign
if (_buffer
[_cursor
] == 45
) {
negative =
true;
_cursor++
;
cur = _cursor
;
} else {
negative =
false;
cur = _cursor
;
}
// just to handle invalid leading 0000
for (; cur
< _buflen
&& _buffer
[cur
] ==
48; cur++
) ;
// Careful we consume the '-' here, but also all the leading 0, even if it is of form 0.xxx
_cursor = cur
;
int len =
1.5.0/docs/api/java/lang/Math.html">Math.
min(_buflen, cur + 18
);
int token
;
long longValue =
0;
for (; cur
< len
; cur++
) {
token = _buffer
[cur
];
if (token
< 48 || token
> 57
) {
break;
}
longValue = 10L
* longValue +
(token - 48
);
}
if (cur
< _buflen
) {
// read the maximum we can to fill the long capacity, at max we can read 1 additional
// digit
token = _buffer
[cur
];
if (token
> 47
&& token
< 58
) {
long newLongValue = 10L
* longValue +
(token - 48
);
if (newLongValue
> longValue
) {
longValue = newLongValue
;
cur++
;
}
/* TODO we parse here Long.MIN_VALUE as a double, we had also pb in the writer
* the solution => instead of doing operations on positive numbers, do the inverse, use negative numbers
* as the min negative long holds the - max positive long.
*/
// else we exceed long capacity, just continue and parse it as a double
}
if (cur
< _buflen
&& ((token = _buffer
[cur
]) == 46 || token == 101 || token == 69 ||
(token
> 47
&& token
< 58
))) {
if (strictDoubleParse
) {
_cursor = begin
;
return consumeStrictNumber
(cur
);
} else return consumeDouble
(cur, longValue, negative
);
}
}
_intValue = negative
? -longValue : longValue
;
_numberLen = cur - _cursor
;
_cursor = cur
;
return INTEGER
;
}
// a custom implementation based on fast path principles
private ValueType consumeDouble
(int cur,
long longValue,
boolean negative
) {
int token
;
// TODO a verifier : on ne veut compter dans les intDigit que ce qui n'est pas pris dans le
// longValue, idem pour les decimales??
int intDigits = cur - _cursor
;
int valueDigits = longValue
> 0
? cur - _cursor :
0;
// ok we have readen as many characters as a long can contain
// the next readen characters will serve for the digit count for large integer numbers
if (intDigits
> 17
) {
for (; cur
< _buflen
; cur++
) {
if (_buffer
[cur
] < 48 || _buffer
[cur
] > 57
) {
break;
}
}
// only if we advanced
if (intDigits
!=
(cur - _cursor
)) intDigits =
(cur - _cursor
) - intDigits
;
} else
// TODO check ça m'a l'air bizarre comme cas...
intDigits =
0;
int decimalDigits =
0;
// next possible case is a dot
if (cur
< _buflen
&& _buffer
[cur
] == 46
) {
cur++
;
int start = cur
;
// if integer part value is zero we could use scientific notation and win in precision
if (longValue ==
0) {
// reset the counter as we don't care of the leading zeros
intDigits =
0;
// now lets try to read as many consecutive zeros as available
for (; cur
< _buflen
&& _buffer
[cur
] ==
48; cur++
) ;
}
// ok now we must read again into the longValue
int len =
1.5.0/docs/api/java/lang/Math.html">Math.
min(_buflen, cur +
(18 - valueDigits
));
for (; cur
< len
; cur++
) {
token = _buffer
[cur
];
if (token
< 48 || token
> 57
) {
break;
}
longValue = 10L
* longValue +
(token - 48
);
}
decimalDigits = cur - start
;
start = cur
;
// no need to count digits after the precision we support for decimals
// continue reading digits and just ignore the values, we will truncate
for (; cur
< _buflen
; cur++
) {
if (_buffer
[cur
] < 48 || _buffer
[cur
] > 57
) {
break;
}
}
}
// now try to read exponent E/e
if ((cur + 1
) < _buflen
&& (_buffer
[cur
] == 101 || _buffer
[cur
] == 69
)) {
token = _buffer
[++cur
];
boolean negativeExp
;
// check the sign
if (token == 45
) {
negativeExp =
true;
cur++
;
} else {
if (token == 43
) cur++
;
negativeExp =
false;
}
// read the power of ten
int powValue =
0;
for (; cur
< _buflen
; cur++
) {
token = _buffer
[cur
];
if (token
< 48 || token
> 57
) {
break;
}
powValue = 10
* powValue +
(token - 48
);
}
// depending on the sign put it in the decimal digit counter or integer digit counter
if (negativeExp
) decimalDigits += powValue
;
else intDigits += powValue
;
}
// and now make the difference so it balances well
decimalDigits = intDigits - decimalDigits
;
if (decimalDigits
< 0) {
/*
* This allows to handle also Double.MIN_VALUE and Double.MIN_NORMAL as
* Double.MIN_NORMAL has 16 decimal digits, 308+16=324, but 10^324 overflows the double
* capacity, its Infinity. So we use this little trick.
*/
if (decimalDigits
< -308
) {
if (decimalDigits
< -325
) {
_doubleValue =
0;
} else {
_doubleValue = longValue / _POWS
[-decimalDigits - 308
];
_doubleValue = _doubleValue / _POWS
[308
];
}
} else {
// better precision than multiplication
_doubleValue = longValue / _POWS
[-decimalDigits
];
}
} else {
if (decimalDigits
> 308
) {
_doubleValue =
1.5.0/docs/api/java/lang/Double.html">Double.
POSITIVE_INFINITY;
} else {
// we don't need to apply same technique as before as
// 308-16=292 so it's okay :)
_doubleValue = longValue
* _POWS
[decimalDigits
];
}
}
_doubleValue = negative
? -_doubleValue : _doubleValue
;
_numberLen = cur - _cursor
;
_cursor = cur
;
return DOUBLE
;
}
private final ValueType consumeStrictNumber
(int localCursor
) {
if (localCursor
< _buflen
) {
// consider all the remaining integer values as part of the double
for (; localCursor
< _buflen
; localCursor++
) {
if (_buffer
[localCursor
] < 48 || _buffer
[localCursor
] > 57
) {
break;
}
}
}
if (localCursor
< _buflen
) {
if (_buffer
[localCursor
] ==
'.') {
localCursor = advanceWhileNumeric
(++localCursor
);
}
}
if (localCursor + 1
< _buflen
) {
char ctoken = _buffer
[localCursor
];
if (ctoken ==
'e' || ctoken ==
'E') {
ctoken = _buffer
[++localCursor
];
if (ctoken ==
'-' || ctoken ==
'+' ||
(ctoken
> 47
&& ctoken
< 58
)) {
localCursor = advanceWhileNumeric
(++localCursor
);
} else newWrongTokenException
("'-' or '+' or '' (same as +)");
}
}
// it may overflow of the max numeric value we accept to read, it should
if (localCursor
>= _buflen
) {
}
_numberLen = localCursor - _cursor
;
_stringValue =
new 1.5.0/docs/api/java/lang/String.html">String(_buffer, _cursor, _numberLen
);
_doubleValue =
1.5.0/docs/api/java/lang/Double.html">Double.
parseDouble(_stringValue
);
_cursor = localCursor
;
return DOUBLE
;
}
private int advanceWhileNumeric
(int cursor
) {
for (; cursor
< _buflen
; cursor++
) {
if ((_buffer
[cursor
] < 48 || _buffer
[cursor
] > 57
)) {
return cursor
;
}
}
return cursor
;
}
protected final int readNextToken
(boolean consume
) {
while (true) {
if (_cursor
>= _buflen
) fillBuffer
(true);
for (; _cursor
< _buflen
; _cursor++
) {
int token = _buffer
[_cursor
];
if (token
< 128
&& SKIPPED_TOKENS
[token
] == 0
) {
if (token ==
'/') {
ensureBufferHas
(2,
true);
if (_buffer
[_cursor +
1] ==
'*') {
_cursor +=
2;
advanceAfter
(_END_OF_BLOCK_COMMENT
);
} else if (_buffer
[_cursor +
1] ==
'/') {
_cursor +=
2;
advanceAfter
(_END_OF_LINE
);
_row++
;
_col = _cursor
;
} else newWrongTokenException
("start comment // or /*", _cursor
);
// don't consume the token
_cursor--
;
} else if (consume
) {
return _buffer
[_cursor++
];
} else return token
;
} else if (_buffer
[_cursor
] ==
'\n') {
_row++
;
_col = _cursor
;
}
}
if (_buflen == -1
) break;
}
return _cursor
< _buflen
? _buffer
[_cursor
] : -
1;
}
private final void advanceAfter
(char[] str
) {
int strPos =
0;
while (true) {
if (_cursor
>= _buflen
) fillBuffer
(true);
for (; _cursor
< _buflen
&& strPos
< str.
length; _cursor++
) {
if (_buffer
[_cursor
] == str
[strPos
]) {
strPos++
;
} else strPos =
0;
}
if (strPos == str.
length) {
return;
}
if (_buflen == -1
) break;
}
}
protected final char readEscaped
() {
fillBuffer
(true);
char token = _buffer
[_cursor++
];
switch (token
) {
case 'b':
return '\b';
case 't':
return '\t';
case 'n':
return '\n';
case 'f':
return '\f';
case 'r':
return '\r';
case '"':
case '/':
case '\\':
return token
;
case 'u':
break;
default:
newMisplacedTokenException
(_cursor - 1
);
}
int value =
0;
if (ensureBufferHas
(4,
false) < 0
) {
throw new JsonStreamException
("Expected 4 hex-digit for character escape sequence!");
// System.arraycopy(buffer, cursor, buffer, 0, buflen-cursor);
// buflen = buflen - cursor + reader.read(buffer, buflen-cursor, cursor);
// cursor = 0;
}
for (int i =
0; i
< 4; ++i
) {
int ch = _buffer
[_cursor++
];
int digit =
(ch
> 127
) ? -1 : sHexValues
[ch
];
if (digit
< 0
) {
throw new JsonStreamException
("Wrong character '" + ch
+
"' expected a hex-digit for character escape sequence!");
}
value =
(value
<< 4
) | digit
;
}
return (char) value
;
}
private final void writeToStringBuffer
(final char[] data,
final int offset,
final int length
) {
if (_stringBufferLength
<=
(_stringBufferTail + length
)) {
expandStringBuffer
(length
);
}
1.5.0/docs/api/java/lang/System.html">System.
arraycopy(data, offset, _stringBuffer, _stringBufferTail, length
);
_stringBufferTail += length
;
}
private final void expandStringBuffer
(int length
) {
char[] extendedStringBuffer =
new char[_stringBufferLength
* 2 + length
];
1.5.0/docs/api/java/lang/System.html">System.
arraycopy(_stringBuffer, 0, extendedStringBuffer, 0, _stringBufferTail
);
_stringBuffer = extendedStringBuffer
;
_stringBufferLength = extendedStringBuffer.
length;
}
private final int fillBuffer
(boolean doThrow
) {
if (_cursor
< _buflen
) return _buflen
;
try {
_buflen = reader.
read(_buffer
);
} catch (1.5.0/docs/api/java/io/IOException.html">IOException ioe
) {
throw new JsonStreamException
(ioe
);
}
checkIllegalEnd
(_buflen
);
_cursor =
0;
_col =
0;
return _buflen
;
}
private final int ensureBufferHas
(int minLength,
boolean doThrow
) {
try {
int actualLen = _buflen - _cursor
;
if (actualLen
>= minLength
) {
return actualLen
;
}
1.5.0/docs/api/java/lang/System.html">System.
arraycopy(_buffer, _cursor, _buffer, 0, actualLen
);
for (; actualLen
< minLength
; ) {
int len = reader.
read(_buffer, actualLen, _buffer.
length - actualLen
);
if (len
< 0
) {
if (doThrow
) throw new JsonStreamException
(
"Encountered end of stream, incomplete json!");
else {
_buflen = actualLen
;
_col =
0;
_cursor =
0;
return len
;
}
}
actualLen += len
;
}
_buflen = actualLen
;
_col =
0;
_cursor =
0;
return actualLen
;
} catch (1.5.0/docs/api/java/io/IOException.html">IOException ioe
) {
throw new JsonStreamException
(ioe
);
}
}
protected final boolean isEOF
() {
return _buflen
< 0 || fillBuffer
(false) < 0;
}
private final void newWrongTokenException
(1.5.0/docs/api/java/lang/String.html">String awaited
) {
newWrongTokenException
(awaited, _cursor
);
}
public int column
() {
int col = _cursor - _col
;
return col
< 0
? 0 : col
;
}
public int row
() {
return _row
;
}
private final void newWrongTokenException
(1.5.0/docs/api/java/lang/String.html">String awaited,
int cursor
) {
// otherwise it fails when an error occurs on first character
if (cursor
< 0
) cursor =
0;
int pos = cursor - _col
;
if (pos
< 0
) pos =
0;
if (_buflen
< 0
) throw new JsonStreamException
(
"Incomplete data or malformed json : encoutered end of stream but expected "
+ awaited
).
niceTrace();
else throw new JsonStreamException.
Builder()
.
message(
"Illegal character at row " + _row +
" and column " + pos +
" expected "
+ awaited +
" but read '" + _buffer
[cursor
] +
"' !")
.
locate(_row, pos
).
create().
niceTrace();
}
private final void newMisplacedTokenException
(int cursor
) {
if (_buflen
< 0
)
throw JsonStreamException.
niceTrace(new JsonStreamException
(
"Incomplete data or malformed json : encoutered end of stream."));
if (cursor
< 0
) cursor =
0;
int pos = cursor - _col
;
if (pos
< 0
) pos =
0;
throw new JsonStreamException.
Builder()
.
message(
"Encountred misplaced character '" + _buffer
[cursor
] +
"' around row "
+ _row +
" and column " + pos
).
locate(_row, pos
).
create().
niceTrace();
}
private final void checkIllegalEnd
(int token
) {
if (token == -1
&& JsonType.
EMPTY != _ctx.
peek())
throw new JsonStreamException
(
"Incomplete data or malformed json : encoutered end of stream!").
niceTrace();
}
private void throwNumberFormatException
(1.5.0/docs/api/java/lang/String.html">String expected,
1.5.0/docs/api/java/lang/String.html">String encoutered
) {
int pos = _cursor - _col - _numberLen
;
throw JsonStreamException.
niceTrace(new 1.5.0/docs/api/java/lang/NumberFormatException.html">NumberFormatException("Wrong numeric type at row " + _row +
" and column " + pos
+
", expected " + expected +
" but encoutered " + encoutered
));
}
}