Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   package com.fasterxml.jackson.core.json;
   
   import java.io.*;
   
This is a concrete implementation of com.fasterxml.jackson.core.JsonParser, which is based on a java.io.Reader to handle low-level character conversion tasks.
  
  public final class ReaderBasedJsonParser
      extends ParserBase
  {
      /*
      /**********************************************************
      /* Input configuration
      /**********************************************************
       */

    
Reader that can be used for reading more content, if one buffer from input source, but in some cases pre-loaded buffer is handed to the parser.
  
      protected Reader _reader;

    
Current buffer from which data is read; generally data is read into buffer from input source.
  
      protected char[] _inputBuffer;
  
      /*
      /**********************************************************
      /* Configuration
      /**********************************************************
       */
  
      protected ObjectCodec _objectCodec;
  
      final protected CharsToNameCanonicalizer _symbols;
      
      final protected int _hashSeed;
  
      /*
      /**********************************************************
      /* Parsing state
      /**********************************************************
       */
    
    
Flag that indicates that the current token has not yet been fully processed, and needs to be finished for some access (or skipped to obtain the next token)
  
      protected boolean _tokenIncomplete = false;
      
      /*
      /**********************************************************
      /* Life-cycle
      /**********************************************************
       */
  
      public ReaderBasedJsonParser(IOContext ctxtint featuresReader r,
              ObjectCodec codecCharsToNameCanonicalizer st)
      {
          super(ctxtfeatures);
           = r;
           = ctxt.allocTokenBuffer();
           = codec;
           = st;
           = st.hashSeed();
      }
  
      @Override
      public Version version() {
          return ..version();
      }
      
      /*
      /**********************************************************
      /* Base method defs, overrides
      /**********************************************************
       */
      
      @Override
      public ObjectCodec getCodec() {
          return ;
      }
  
      @Override
      public void setCodec(ObjectCodec c) {
           = c;
     }
     
     @Override
     public int releaseBuffered(Writer wthrows IOException
     {
         int count =  - ;
         if (count < 1) {
             return 0;
         }
         // let's just advance ptr to end
         int origPtr = ;
         w.write(origPtrcount);
         return count;
     }
 
     @Override
     public Object getInputSource() {
         return ;
     }
 
     @Override
     protected boolean loadMore() throws IOException
     {
          += ;
          -= ;
 
         if ( != null) {
             int count = .read(, 0, .);
             if (count > 0) {
                  = 0;
                  = count;
                 return true;
             }
             // End of input
             _closeInput();
             // Should never return 0, so let's fail
             if (count == 0) {
                 throw new IOException("Reader returned 0 characters when trying to read "+);
             }
         }
         return false;
     }
 
     protected char getNextChar(String eofMsg)
         throws IOExceptionJsonParseException
     {
         if ( >= ) {
             if (!loadMore()) {
                 _reportInvalidEOF(eofMsg);
             }
         }
         return [++];
     }
 
     @Override
     protected void _closeInput() throws IOException
     {
         /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
          *   on the underlying Reader, unless we "own" it, or auto-closing
          *   feature is enabled.
          *   One downside is that when using our optimized
          *   Reader (granted, we only do that for UTF-32...) this
          *   means that buffer recycling won't work correctly.
          */
         if ( != null) {
             if (.isResourceManaged() || isEnabled(.)) {
                 .close();
             }
              = null;
         }
     }

    
Method called to release internal buffers owned by the base reader. This may be called along with _closeInput() (for example, when explicitly closing this reader instance), or separately (if need be).
 
     @Override
     protected void _releaseBuffers()
         throws IOException
     {
         super._releaseBuffers();
         char[] buf = ;
         if (buf != null) {
              = null;
             .releaseTokenBuffer(buf);
         }
     }
     
     /*
     /**********************************************************
     /* Public API, data access
     /**********************************************************
      */
    
    
Method for accessing textual representation of the current event; if no current event (before first call to nextToken(), or after encountering end-of-input), returns null. Method can be called for any event.
 
     @Override
     public String getText()
         throws IOExceptionJsonParseException
     {
         JsonToken t = ;
         if (t == .) {
             if () {
                  = false;
                 _finishString(); // only strings can be incomplete
             }
             return .contentsAsString();
         }
         return _getText2(t);
     }
 
     // // // Let's override default impls for improved performance
     
     // @since 2.1
     @Override
     {
         if ( == .) {
             if () {
                  = false;
                 _finishString(); // only strings can be incomplete
             }
             return .contentsAsString();
         }
         return super.getValueAsString(null);
     }
     
     // @since 2.1
     @Override
     public String getValueAsString(String defValuethrows IOExceptionJsonParseException
     {
         if ( == .) {
             if () {
                  = false;
                 _finishString(); // only strings can be incomplete
             }
             return .contentsAsString();
         }
         return super.getValueAsString(defValue);
     }
     
     
     protected String _getText2(JsonToken t)
     {
         if (t == null) {
             return null;
         }
         switch (t) {
         case :
             return .getCurrentName();
 
         case :
             // fall through
         case :
         case :
             return .contentsAsString();
         }
         return t.asString();
     }
 
     @Override
     public char[] getTextCharacters()
         throws IOExceptionJsonParseException
     {
         if ( != null) { // null only before/after document
             switch () {
                 
             case :
                 if (!) {
                     String name = .getCurrentName();
                     int nameLen = name.length();
                     if ( == null) {
                          = .allocNameCopyBuffer(nameLen);
                     } else if (. < nameLen) {
                          = new char[nameLen];
                     }
                     name.getChars(0, nameLen, 0);
                      = true;
                 }
                 return ;
     
             case :
                 if () {
                      = false;
                     _finishString(); // only strings can be incomplete
                 }
                 // fall through
             case :
             case :
                 return .getTextBuffer();
                 
             default:
                 return .asCharArray();
             }
         }
         return null;
     }
 
     @Override
     public int getTextLength()
         throws IOExceptionJsonParseException
     {
         if ( != null) { // null only before/after document
             switch () {
                 
             case :
                 return .getCurrentName().length();
             case :
                 if () {
                      = false;
                     _finishString(); // only strings can be incomplete
                 }
                 // fall through
             case :
             case :
                 return .size();
                 
             default:
                 return .asCharArray().length;
             }
         }
         return 0;
     }
 
     @Override
     public int getTextOffset() throws IOExceptionJsonParseException
     {
         // Most have offset of 0, only some may have other values:
         if ( != null) {
             switch () {
             case :
                 return 0;
             case :
                 if () {
                      = false;
                     _finishString(); // only strings can be incomplete
                 }
                 // fall through
             case :
             case :
                 return .getTextOffset();
             }
         }
         return 0;
     }
 
     @Override
     public Object getEmbeddedObject() throws IOExceptionJsonParseException {
         return null;
     }
 
     @Override
     public byte[] getBinaryValue(Base64Variant b64variant)
         throws IOExceptionJsonParseException
     {
         if ( != . &&
                 ( != . ||  == null)) {
             _reportError("Current token ("++") not VALUE_STRING or VALUE_EMBEDDED_OBJECT, can not access as binary");
         }
         /* To ensure that we won't see inconsistent data, better clear up
          * state...
          */
         if () {
             try {
                  = _decodeBase64(b64variant);
             } catch (IllegalArgumentException iae) {
                 throw _constructError("Failed to decode VALUE_STRING as base64 ("+b64variant+"): "+iae.getMessage());
             }
             /* let's clear incomplete only now; allows for accessing other
              * textual content in error cases
              */
              = false;
         } else { // may actually require conversion...
             if ( == null) {
                 ByteArrayBuilder builder = _getByteArrayBuilder();
                 _decodeBase64(getText(), builderb64variant);
                  = builder.toByteArray();
             }
         }
         return ;
     }
     
     @Override
     public int readBinaryValue(Base64Variant b64variantOutputStream out)
         throws IOExceptionJsonParseException
     {
         // if we have already read the token, just use whatever we may have
         if (! ||  != .) {
             byte[] b = getBinaryValue(b64variant);
             out.write(b);
             return b.length;
         }
         // otherwise do "real" incremental parsing...
         byte[] buf = .allocBase64Buffer();
         try {
             return _readBinary(b64variantoutbuf);
         } finally {
             .releaseBase64Buffer(buf);
         }
     }
 
     protected int _readBinary(Base64Variant b64variantOutputStream out,
                               byte[] buffer)
             throws IOExceptionJsonParseException
     {
         int outputPtr = 0;
         final int outputEnd = buffer.length - 3;
         int outputCount = 0;
 
         while (true) {
             // first, we'll skip preceding white space, if any
             char ch;
             do {
                 if ( >= ) {
                     loadMoreGuaranteed();
                 }
                 ch = [++];
             } while (ch <= );
             int bits = b64variant.decodeBase64Char(ch);
             if (bits < 0) { // reached the end, fair and square?
                 if (ch == '"') {
                     break;
                 }
                 bits = _decodeBase64Escape(b64variantch, 0);
                 if (bits < 0) { // white space to skip
                     continue;
                 }
             }
 
             // enough room? If not, flush
             if (outputPtr > outputEnd) {
                 outputCount += outputPtr;
                 out.write(buffer, 0, outputPtr);
                 outputPtr = 0;
             }
 
             int decodedData = bits;
 
             // then second base64 char; can't get padding yet, nor ws
 
             if ( >= ) {
                 loadMoreGuaranteed();
             }
             ch = [++];
             bits = b64variant.decodeBase64Char(ch);
             if (bits < 0) {
                 bits = _decodeBase64Escape(b64variantch, 1);
             }
             decodedData = (decodedData << 6) | bits;
 
             // third base64 char; can be padding, but not ws
             if ( >= ) {
                 loadMoreGuaranteed();
             }
             ch = [++];
             bits = b64variant.decodeBase64Char(ch);
 
             // First branch: can get padding (-> 1 byte)
             if (bits < 0) {
                 if (bits != .) {
                     // as per [JACKSON-631], could also just be 'missing'  padding
                     if (ch == '"' && !b64variant.usesPadding()) {
                         decodedData >>= 4;
                         buffer[outputPtr++] = (bytedecodedData;
                         break;
                     }
                     bits = _decodeBase64Escape(b64variantch, 2);
                 }
                 if (bits == .) {
                     // Ok, must get padding
                     if ( >= ) {
                         loadMoreGuaranteed();
                     }
                     ch = [++];
                     if (!b64variant.usesPaddingChar(ch)) {
                         throw reportInvalidBase64Char(b64variantch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
                     }
                     // Got 12 bits, only need 8, need to shift
                     decodedData >>= 4;
                     buffer[outputPtr++] = (bytedecodedData;
                     continue;
                 }
             }
             // Nope, 2 or 3 bytes
             decodedData = (decodedData << 6) | bits;
             // fourth and last base64 char; can be padding, but not ws
             if ( >= ) {
                 loadMoreGuaranteed();
             }
             ch = [++];
             bits = b64variant.decodeBase64Char(ch);
             if (bits < 0) {
                 if (bits != .) {
                     // as per [JACKSON-631], could also just be 'missing'  padding
                     if (ch == '"' && !b64variant.usesPadding()) {
                         decodedData >>= 2;
                         buffer[outputPtr++] = (byte) (decodedData >> 8);
                         buffer[outputPtr++] = (bytedecodedData;
                         break;
                     }
                     bits = _decodeBase64Escape(b64variantch, 3);
                 }
                 if (bits == .) {
                     /* With padding we only get 2 bytes; but we have
                      * to shift it a bit so it is identical to triplet
                      * case with partial output.
                      * 3 chars gives 3x6 == 18 bits, of which 2 are
                      * dummies, need to discard:
                      */
                     decodedData >>= 2;
                     buffer[outputPtr++] = (byte) (decodedData >> 8);
                     buffer[outputPtr++] = (bytedecodedData;
                     continue;
                 }
             }
             // otherwise, our triplet is now complete
             decodedData = (decodedData << 6) | bits;
             buffer[outputPtr++] = (byte) (decodedData >> 16);
             buffer[outputPtr++] = (byte) (decodedData >> 8);
             buffer[outputPtr++] = (bytedecodedData;
         }
          = false;
         if (outputPtr > 0) {
             outputCount += outputPtr;
             out.write(buffer, 0, outputPtr);
         }
         return outputCount;
     }
 
     /*
    /**********************************************************
    /* Public API, traversal
    /**********************************************************
     */

    

Returns:
Next token from the stream, if any found, or null to indicate end-of-input
 
     @Override
     public JsonToken nextToken()
         throws IOExceptionJsonParseException
     {
          = ;
 
         /* First: field names are special -- we will always tokenize
          * (part of) value along with field name to simplify
          * state handling. If so, can and need to use secondary token:
          */
         if ( == .) {
             return _nextAfterName();
         }
         if () {
             _skipString(); // only strings can be partial
         }
         int i = _skipWSOrEnd();
         if (i < 0) { // end-of-input
             /* 19-Feb-2009, tatu: Should actually close/release things
              *    like input source, symbol table and recyclable buffers now.
              */
             close();
             return ( = null);
         }
 
         /* First, need to ensure we know the starting location of token
          * after skipping leading white space
          */
          =  +  - 1;
          = ;
          =  -  - 1;
 
         // finally: clear any data retained so far
          = null;
 
         // Closing scope?
         if (i == ) {
             if (!.inArray()) {
                 _reportMismatchedEndMarker(i'}');
             }
              = .getParent();
             return ( = .);
         }
         if (i == ) {
             if (!.inObject()) {
                 _reportMismatchedEndMarker(i']');
             }
              = .getParent();
             return ( = .);
         }
 
         // Nope: do we then expect a comma?
         if (.expectComma()) {
             if (i != ) {
                 _reportUnexpectedChar(i"was expecting comma to separate "+.getTypeDesc()+" entries");
             }
             i = _skipWS();
         }
 
         /* And should we now have a name? Always true for
          * Object contexts, since the intermediate 'expect-value'
          * state is never retained.
          */
         boolean inObject = .inObject();
         if (inObject) {
            // First, field name itself:
             String name = _parseFieldName(i);
             .setCurrentName(name);
              = .;
             i = _skipWS();
             if (i != ) {
                 _reportUnexpectedChar(i"was expecting a colon to separate field name and value");
             }
             i = _skipWS();
         }
 
         // Ok: we must have a value... what is it?
 
         JsonToken t;
 
         switch (i) {
         case :
              = true;
             t = .;
             break;
         case :
             if (!inObject) {
             }
             t = .;
             break;
         case :
             if (!inObject) {
             }
             t = .;
             break;
         case :
         case :
             // Error: neither is valid at this point; valid closers have
             // been handled earlier
             _reportUnexpectedChar(i"expected a value");
         case :
             _matchToken("true", 1);
             t = .;
             break;
         case :
             _matchToken("false", 1);
             t = .;
             break;
         case :
             _matchToken("null", 1);
             t = .;
             break;
 
         case :
             /* Should we have separate handling for plus? Although
              * it is not allowed per se, it may be erroneously used,
              * and could be indicate by a more specific error message.
              */
         case :
         case :
         case :
         case :
         case :
         case :
         case :
         case :
         case :
         case :
             t = parseNumberText(i);
             break;
         default:
             t = _handleUnexpectedValue(i);
             break;
         }
 
         if (inObject) {
              = t;
             return ;
         }
          = t;
         return t;
     }
 
     private JsonToken _nextAfterName()
     {
          = false// need to invalidate if it was copied
         JsonToken t = ;
          = null;
         // Also: may need to start new context?
         if (t == .) {
         } else if (t == .) {
         }
         return ( = t);
     }
 
     /*
     @Override
     public boolean nextFieldName(SerializableString str)
          throws IOException, JsonParseException
      */
 
     // note: identical to one in Utf8StreamParser
     @Override
     public String nextTextValue()
         throws IOExceptionJsonParseException
     {
         if ( == .) { // mostly copied from '_nextAfterName'
              = false;
             JsonToken t = ;
              = null;
              = t;
             if (t == .) {
                 if () {
                      = false;
                     _finishString();
                 }
                 return .contentsAsString();
             }
             if (t == .) {
             } else if (t == .) {
             }
             return null;
         }
         // !!! TODO: optimize this case as well
         return (nextToken() == .) ? getText() : null;
     }
 
     // note: identical to one in Utf8StreamParser
     @Override
     public int nextIntValue(int defaultValue)
         throws IOExceptionJsonParseException
     {
         if ( == .) {
              = false;
             JsonToken t = ;
              = null;
              = t;
             if (t == .) {
                 return getIntValue();
             }
             if (t == .) {
             } else if (t == .) {
             }
             return defaultValue;
         }
         // !!! TODO: optimize this case as well
         return (nextToken() == .) ? getIntValue() : defaultValue;
     }
 
     // note: identical to one in Utf8StreamParser
     @Override
     public long nextLongValue(long defaultValue)
         throws IOExceptionJsonParseException
     {
         if ( == .) { // mostly copied from '_nextAfterName'
              = false;
             JsonToken t = ;
              = null;
              = t;
             if (t == .) {
                 return getLongValue();
             }
             if (t == .) {
             } else if (t == .) {
             }
             return defaultValue;
         }
         // !!! TODO: optimize this case as well
         return (nextToken() == .) ? getLongValue() : defaultValue;
     }
 
     // note: identical to one in Utf8StreamParser
     @Override
     public Boolean nextBooleanValue()
         throws IOExceptionJsonParseException
     {
         if ( == .) { // mostly copied from '_nextAfterName'
              = false;
             JsonToken t = ;
              = null;
              = t;
             if (t == .) {
                 return .;
             }
             if (t == .) {
                 return .;
             }
             if (t == .) {
             } else if (t == .) {
             }
             return null;
         }
         switch (nextToken()) {
         case :
             return .;
         case :
             return .;
         }
         return null;
     }
     
     @Override
     public void close() throws IOException
     {
         super.close();
         .release();
     }
 
     /*
     /**********************************************************
     /* Internal methods, number parsing
     /* (note: in 1.8 and prior, part of "ReaderBasedNumericParser"
     /**********************************************************
      */

    
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.

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.

 
     protected JsonToken parseNumberText(int ch)
         throws IOExceptionJsonParseException
     {
         /* Although we will always be complete with respect to textual
          * representation (that is, all characters will be parsed),
          * actual conversion to a number is deferred. Thus, need to
          * note that no representations are valid yet
          */
         boolean negative = (ch == );
         int ptr = ;
         int startPtr = ptr-1; // to include sign/digit already read
         final int inputLen = ;
 
         dummy_loop:
         do { // dummy loop, to be able to break out
             if (negative) { // need to read the next digit
                 if (ptr >= ) {
                     break dummy_loop;
                 }
                 ch = [ptr++];
                 // First check: must have a digit to follow minus sign
                 if (ch >  || ch < ) {
                      = ptr;
                     return _handleInvalidNumberStart(chtrue);
                 }
                 /* (note: has been checked for non-negative already, in
                  * the dispatching code that determined it should be
                  * a numeric value)
                  */
             }
             // One special case, leading zero(es):
             if (ch == ) {
                 break dummy_loop;
             }
             
             /* First, let's see if the whole number is contained within
              * the input buffer unsplit. This should be the common case;
              * and to simplify processing, we will just reparse contents
              * in the alternative case (number split on buffer boundary)
              */
             
             int intLen = 1; // already got one
             
             // First let's get the obligatory integer part:
             
             int_loop:
             while (true) {
                 if (ptr >= ) {
                     break dummy_loop;
                 }
                 ch = (int[ptr++];
                 if (ch <  || ch > ) {
                     break int_loop;
                 }
                 ++intLen;
             }
 
             int fractLen = 0;
             
             // And then see if we get other parts
             if (ch == ) { // yes, fraction
                 fract_loop:
                 while (true) {
                     if (ptr >= inputLen) {
                         break dummy_loop;
                     }
                     ch = (int[ptr++];
                     if (ch <  || ch > ) {
                         break fract_loop;
                     }
                     ++fractLen;
                 }
                 // must be followed by sequence of ints, one minimum
                 if (fractLen == 0) {
                     reportUnexpectedNumberChar(ch"Decimal point not followed by a digit");
                 }
             }
 
             int expLen = 0;
             if (ch ==  || ch == ) { // and/or exponent
                 if (ptr >= inputLen) {
                     break dummy_loop;
                 }
                 // Sign indicator?
                 ch = (int[ptr++];
                 if (ch ==  || ch == ) { // yup, skip for now
                     if (ptr >= inputLen) {
                         break dummy_loop;
                     }
                     ch = (int[ptr++];
                 }
                 while (ch <=  && ch >= ) {
                     ++expLen;
                     if (ptr >= inputLen) {
                         break dummy_loop;
                     }
                     ch = (int[ptr++];
                 }
                 // must be followed by sequence of ints, one minimum
                 if (expLen == 0) {
                     reportUnexpectedNumberChar(ch"Exponent indicator not followed by a digit");
                 }
             }
 
             // Got it all: let's add to text buffer for parsing, access
             --ptr// need to push back following separator
              = ptr;
             int len = ptr-startPtr;
             .resetWithShared(startPtrlen);
             return reset(negativeintLenfractLenexpLen);
         } while (false);
 
          = negative ? (startPtr+1) : startPtr;
         return parseNumberText2(negative);
     }

    
Method called to parse a number, when the primary parse method has failed to parse it, due to it being split on buffer boundary. As a result code is very similar, except that it has to explicitly copy contents to the text buffer instead of just sharing the main input buffer.
 
     private JsonToken parseNumberText2(boolean negative)
         throws IOExceptionJsonParseException
     {
         char[] outBuf = .emptyAndGetCurrentSegment();
         int outPtr = 0;
 
         // Need to prepend sign?
         if (negative) {
             outBuf[outPtr++] = '-';
         }
 
         // This is the place to do leading-zero check(s) too:
         int intLen = 0;
         char c = ( < ) ? [++] : getNextChar("No digit following minus sign");
         if (c == '0') {
             c = _verifyNoLeadingZeroes();
         }
         boolean eof = false;
 
         // Ok, first the obligatory integer part:
         int_loop:
         while (c >= '0' && c <= '9') {
             ++intLen;
             if (outPtr >= outBuf.length) {
                 outBuf = .finishCurrentSegment();
                 outPtr = 0;
             }
             outBuf[outPtr++] = c;
             if ( >=  && !loadMore()) {
                 // EOF is legal for main level int values
                c = ;
                eof = true;
                break int_loop;
            }
            c = [++];
        }
        // 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++] = c;
            fract_loop:
            while (true) {
                if ( >=  && !loadMore()) {
                    eof = true;
                    break fract_loop;
                }
                c = [++];
                if (c <  || c > ) {
                    break fract_loop;
                }
                ++fractLen;
                if (outPtr >= outBuf.length) {
                    outBuf = .finishCurrentSegment();
                    outPtr = 0;
                }
                outBuf[outPtr++] = 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 = .finishCurrentSegment();
                outPtr = 0;
            }
            outBuf[outPtr++] = c;
            // Not optional, can require that we get one more char
            c = ( < ) ? [++]
                : getNextChar("expected a digit for number exponent");
            // Sign indicator?
            if (c == '-' || c == '+') {
                if (outPtr >= outBuf.length) {
                    outBuf = .finishCurrentSegment();
                    outPtr = 0;
                }
                outBuf[outPtr++] = c;
                // Likewise, non optional:
                c = ( < ) ? [++]
                    : getNextChar("expected a digit for number exponent");
            }
            exp_loop:
            while (c <=  && c >= ) {
                ++expLen;
                if (outPtr >= outBuf.length) {
                    outBuf = .finishCurrentSegment();
                    outPtr = 0;
                }
                outBuf[outPtr++] = c;
                if ( >=  && !loadMore()) {
                    eof = true;
                    break exp_loop;
                }
                c = [++];
            }
            // 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) {
            --;
        }
        .setCurrentLength(outPtr);
        // And there we have it!
        return reset(negativeintLenfractLenexpLen);
    }

    
Method called when we have seen one zero, and want to ensure it is not followed by another
    private char _verifyNoLeadingZeroes()
        throws IOExceptionJsonParseException
    {
        // Ok to have plain "0"
        if ( >=  && !loadMore()) {
            return '0';
        }
        char ch = [];
        // if not followed by a number (probably '.'); return zero as is, to be included
        if (ch < '0' || ch > '9') {
            return '0';
        }