package org.codehaus.jackson.impl; import java.io.IOException; import java.io.InputStream; import org.codehaus.jackson.JsonParseException; import org.codehaus.jackson.JsonToken; import org.codehaus.jackson.io.IOContext; /** * Intermediate class that implements handling of numeric parsing, * when using UTF-8 encoded byte-based input source. * Separate from the actual parser class just to isolate numeric * parsing: would be nice to use aggregation, but unfortunately * many parts are hard to implement without direct access to * underlying buffers. */ public abstract class Utf8NumericParser extends StreamBasedParserBase { /* //////////////////////////////////////////////////// // Life-cycle //////////////////////////////////////////////////// */ public Utf8NumericParser(IOContext pc, int features, InputStream in, byte[] inputBuffer, int start, int end, boolean bufferRecyclable) { super(pc, features, in, inputBuffer, start, end, bufferRecyclable); } /* //////////////////////////////////////////////////// // Textual parsing of number values //////////////////////////////////////////////////// */ /** * Initial parsing method for number values. It needs to be able * to parse enough input to be able to determine whether the * value is to be considered a simple integer value, or a more * generic decimal value: latter of which needs to be expressed * as a floating point number. The basic rule is that if the number * has no fractional or exponential part, it is an integer; otherwise * a floating point number. *<p> * Because much of input has to be processed in any case, no partial * parsing is done: all input text will be stored for further * processing. However, actual numeric value conversion will be * deferred, since it is usually the most complicated and costliest * part of processing. */ @Override protected final JsonToken parseNumberText(int c) throws IOException, JsonParseException { char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); int outPtr = 0; boolean negative = (c == INT_MINUS); // Need to prepend sign? if (negative) { outBuf[outPtr++] = '-'; // Must have something after sign too if (_inputPtr >= _inputEnd) { loadMoreGuaranteed(); } c = (int) _inputBuffer[_inputPtr++] & 0xFF; } int intLen = 0; boolean eof = false; // Ok, first the obligatory integer part: int_loop: while (true) { if (c < INT_0 || c > INT_9) { break int_loop; } ++intLen; // Quickie check: no leading zeroes allowed if (intLen == 2) { if (outBuf[outPtr-1] == '0') { reportInvalidNumber("Leading zeroes not allowed"); } } if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } outBuf[outPtr++] = (char) c; if (_inputPtr >= _inputEnd && !loadMore()) { // EOF is legal for main level int values c = CHAR_NULL; eof = true; break int_loop; } c = (int) _inputBuffer[_inputPtr++] & 0xFF; } // Also, integer part is not optional if (intLen == 0) { reportInvalidNumber("Missing integer part (next char "+_getCharDesc(c)+")"); } int fractLen = 0; // And then see if we get other parts if (c == '.') { // yes, fraction outBuf[outPtr++] = (char) c; fract_loop: while (true) { if (_inputPtr >= _inputEnd && !loadMore()) { eof = true; break fract_loop; } c = (int) _inputBuffer[_inputPtr++] & 0xFF; if (c < INT_0 || c > INT_9) { break fract_loop; } ++fractLen; if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } outBuf[outPtr++] = (char) c; } // must be followed by sequence of ints, one minimum if (fractLen == 0) { reportUnexpectedNumberChar(c, "Decimal point not followed by a digit"); } } int expLen = 0; if (c == 'e' || c == 'E') { // exponent? if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } outBuf[outPtr++] = (char) c; // Not optional, can require that we get one more char if (_inputPtr >= _inputEnd) { loadMoreGuaranteed(); } c = (int) _inputBuffer[_inputPtr++] & 0xFF; // Sign indicator? if (c == '-' || c == '+') { if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } outBuf[outPtr++] = (char) c; // Likewise, non optional: if (_inputPtr >= _inputEnd) { loadMoreGuaranteed(); } c = (int) _inputBuffer[_inputPtr++] & 0xFF; } exp_loop: while (c <= INT_9 && c >= INT_0) { ++expLen; if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } outBuf[outPtr++] = (char) c; if (_inputPtr >= _inputEnd && !loadMore()) { eof = true; break exp_loop; } c = (int) _inputBuffer[_inputPtr++] & 0xFF; } // must be followed by sequence of ints, one minimum if (expLen == 0) { reportUnexpectedNumberChar(c, "Exponent indicator not followed by a digit"); } } // Ok; unless we hit end-of-input, need to push last char read back if (!eof) { --_inputPtr; } _textBuffer.setCurrentLength(outPtr); // And there we have it! return reset(negative, intLen, fractLen, expLen); } }