Rewrite of #296: require setting during construction to enable "check for existing current token"
diff --git a/src/main/java/com/fasterxml/jackson/core/util/JsonParserSequence.java b/src/main/java/com/fasterxml/jackson/core/util/JsonParserSequence.java
index 0c9c4eb..5b75047 100644
--- a/src/main/java/com/fasterxml/jackson/core/util/JsonParserSequence.java
+++ b/src/main/java/com/fasterxml/jackson/core/util/JsonParserSequence.java
@@ -20,11 +20,26 @@
* 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 _nextParser;
+ protected int _nextParserIndex;
/**
* Flag used to indicate that `JsonParser.nextToken()` should not be called,
@@ -32,20 +47,29 @@
*
* @since 2.8
*/
- protected boolean _suppressNextToken;
-
+ protected boolean _hasToken;
+
/*
*******************************************************
* Construction
*******************************************************
*/
- protected JsonParserSequence(JsonParser[] parsers)
+ @Deprecated // since 2.8
+ protected JsonParserSequence(JsonParser[] parsers) {
+ this(false, parsers);
+ }
+
+ /**
+ * @since 2.8
+ */
+ protected JsonParserSequence(boolean checkForExistingToken, JsonParser[] parsers)
{
super(parsers[0]);
- _suppressNextToken = delegate.hasCurrentToken();
+ _checkForExistingToken = checkForExistingToken;
+ _hasToken = checkForExistingToken && delegate.hasCurrentToken();
_parsers = parsers;
- _nextParser = 1;
+ _nextParserIndex = 1;
}
/**
@@ -57,11 +81,12 @@
* within sequences. This is done to minimize delegation depth,
* ideally only having just a single level of delegation.
*/
- public static JsonParserSequence createFlattened(JsonParser first, JsonParser second)
+ public static JsonParserSequence createFlattened(boolean checkForExistingToken,
+ JsonParser first, JsonParser second)
{
if (!(first instanceof JsonParserSequence || second instanceof JsonParserSequence)) {
- // simple:
- return new JsonParserSequence(new JsonParser[] { first, second });
+ return new JsonParserSequence(checkForExistingToken,
+ new JsonParser[] { first, second });
}
ArrayList<JsonParser> p = new ArrayList<JsonParser>();
if (first instanceof JsonParserSequence) {
@@ -74,29 +99,39 @@
} else {
p.add(second);
}
- return new JsonParserSequence(p.toArray(new JsonParser[p.size()]));
+ 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> result)
+ protected void addFlattenedActiveParsers(List<JsonParser> listToAddIn)
{
- for (int i = _nextParser-1, len = _parsers.length; i < len; ++i) {
+ for (int i = _nextParserIndex-1, len = _parsers.length; i < len; ++i) {
JsonParser p = _parsers[i];
if (p instanceof JsonParserSequence) {
- ((JsonParserSequence) p).addFlattenedActiveParsers(result);
+ ((JsonParserSequence) p).addFlattenedActiveParsers(listToAddIn);
} else {
- result.add(p);
+ listToAddIn.add(p);
}
}
}
-
+
/*
- *******************************************************
- * Overridden methods, needed: cases where default
- * delegation does not work
- *******************************************************
+ /*******************************************************
+ /* Overridden methods, needed: cases where default
+ /* delegation does not work
+ /*******************************************************
*/
-
+
@Override
public void close() throws IOException {
do { delegate.close(); } while (switchToNext());
@@ -108,14 +143,13 @@
if (delegate == null) {
return null;
}
- if (_suppressNextToken) {
- _suppressNextToken = false;
- return delegate.currentToken();
+ if (_hasToken) {
+ _hasToken = false;
+ return delegate.currentToken();
}
JsonToken t = delegate.nextToken();
- while ((t == null) && switchToNext()) {
- t = delegate.hasCurrentToken()
- ? delegate.currentToken() : delegate.nextToken();
+ if (t == null) {
+ return switchAndReturnNext();
}
return t;
}
@@ -134,7 +168,7 @@
public int containedParsersCount() {
return _parsers.length;
}
-
+
/*
/*******************************************************
/* Helper methods
@@ -142,19 +176,35 @@
*/
/**
- * Method that will switch active parser from the current one
- * to next parser in sequence, if there is another parser left,
- * making this the new delegate. Old delegate is returned if
- * switch succeeds.
+ * 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 (_nextParser >= _parsers.length) {
- return false;
+ if (_nextParserIndex < _parsers.length) {
+ delegate = _parsers[_nextParserIndex++];
+ return true;
}
- delegate = _parsers[_nextParser++];
- 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;
}
}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/ParserSequenceTest.java b/src/test/java/com/fasterxml/jackson/core/json/ParserSequenceTest.java
index f555186..85363f4 100644
--- a/src/test/java/com/fasterxml/jackson/core/json/ParserSequenceTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/json/ParserSequenceTest.java
@@ -11,7 +11,7 @@
{
JsonParser p1 = JSON_FACTORY.createParser("[ 1 ]");
JsonParser p2 = JSON_FACTORY.createParser("[ 2 ]");
- JsonParserSequence seq = JsonParserSequence.createFlattened(p1, p2);
+ JsonParserSequence seq = JsonParserSequence.createFlattened(false, p1, p2);
assertEquals(2, seq.containedParsersCount());
assertFalse(p1.isClosed());
@@ -47,19 +47,49 @@
}
// for [jackson-core#296]
- public void testInitialized() throws Exception
+ public void testInitializationDisabled() throws Exception
{
- JsonParser p1 = JSON_FACTORY.createParser("1 2");
- JsonParser p2 = JSON_FACTORY.createParser("3 false");
- // consume '1', move to '2'
- assertToken(JsonToken.VALUE_NUMBER_INT, p1.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_INT, p1.nextToken());
+ // // First, with old legacy settings
- JsonParserSequence seq = JsonParserSequence.createFlattened(p1, p2);
+ JsonParser p1 = JSON_FACTORY.createParser("1 2");
+ JsonParser p2 = JSON_FACTORY.createParser("3 true");
+ assertToken(JsonToken.VALUE_NUMBER_INT, p1.nextToken());
+ assertEquals(1, p1.getIntValue());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p2.nextToken());
+ assertEquals(3, p2.getIntValue());
+
+ // with legacy settings, will see neither '1' nor '3'
+
+ JsonParserSequence seq = JsonParserSequence.createFlattened(false, p1, p2);
+ assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken());
+ assertEquals(2, seq.getIntValue());
+ assertToken(JsonToken.VALUE_TRUE, seq.nextToken());
+ assertNull(seq.nextToken());
+ seq.close();
+ }
+
+ // for [jackson-core#296]
+ public void testInitializationEnabled() throws Exception
+ {
+ // // and then with new "check for current":
+ JsonParser p1 = JSON_FACTORY.createParser("1 2");
+ JsonParser p2 = JSON_FACTORY.createParser("3 true");
+ assertToken(JsonToken.VALUE_NUMBER_INT, p1.nextToken());
+ assertEquals(1, p1.getIntValue());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p2.nextToken());
+ assertEquals(3, p2.getIntValue());
+
+ // with new settings, both '1' and '3' will be visible
+
+ JsonParserSequence seq = JsonParserSequence.createFlattened(true, p1, p2);
+ assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken());
+ assertEquals(1, seq.getIntValue());
assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken());
assertEquals(2, seq.getIntValue());
assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken());
assertEquals(3, seq.getIntValue());
+ assertToken(JsonToken.VALUE_TRUE, seq.nextToken());
+ assertNull(seq.nextToken());
seq.close();
}
}