All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.java.org.codehaus.jackson.impl.JsonParserBase Maven / Gradle / Ivy

Go to download

Jackson is a high-performance JSON processor (parser, generator)

There is a newer version: 0.9.5
Show newest version
package org.codehaus.jackson.impl;

import java.io.*;

import org.codehaus.jackson.*;
import org.codehaus.jackson.io.IOContext;
import org.codehaus.jackson.util.TextBuffer;

/**
 * Intermediate base class used by all Jackson {@link JsonParser}
 * implementations. Contains most common things that are independent
 * of actual underlying input source
 *
 * @author Tatu Saloranta
 */
public abstract class JsonParserBase
    extends JsonParser
{
    // Control chars:
    final static int INT_TAB = '\t';
    final static int INT_LF = '\n';
    final static int INT_CR = '\r';
    final static int INT_SPACE = 0x0020;

    // Markup
    final static int INT_LBRACKET = '[';
    final static int INT_RBRACKET = ']';
    final static int INT_LCURLY = '{';
    final static int INT_RCURLY = '}';
    final static int INT_QUOTE = '"';
    final static int INT_BACKSLASH = '\\';
    final static int INT_SLASH = '/';
    final static int INT_COLON = ':';

    // Letters we need
    final static int INT_b = 'b';
    final static int INT_f = 'f';
    final static int INT_n = 'n';
    final static int INT_r = 'r';
    final static int INT_t = 't';
    final static int INT_u = 'u';

    /*
    ////////////////////////////////////////////////////
    // Configuration
    ////////////////////////////////////////////////////
     */

    /**
     * I/O context for this reader. It handles buffer allocation
     * for the reader.
     */
    final protected IOContext mIOContext;

    /*
    ////////////////////////////////////////////////////
    // Current input data
    ////////////////////////////////////////////////////
     */

    protected JsonToken mCurrToken;

    // Note: type of actual buffer depends on sub-class, can't include

    /**
     * Pointer to next available character in buffer
     */
    protected int mInputPtr = 0;

    /**
     * Index of character after last available one in the buffer.
     */
    protected int mInputLast = 0;

    /*
    ////////////////////////////////////////////////////
    // Current input location information
    ////////////////////////////////////////////////////
     */

    /**
     * Number of characters that were contained in previous blocks
     * (blocks that were already processed prior to the current buffer).
     */
    protected long mCurrInputProcessed = 0L;

    /**
     * Current row location of current point in input buffer, starting
     * from 1
     */
    protected int mCurrInputRow = 1;

    /**
     * Current index of the first character of the current row in input
     * buffer. Needed to calculate column position, if necessary; benefit
     * of not having column itself is that this only has to be updated
     * once per line.
     */
    protected int mCurrInputRowStart = 0;

    /*
    ////////////////////////////////////////////////////
    // Information about starting location of event
    // Reader is pointing to; updated on-demand
    ////////////////////////////////////////////////////
     */

    // // // Location info at point when current token was started

    /**
     * Total number of characters read before start of current token.
     * For big (gigabyte-sized) sizes are possible, needs to be long,
     * unlike pointers and sizes related to in-memory buffers.
     */
    protected long mTokenInputTotal = 0; 

    /**
     * Input row on which current token starts, 1-based
     */
    protected int mTokenInputRow = 1;

    /**
     * Column on input row that current token starts; 0-based (although
     * in the end it'll be converted to 1-based)
     */
    protected int mTokenInputCol = 0;

    /*
    ////////////////////////////////////////////////////
    // Parsing state
    ////////////////////////////////////////////////////
     */

    protected JsonReadContext mParsingContext;

    /**
     * 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 mTokenIncomplete = false;

    /*
    ////////////////////////////////////////////////////
    // Buffer(s) for local name(s) and text content
    ////////////////////////////////////////////////////
     */
    /**
     * Buffer that contains contents of String values, including
     * field names if necessary (name split across boundary,
     * contains escape sequence, or access needed to char array)
     */
    protected final TextBuffer mTextBuffer;

    /**
     * Flag set to indicate whether field name parsed is available
     * from the text buffer or not.
     */
    protected boolean mFieldInBuffer = false;

    /*
    ////////////////////////////////////////////////////
    // Life-cycle
    ////////////////////////////////////////////////////
     */

    protected JsonParserBase(IOContext ctxt)
    {
        mIOContext = ctxt;
        mTextBuffer = ctxt.constructTextBuffer();
        mParsingContext = JsonReadContext.createRootContext(mTokenInputRow, mTokenInputCol);
    }

    /*
    ////////////////////////////////////////////////////
    // Abstract methods needed from sub-classes
    ////////////////////////////////////////////////////
     */

    //protected abstract void finishToken() throws IOException, JsonParseException;

    protected abstract void finishString() throws IOException, JsonParseException;

    /*
    ////////////////////////////////////////////////////
    // JsonParser impl
    ////////////////////////////////////////////////////
     */

    public abstract JsonToken nextToken()
        throws IOException, JsonParseException;

    public void skipChildren()
        throws IOException, JsonParseException
    {
        if (mCurrToken != JsonToken.START_OBJECT
            && mCurrToken != JsonToken.START_ARRAY) {
            return;
        }
        int open = 1;

        /* Since proper matching of start/end markers is handled
         * by nextToken(), we'll just count nesting levels here
         */
        while (true) {
            JsonToken t = nextToken();
            if (t == null) {
                handleEOF();
            }
            switch (t) {
            case START_OBJECT:
            case START_ARRAY:
                ++open;
                break;
            case END_OBJECT:
            case END_ARRAY:
                if (--open == 0) {
                    return;
                }
                break;
            }
        }
    }

    /**
     * @return Type of the token this parser currently points to,
     *   if any: null both before any tokens have been read, and
     *   after end-of-input has been encountered.
     */
    public JsonToken getCurrentToken()
    {
        return mCurrToken;
    }

    public boolean hasCurrentToken()
    {
        return mCurrToken != null;
    }

    /**
     * Method that can be called to get the name associated with
     * the current event. Will return null for all token types
     * except for {@link JsonToken#FIELD_NAME}.
     */
    public String getCurrentName()
        throws IOException, JsonParseException
    {
        return (mCurrToken == JsonToken.FIELD_NAME) ? mParsingContext.getCurrentName() : null;
    }

    public void close()
        throws IOException
    {
        closeInput();
        // Also, internal buffer(s) can now be released as well
        releaseBuffers();
    }

    public JsonReadContext getParsingContext()
    {
        return mParsingContext;
    }


    /**
     * Method that return the starting location of the current
     * token; that is, position of the first character from input
     * that starts the current token.
     */
    public JsonLocation getTokenLocation()
    {
        return new JsonLocation(mIOContext.getSourceReference(),
                                mTokenInputTotal,
                                mTokenInputRow, mTokenInputCol + 1);
    }

    /**
     * Method that returns location of the last processed character;
     * usually for error reporting purposes
     */
    public JsonLocation getCurrentLocation()
    {
        return new JsonLocation(mIOContext.getSourceReference(),
                                mCurrInputProcessed + mInputPtr - 1,
                                mCurrInputRow, mInputPtr - mCurrInputRowStart);
    }

    /*
    ////////////////////////////////////////////////////
    // Public API, access to token information, text
    ////////////////////////////////////////////////////
     */

    /**
     * Method for accessing textual representation of the current event;
     * if no current event (before first call to {@link #nextToken}, or
     * after encountering end-of-input), returns null.
     * Method can be called for any event.
     */
    public String getText()
        throws IOException, JsonParseException
    {
        if (mCurrToken != null) { // null only before/after document
            switch (mCurrToken) {
            case FIELD_NAME:
                return mParsingContext.getCurrentName();

            case VALUE_STRING:
                if (mTokenIncomplete) {
                    mTokenIncomplete = false;
                    finishString(); // only strings can be incomplete
                }
                // fall through
            case VALUE_NUMBER_INT:
            case VALUE_NUMBER_FLOAT:
                return mTextBuffer.contentsAsString();
                
            default:
                return mCurrToken.asString();
            }
        }
        return null;
    }

    public char[] getTextCharacters()
        throws IOException, JsonParseException
    {
        if (mCurrToken != null) { // null only before/after document
            switch (mCurrToken) {
                
            case FIELD_NAME:
                if (!mFieldInBuffer) {
                    mTextBuffer.resetWithString(mParsingContext.getCurrentName());
                    mFieldInBuffer = true;
                }
                return mTextBuffer.getTextBuffer();

            case VALUE_STRING:
                if (mTokenIncomplete) {
                    mTokenIncomplete = false;
                    finishString(); // only strings can be incomplete
                }
                // fall through
            case VALUE_NUMBER_INT:
            case VALUE_NUMBER_FLOAT:
                return mTextBuffer.getTextBuffer();
                
            default:
                return mCurrToken.asCharArray();
            }
        }
        return null;
    }

    public int getTextLength()
        throws IOException, JsonParseException
    {
        if (mCurrToken != null) { // null only before/after document
            switch (mCurrToken) {
                
            case FIELD_NAME:
                return mParsingContext.getCurrentName().length();
            case VALUE_STRING:
                if (mTokenIncomplete) {
                    mTokenIncomplete = false;
                    finishString(); // only strings can be incomplete
                }
                // fall through
            case VALUE_NUMBER_INT:
            case VALUE_NUMBER_FLOAT:
                return mTextBuffer.size();
                
            default:
                return mCurrToken.asCharArray().length;
            }
        }
        return 0;
    }

    public int getTextOffset()
        throws IOException, JsonParseException
    {
        // Most have offset of 0, only some may have other values:
        if (mCurrToken != null) {
            switch (mCurrToken) {
            case FIELD_NAME:
                return 0;
            case VALUE_STRING:
                if (mTokenIncomplete) {
                    mTokenIncomplete = false;
                    finishString(); // only strings can be incomplete
                }
                // fall through
            case VALUE_NUMBER_INT:
            case VALUE_NUMBER_FLOAT:
                return mTextBuffer.getTextOffset();
            }
        }
        return 0;
    }

    /*
    ////////////////////////////////////////////////////
    // Public low-level accessors
    ////////////////////////////////////////////////////
     */

    public final long getTokenCharacterOffset() { return mTokenInputTotal; }
    public final int getTokenLineNr() { return mTokenInputRow; }
    public final int getTokenColumnNr() { return mTokenInputCol; }

    /*
    ////////////////////////////////////////////////////
    // Low-level reading, linefeed handling
    ////////////////////////////////////////////////////
     */

    protected final void skipCR()
        throws IOException
    {
        if (mInputPtr < mInputLast || loadMore()) {
            ++mInputPtr;
        }
        ++mCurrInputRow;
        mCurrInputRowStart = mInputPtr;
    }

    protected final void skipLF()
        throws IOException
    {
        ++mCurrInputRow;
        mCurrInputRowStart = mInputPtr;
    }

    protected final void markLF() {
        ++mCurrInputRow;
        mCurrInputRowStart = mInputPtr;
    }

    protected final void markLF(int inputPtr) {
        ++mCurrInputRow;
        mCurrInputRowStart = inputPtr;
    }

    /*
    ////////////////////////////////////////////////////
    // Low-level reading, other
    ////////////////////////////////////////////////////
     */

    protected abstract boolean loadMore()
        throws IOException;

    protected final void loadMoreGuaranteed()
        throws IOException
    {
        if (!loadMore()) {
            reportInvalidEOF();
        }
    }

    protected abstract void closeInput()
        throws IOException;

    /**
     * Method called to release internal buffers owned by the base
     * reader. This may be called along with {@link #closeInput} (for
     * example, when explicitly closing this reader instance), or
     * separately (if need be).
     */
    protected void releaseBuffers()
        throws IOException
    {
        mTextBuffer.releaseBuffers();
    }

    /**
     * Method called when an EOF is encountered between tokens.
     * If so, it may be a legitimate EOF, but only iff there
     * is no open non-root context.
     */
    protected void handleEOF()
        throws JsonParseException
    {
        if (!mParsingContext.isRoot()) {
            reportInvalidEOF(": expected close marker for "+mParsingContext.getTypeDesc()+" (from "+mParsingContext.getStartLocation(mIOContext.getSourceReference())+")");
        }
    }

    /*
    ////////////////////////////////////////////////////
    // Error reporting
    ////////////////////////////////////////////////////
     */

    protected void reportUnexpectedChar(int ch, String comment)
        throws JsonParseException
    {
        String msg = "Unexpected character ("+getCharDesc(ch)+")";
        if (comment != null) {
            msg += ": "+comment;
        }
        reportError(msg);
    }

    protected void reportInvalidEOF()
        throws JsonParseException
    {
        reportInvalidEOF(" in "+mCurrToken);
    }

    protected void reportInvalidEOF(String msg)
        throws JsonParseException
    {
        reportError("Unexpected end-of-input"+msg);
    }

    protected void throwInvalidSpace(int i)
        throws JsonParseException
    {
        char c = (char) i;
        String msg = "Illegal character ("+getCharDesc(c)+"): only regular white space (\\r, \\n, \\t) is allowed between tokens";
        reportError(msg);
    }

    protected void throwUnquotedSpace(int i, String ctxtDesc)
        throws JsonParseException
    {
        char c = (char) i;
        String msg = "Illegal unquoted character ("+getCharDesc(c)+"): has to be escaped using backslash to be included in "+ctxtDesc;
        reportError(msg);
    }

    protected void reportMismatchedEndMarker(int actCh, char expCh)
        throws JsonParseException
    {
        String startDesc = ""+mParsingContext.getStartLocation(mIOContext.getSourceReference());
        reportError("Unexpected close marker '"+((char) actCh)+"': expected '"+expCh+"' (for "+mParsingContext.getTypeDesc()+" starting at "+startDesc+")");
    }

    /*
    ////////////////////////////////////////////////////
    // Error reporting, generic
    ////////////////////////////////////////////////////
     */

    protected static String getCharDesc(int ch)
    {
        char c = (char) ch;
        if (Character.isISOControl(c)) {
            return "(CTRL-CHAR, code "+ch+")";
        }
        if (ch > 255) {
            return "'"+c+"' (code "+ch+" / 0x"+Integer.toHexString(ch)+")";
        }
        return "'"+c+"' (code "+ch+")";
    }

    protected void reportError(String msg)
        throws JsonParseException
    {
        throw new JsonParseException(msg, getCurrentLocation());
    }

    protected void wrapError(String msg, Throwable t)
        throws JsonParseException
    {
        throw new JsonParseException(msg, getCurrentLocation(), t);
    }

    protected void throwInternal()
    {
        throw new RuntimeException("Internal error: this code path should never get executed");
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy