blob: 531adb5757064c21fc1b93e113a81e86b8f805b9 [file] [log] [blame]
package com.fasterxml.jackson.core.util;
import java.io.IOException;
import java.util.*;
import com.fasterxml.jackson.core.*;
/**
* Helper class that can be used to sequence multiple physical
* {@link JsonParser}s to create a single logical sequence of
* tokens, as a single {@link JsonParser}.
*<p>
* Fairly simple use of {@link JsonParserDelegate}: only need
* to override {@link #nextToken} to handle transition
*/
public class JsonParserSequence extends JsonParserDelegate
{
/**
* Parsers other than the first one (which is initially assigned
* as delegate)
*/
protected final JsonParser[] _parsers;
/**
* Configuration that determines whether state of parsers is first verified
* to see if parser already points to a token (that is,
* {@link JsonParser#hasCurrentToken()} returns <code>true</code>), and if so
* that token is first return before {@link JsonParser#nextToken} is called.
* If enabled, this check is made; if disabled, no check is made and
* {@link JsonParser#nextToken} is always called for all parsers.
*<p>
* Default setting is <code>false</code> (for backwards-compatibility)
* so that possible existing token is not considered for parsers.
*
* @since 2.8
*/
protected final boolean _checkForExistingToken;
/**
* Index of the next parser in {@link #_parsers}.
*/
protected int _nextParserIndex;
/**
* Flag used to indicate that `JsonParser.nextToken()` should not be called,
* due to parser already pointing to a token.
*
* @since 2.8
*/
protected boolean _hasToken;
/*
*******************************************************
* Construction
*******************************************************
*/
@Deprecated // since 2.8
protected JsonParserSequence(JsonParser[] parsers) {
this(false, parsers);
}
/**
* @since 2.8
*/
protected JsonParserSequence(boolean checkForExistingToken, JsonParser[] parsers)
{
super(parsers[0]);
_checkForExistingToken = checkForExistingToken;
_hasToken = checkForExistingToken && delegate.hasCurrentToken();
_parsers = parsers;
_nextParserIndex = 1;
}
/**
* Method that will construct a parser (possibly a sequence) that
* contains all given sub-parsers.
* All parsers given are checked to see if they are sequences: and
* if so, they will be "flattened", that is, contained parsers are
* directly added in a new sequence instead of adding sequences
* within sequences. This is done to minimize delegation depth,
* ideally only having just a single level of delegation.
*/
public static JsonParserSequence createFlattened(boolean checkForExistingToken,
JsonParser first, JsonParser second)
{
if (!(first instanceof JsonParserSequence || second instanceof JsonParserSequence)) {
return new JsonParserSequence(checkForExistingToken,
new JsonParser[] { first, second });
}
ArrayList<JsonParser> p = new ArrayList<JsonParser>();
if (first instanceof JsonParserSequence) {
((JsonParserSequence) first).addFlattenedActiveParsers(p);
} else {
p.add(first);
}
if (second instanceof JsonParserSequence) {
((JsonParserSequence) second).addFlattenedActiveParsers(p);
} else {
p.add(second);
}
return new JsonParserSequence(checkForExistingToken,
p.toArray(new JsonParser[p.size()]));
}
/**
* @deprecated Since 2.8 use {@link #createFlattened(boolean, JsonParser, JsonParser)}
* instead
*/
@Deprecated // since 2.8
public static JsonParserSequence createFlattened(JsonParser first, JsonParser second) {
return createFlattened(false, first, second);
}
@SuppressWarnings("resource")
protected void addFlattenedActiveParsers(List<JsonParser> listToAddIn)
{
for (int i = _nextParserIndex-1, len = _parsers.length; i < len; ++i) {
JsonParser p = _parsers[i];
if (p instanceof JsonParserSequence) {
((JsonParserSequence) p).addFlattenedActiveParsers(listToAddIn);
} else {
listToAddIn.add(p);
}
}
}
/*
/*******************************************************
/* Overridden methods, needed: cases where default
/* delegation does not work
/*******************************************************
*/
@Override
public void close() throws IOException {
do { delegate.close(); } while (switchToNext());
}
@Override
public JsonToken nextToken() throws IOException
{
if (delegate == null) {
return null;
}
if (_hasToken) {
_hasToken = false;
return delegate.currentToken();
}
JsonToken t = delegate.nextToken();
if (t == null) {
return switchAndReturnNext();
}
return t;
}
/**
* Need to override, re-implement similar to how method defined in
* {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep
* state correct here.
*/
@Override
public JsonParser skipChildren() throws IOException
{
if ((delegate.currentToken() != JsonToken.START_OBJECT)
&& (delegate.currentToken() != JsonToken.START_ARRAY)) {
return this;
}
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) { // not ideal but for now, just return
return this;
}
if (t.isStructStart()) {
++open;
} else if (t.isStructEnd()) {
if (--open == 0) {
return this;
}
}
}
}
/*
/*******************************************************
/* Additional extended API
/*******************************************************
*/
/**
* Method that is most useful for debugging or testing;
* returns actual number of underlying parsers sequence
* was constructed with (nor just ones remaining active)
*/
public int containedParsersCount() {
return _parsers.length;
}
/*
/*******************************************************
/* Helper methods
/*******************************************************
*/
/**
* Method that will switch active delegate parser from the current one
* to the next parser in sequence, if there is another parser left:
* if so, the next parser will become the active delegate parser.
*
* @return True if switch succeeded; false otherwise
*
* @since 2.8
*/
protected boolean switchToNext()
{
if (_nextParserIndex < _parsers.length) {
delegate = _parsers[_nextParserIndex++];
return true;
}
return false;
}
protected JsonToken switchAndReturnNext() throws IOException
{
while (_nextParserIndex < _parsers.length) {
delegate = _parsers[_nextParserIndex++];
if (_checkForExistingToken && delegate.hasCurrentToken()) {
return delegate.getCurrentToken();
}
JsonToken t = delegate.nextToken();
if (t != null) {
return t;
}
}
return null;
}
}