public Type readNumber()

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;
    }