| package org.codehaus.jackson.impl; |
| |
| import java.io.*; |
| |
| import org.codehaus.jackson.io.IOContext; |
| |
| /** |
| * This is a simple low-level input reader base class, used by |
| * JSON parser. It is used when underlying input source is |
| * a byte stream such as {@link InputStream}. |
| * The reason for sub-classing (over composition) |
| * is due to need for direct access to low-level byte buffers |
| * and positions. |
| * |
| * @author Tatu Saloranta |
| * |
| * @deprecated Since 1.9, sub-classes should just embed code from here |
| */ |
| @Deprecated |
| public abstract class StreamBasedParserBase |
| extends JsonParserBase |
| { |
| /* |
| /********************************************************** |
| /* Configuration |
| /********************************************************** |
| */ |
| |
| /** |
| * Input stream that can be used for reading more content, if one |
| * in use. May be null, if input comes just as a full buffer, |
| * or if the stream has been closed. |
| */ |
| protected InputStream _inputStream; |
| |
| /* |
| /********************************************************** |
| /* Current input data |
| /********************************************************** |
| */ |
| |
| /** |
| * Current buffer from which data is read; generally data is read into |
| * buffer from input source, but in some cases pre-loaded buffer |
| * is handed to the parser. |
| */ |
| protected byte[] _inputBuffer; |
| |
| /** |
| * Flag that indicates whether the input buffer is recycable (and |
| * needs to be returned to recycler once we are done) or not. |
| *<p> |
| * If it is not, it also means that parser can NOT modify underlying |
| * buffer. |
| */ |
| protected boolean _bufferRecyclable; |
| |
| /* |
| /********************************************************** |
| /* Life-cycle |
| /********************************************************** |
| */ |
| |
| protected StreamBasedParserBase(IOContext ctxt, int features, |
| InputStream in, byte[] inputBuffer, int start, int end, |
| boolean bufferRecyclable) |
| { |
| super(ctxt, features); |
| _inputStream = in; |
| _inputBuffer = inputBuffer; |
| _inputPtr = start; |
| _inputEnd = end; |
| _bufferRecyclable = bufferRecyclable; |
| } |
| |
| /* |
| /********************************************************** |
| /* Overrides |
| /********************************************************** |
| */ |
| |
| @Override |
| public int releaseBuffered(OutputStream out) throws IOException |
| { |
| int count = _inputEnd - _inputPtr; |
| if (count < 1) { |
| return 0; |
| } |
| // let's just advance ptr to end |
| int origPtr = _inputPtr; |
| out.write(_inputBuffer, origPtr, count); |
| return count; |
| } |
| |
| @Override |
| public Object getInputSource() { |
| return _inputStream; |
| } |
| |
| /* |
| /********************************************************** |
| /* Low-level reading, other |
| /********************************************************** |
| */ |
| |
| @Override |
| protected final boolean loadMore() |
| throws IOException |
| { |
| _currInputProcessed += _inputEnd; |
| _currInputRowStart -= _inputEnd; |
| |
| if (_inputStream != null) { |
| int count = _inputStream.read(_inputBuffer, 0, _inputBuffer.length); |
| if (count > 0) { |
| _inputPtr = 0; |
| _inputEnd = count; |
| return true; |
| } |
| // End of input |
| _closeInput(); |
| // Should never return 0, so let's fail |
| if (count == 0) { |
| throw new IOException("InputStream.read() returned 0 characters when trying to read "+_inputBuffer.length+" bytes"); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Helper method that will try to load at least specified number bytes in |
| * input buffer, possible moving existing data around if necessary |
| * |
| * @since 1.6 |
| */ |
| protected final boolean _loadToHaveAtLeast(int minAvailable) |
| throws IOException |
| { |
| // No input stream, no leading (either we are closed, or have non-stream input source) |
| if (_inputStream == null) { |
| return false; |
| } |
| // Need to move remaining data in front? |
| int amount = _inputEnd - _inputPtr; |
| if (amount > 0 && _inputPtr > 0) { |
| _currInputProcessed += _inputPtr; |
| _currInputRowStart -= _inputPtr; |
| System.arraycopy(_inputBuffer, _inputPtr, _inputBuffer, 0, amount); |
| _inputEnd = amount; |
| } else { |
| _inputEnd = 0; |
| } |
| _inputPtr = 0; |
| while (_inputEnd < minAvailable) { |
| int count = _inputStream.read(_inputBuffer, _inputEnd, _inputBuffer.length - _inputEnd); |
| if (count < 1) { |
| // End of input |
| _closeInput(); |
| // Should never return 0, so let's fail |
| if (count == 0) { |
| throw new IOException("InputStream.read() returned 0 characters when trying to read "+amount+" bytes"); |
| } |
| return false; |
| } |
| _inputEnd += count; |
| } |
| return true; |
| } |
| |
| @Override |
| protected void _closeInput() throws IOException |
| { |
| /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close() |
| * on the underlying InputStream, unless we "own" it, or auto-closing |
| * feature is enabled. |
| */ |
| if (_inputStream != null) { |
| if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_SOURCE)) { |
| _inputStream.close(); |
| } |
| _inputStream = null; |
| } |
| } |
| |
| @Override |
| protected void _releaseBuffers() throws IOException |
| { |
| super._releaseBuffers(); |
| if (_bufferRecyclable) { |
| byte[] buf = _inputBuffer; |
| if (buf != null) { |
| _inputBuffer = null; |
| _ioContext.releaseReadIOBuffer(buf); |
| } |
| } |
| } |
| } |