| package com.fasterxml.jackson.core.json.async; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| |
| import com.fasterxml.jackson.core.JsonToken; |
| import com.fasterxml.jackson.core.async.ByteArrayFeeder; |
| import com.fasterxml.jackson.core.async.NonBlockingInputFeeder; |
| import com.fasterxml.jackson.core.io.IOContext; |
| import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer; |
| import com.fasterxml.jackson.core.util.VersionUtil; |
| |
| public class NonBlockingJsonParser |
| extends NonBlockingJsonParserBase |
| implements ByteArrayFeeder |
| { |
| /* |
| /********************************************************************** |
| /* Input source config |
| /********************************************************************** |
| */ |
| |
| /** |
| * This buffer is actually provided via {@link NonBlockingInputFeeder} |
| */ |
| protected byte[] _inputBuffer = NO_BYTES; |
| |
| /** |
| * In addition to current buffer pointer, and end pointer, |
| * we will also need to know number of bytes originally |
| * contained. This is needed to correctly update location |
| * information when the block has been completed. |
| */ |
| protected int _origBufferLen; |
| |
| // And from ParserBase: |
| // protected int _inputPtr; |
| // protected int _inputEnd; |
| |
| /* |
| /********************************************************************** |
| /* Life-cycle |
| /********************************************************************** |
| */ |
| |
| public NonBlockingJsonParser(IOContext ctxt, int parserFeatures, |
| ByteQuadsCanonicalizer sym) |
| { |
| super(ctxt, parserFeatures, sym); |
| } |
| |
| /* |
| /********************************************************************** |
| /* AsyncInputFeeder impl |
| /********************************************************************** |
| */ |
| |
| @Override |
| public ByteArrayFeeder getNonBlockingInputFeeder() { |
| return this; |
| } |
| |
| @Override |
| public final boolean needMoreInput() { |
| return (_inputPtr >=_inputEnd) && !_endOfInput; |
| } |
| |
| @Override |
| public void feedInput(byte[] buf, int start, int end) throws IOException |
| { |
| // Must not have remaining input |
| if (_inputPtr < _inputEnd) { |
| _reportError("Still have %d undecoded bytes, should not call 'feedInput'", _inputEnd - _inputPtr); |
| } |
| if (end < start) { |
| _reportError("Input end (%d) may not be before start (%d)", end, start); |
| } |
| // and shouldn't have been marked as end-of-input |
| if (_endOfInput) { |
| _reportError("Already closed, can not feed more input"); |
| } |
| // Time to update pointers first |
| _currInputProcessed += _origBufferLen; |
| |
| // And then update buffer settings |
| _inputBuffer = buf; |
| _inputPtr = start; |
| _inputEnd = end; |
| _origBufferLen = end - start; |
| } |
| |
| @Override |
| public void endOfInput() { |
| _endOfInput = true; |
| } |
| |
| /* |
| /********************************************************************** |
| /* Abstract methods/overrides from JsonParser |
| /********************************************************************** |
| */ |
| |
| /* Implementing these methods efficiently for non-blocking cases would |
| * be complicated; so for now let's just use the default non-optimized |
| * implementation |
| */ |
| |
| // public boolean nextFieldName(SerializableString str) throws IOException |
| // public String nextTextValue() throws IOException |
| // public int nextIntValue(int defaultValue) throws IOException |
| // public long nextLongValue(long defaultValue) throws IOException |
| // public Boolean nextBooleanValue() throws IOException |
| |
| @Override |
| public int releaseBuffered(OutputStream out) throws IOException { |
| int avail = _inputEnd - _inputPtr; |
| if (avail > 0) { |
| out.write(_inputBuffer, _inputPtr, avail); |
| } |
| return avail; |
| } |
| |
| /* |
| /********************************************************************** |
| /* Main-level decoding |
| /********************************************************************** |
| */ |
| |
| @Override |
| public JsonToken nextToken() throws IOException |
| { |
| // First: regardless of where we really are, need at least one more byte; |
| // can simplify some of the checks by short-circuiting right away |
| if (_inputPtr >= _inputEnd) { |
| if (_closed) { |
| return null; |
| } |
| // note: if so, do not even bother changing state |
| if (_endOfInput) { // except for this special case |
| return _eofAsNextToken(); |
| } |
| return JsonToken.NOT_AVAILABLE; |
| } |
| // in the middle of tokenization? |
| if (_currToken == JsonToken.NOT_AVAILABLE) { |
| return _finishToken(); |
| } |
| |
| // No: fresh new token; may or may not have existing one |
| _numTypesValid = NR_UNKNOWN; |
| // _tokenInputTotal = _currInputProcessed + _inputPtr; |
| // also: clear any data retained so far |
| _binaryValue = null; |
| int ch = _inputBuffer[_inputPtr++]; |
| |
| switch (_majorState) { |
| case MAJOR_INITIAL: |
| // TODO: Bootstrapping? BOM? |
| |
| case MAJOR_ROOT: |
| return _startValue(ch); |
| |
| case MAJOR_OBJECT_FIELD: // field or end-object |
| // expect name |
| return _startFieldName(ch); |
| |
| case MAJOR_OBJECT_VALUE: |
| case MAJOR_ARRAY_ELEMENT: // element or end-array |
| return _startValue(ch); |
| |
| default: |
| } |
| VersionUtil.throwInternal(); |
| return null; |
| } |
| |
| /** |
| * Method called when a (scalar) value type has been detected, but not all of |
| * contents have been decoded due to incomplete input available. |
| */ |
| protected final JsonToken _finishToken() throws IOException |
| { |
| // NOTE: caller ensures availability of at least one byte |
| |
| switch (_minorState) { |
| } |
| return null; |
| } |
| |
| /* |
| /********************************************************************** |
| /* Second-level decoding, root level |
| /********************************************************************** |
| */ |
| |
| /** |
| * Helper method called to detect type of a value token (at any level), and possibly |
| * decode it if contained in input buffer. |
| * Note that possible header has been ruled out by caller and is not checked here. |
| */ |
| private final JsonToken _startValue(int ch) throws IOException |
| { |
| return null; |
| } |
| |
| /* |
| /********************************************************************** |
| /* Second-level decoding, Name decoding |
| /********************************************************************** |
| */ |
| |
| /** |
| * Method that handles initial token type recognition for token |
| * that has to be either FIELD_NAME or END_OBJECT. |
| */ |
| protected final JsonToken _startFieldName(int ch) throws IOException |
| { |
| return null; |
| } |
| } |