in src/com/amazon/ion/impl/IonTokenReader.java [1252:1447]
public Type readNumber(int c) throws IOException {
// clear out our string buffer
resetValue();
// process the initial sign character, if its there.
boolean explicitPlusSign = false;
switch (c) {
case '-':
value.append((char)c);
c = this.read();
t = Type.constNegInt;
this.numberType = NumberType.NT_NEGINT;
this.isNegative = true;
break;
case '+':
// we eat the plus sign, but remember we saw one
explicitPlusSign = true;
c = this.read();
t = Type.constPosInt;
this.isNegative = false;
this.numberType = NumberType.NT_POSINT;
break;
default:
t = Type.constPosInt;
this.numberType = NumberType.NT_POSINT;
this.isNegative = false;
break;
}
// process the initial digit. Caller has checked that it's a digit.
assert isDigit(c, 10);
value.append((char)c);
boolean isZero = (c == '0');
boolean leadingZero = isZero;
// process the next character after the initial digit.
switch((c = this.read())) {
case 'x':
case 'X':
if (!isZero) {
throw new IonException("badly formed number encountered at " + position());
}
this.numberType = NumberType.NT_HEX;
// We don't need to append the char because we're going to wipe
// the value anyway to accumulate just the hex digits.
return scanHexNumber();
case '.':
case 'd':
case 'D':
t = Type.constDecimal;
this.numberType = NumberType.NT_DECIMAL;
value.append((char)c);
break;
case 'e':
case 'E':
t = Type.constFloat;
this.numberType = NumberType.NT_FLOAT;
value.append((char)c);
break;
default:
if (!isDigit(c, 10)) {
checkAndUnreadNumericStopper(c);
if (isZero && NumberType.NT_NEGINT.equals(this.numberType)) {
t = Type.constPosInt;
this.numberType = NumberType.NT_POSINT;
}
return t;
}
value.append((char)c);
isZero &= (c == '0');
break;
}
// see if see can continue find leading digits
if (NumberType.NT_NEGINT.equals(numberType)
|| NumberType.NT_POSINT.equals(numberType)
) {
// We've now scanned at least two digits.
// read in the remaining whole number digits
for (;;) {
c = this.read();
if (!isDigit(c, 10)) break;
value.append((char)c);
isZero &= (c == '0');
}
// and see what we ran aground on ..
switch (c) {
case '.':
case 'd':
case 'D':
if (leadingZero) {
throw new IonException(position() + ": Invalid leading zero on numeric");
}
t = Type.constDecimal;
this.numberType = NumberType.NT_DECIMAL;
value.append((char)c);
break;
case 'e':
case 'E':
if (leadingZero) {
throw new IonException(position() + ": Invalid leading zero on numeric");
}
t = Type.constFloat;
this.numberType = NumberType.NT_FLOAT;
value.append((char)c);
break;
case 'T': // same as '-' it's a timestamp
case '-':
if (NumberType.NT_POSINT.equals(this.numberType) && !explicitPlusSign) {
return scanTimestamp(c);
}
// otherwise fall through to "all other" processing
default:
// Hit the end of our integer. Make sure its a valid stopper.
checkAndUnreadNumericStopper(c);
if (leadingZero && !isZero) {
throw new IonException(position() + ": Invalid leading zero on numeric");
}
if (isZero && NumberType.NT_NEGINT.equals(this.numberType)) {
t = Type.constPosInt;
this.numberType = NumberType.NT_POSINT;
}
return t;
}
}
// now we must have just decimal or float
// if it's decimal we ended on a DOT, so get the trailing
// (fractional) digits
if (NumberType.NT_DECIMAL.equals(this.numberType)) {
for (;;) {
c = this.read();
if (!isDigit(c, 10)) break;
value.append((char)c);
}
// and see what we ran aground on this time..
switch (c) {
case '-':
case '+':
// We'll handle exponent below.
this.unread(c);
break;
case 'e':
case 'E':
// this is the only viable option now
t = Type.constFloat;
this.numberType = NumberType.NT_FLOAT;
value.append((char)c);
break;
case 'd':
case 'D':
// this is the only viable option now
assert t == Type.constDecimal;
assert NumberType.NT_DECIMAL.equals(this.numberType);
value.append((char)c);
break;
default:
checkAndUnreadNumericStopper(c);
return t;
}
}
// now we have the exponent part to read
// reading the first character of the exponent, it can be
// a sign character or a digit (this is the only place
// the sign is valid) and we're for sure a float at this point
switch ((c = this.read())) {
case '-':
value.append((char)c);
break;
case '+':
// we eat the plus sign
break;
default:
if (!isDigit(c, 10)) {
// this they said 'e' then they better put something valid after it!
throw new IonException("badly formed number encountered at " + position());
}
this.unread(c);
// we'll re-read this in just a bit, but we had to check
break;
}
// read in the remaining whole number digits and then quit
for (;;) {
c = this.read();
if (!isDigit(c, 10)) break;
value.append((char)c);
}
checkAndUnreadNumericStopper(c);
return t;
}