Skip to content

Commit 0d8b14d

Browse files
committed
Rewrite of #296: require setting during construction to enable "check for existing current token"
1 parent 456ff3c commit 0d8b14d

File tree

3 files changed

+122
-42
lines changed

3 files changed

+122
-42
lines changed

release-notes/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ JSON library.
3939
#290: Add `JsonGenerator.canWriteFormattedNumbers()` for introspection
4040
#294: Add `JsonGenerator.writeFieldId(long)` method to support binary formats
4141
with non-String keys
42-
#296: JsonParserSequence skips a token on a switched Parser
42+
#296: `JsonParserSequence` skips a token on a switched Parser
4343
(reported by Kevin G)
4444
- Add `JsonParser.currentToken()` and `JsonParser.currentTokenId()` as replacements
4545
for `getCurrentToken()` and `getCurrentTokenId()`, respectively. Existing methods

src/main/java/com/fasterxml/jackson/core/util/JsonParserSequence.java

Lines changed: 86 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,56 @@ public class JsonParserSequence extends JsonParserDelegate
2020
* as delegate)
2121
*/
2222
protected final JsonParser[] _parsers;
23-
23+
24+
/**
25+
* Configuration that determines whether state of parsers is first verified
26+
* to see if parser already points to a token (that is,
27+
* {@link JsonParser#hasCurrentToken()} returns <code>true</code>), and if so
28+
* that token is first return before {@link JsonParser#nextToken} is called.
29+
* If enabled, this check is made; if disabled, no check is made and
30+
* {@link JsonParser#nextToken} is always called for all parsers.
31+
*<p>
32+
* Default setting is <code>false</code> (for backwards-compatibility)
33+
* so that possible existing token is not considered for parsers.
34+
*
35+
* @since 2.8
36+
*/
37+
protected final boolean _checkForExistingToken;
38+
2439
/**
2540
* Index of the next parser in {@link #_parsers}.
2641
*/
27-
protected int _nextParser;
42+
protected int _nextParserIndex;
2843

2944
/**
3045
* Flag used to indicate that `JsonParser.nextToken()` should not be called,
3146
* due to parser already pointing to a token.
3247
*
3348
* @since 2.8
3449
*/
35-
protected boolean _suppressNextToken;
36-
50+
protected boolean _hasToken;
51+
3752
/*
3853
*******************************************************
3954
* Construction
4055
*******************************************************
4156
*/
4257

43-
protected JsonParserSequence(JsonParser[] parsers)
58+
@Deprecated // since 2.8
59+
protected JsonParserSequence(JsonParser[] parsers) {
60+
this(false, parsers);
61+
}
62+
63+
/**
64+
* @since 2.8
65+
*/
66+
protected JsonParserSequence(boolean checkForExistingToken, JsonParser[] parsers)
4467
{
4568
super(parsers[0]);
46-
_suppressNextToken = delegate.hasCurrentToken();
69+
_checkForExistingToken = checkForExistingToken;
70+
_hasToken = checkForExistingToken && delegate.hasCurrentToken();
4771
_parsers = parsers;
48-
_nextParser = 1;
72+
_nextParserIndex = 1;
4973
}
5074

5175
/**
@@ -57,11 +81,12 @@ protected JsonParserSequence(JsonParser[] parsers)
5781
* within sequences. This is done to minimize delegation depth,
5882
* ideally only having just a single level of delegation.
5983
*/
60-
public static JsonParserSequence createFlattened(JsonParser first, JsonParser second)
84+
public static JsonParserSequence createFlattened(boolean checkForExistingToken,
85+
JsonParser first, JsonParser second)
6186
{
6287
if (!(first instanceof JsonParserSequence || second instanceof JsonParserSequence)) {
63-
// simple:
64-
return new JsonParserSequence(new JsonParser[] { first, second });
88+
return new JsonParserSequence(checkForExistingToken,
89+
new JsonParser[] { first, second });
6590
}
6691
ArrayList<JsonParser> p = new ArrayList<JsonParser>();
6792
if (first instanceof JsonParserSequence) {
@@ -74,29 +99,39 @@ public static JsonParserSequence createFlattened(JsonParser first, JsonParser se
7499
} else {
75100
p.add(second);
76101
}
77-
return new JsonParserSequence(p.toArray(new JsonParser[p.size()]));
102+
return new JsonParserSequence(checkForExistingToken,
103+
p.toArray(new JsonParser[p.size()]));
78104
}
79105

106+
/**
107+
* @deprecated Since 2.8 use {@link #createFlattened(boolean, JsonParser, JsonParser)}
108+
* instead
109+
*/
110+
@Deprecated // since 2.8
111+
public static JsonParserSequence createFlattened(JsonParser first, JsonParser second) {
112+
return createFlattened(false, first, second);
113+
}
114+
80115
@SuppressWarnings("resource")
81-
protected void addFlattenedActiveParsers(List<JsonParser> result)
116+
protected void addFlattenedActiveParsers(List<JsonParser> listToAddIn)
82117
{
83-
for (int i = _nextParser-1, len = _parsers.length; i < len; ++i) {
118+
for (int i = _nextParserIndex-1, len = _parsers.length; i < len; ++i) {
84119
JsonParser p = _parsers[i];
85120
if (p instanceof JsonParserSequence) {
86-
((JsonParserSequence) p).addFlattenedActiveParsers(result);
121+
((JsonParserSequence) p).addFlattenedActiveParsers(listToAddIn);
87122
} else {
88-
result.add(p);
123+
listToAddIn.add(p);
89124
}
90125
}
91126
}
92-
127+
93128
/*
94-
*******************************************************
95-
* Overridden methods, needed: cases where default
96-
* delegation does not work
97-
*******************************************************
129+
/*******************************************************
130+
/* Overridden methods, needed: cases where default
131+
/* delegation does not work
132+
/*******************************************************
98133
*/
99-
134+
100135
@Override
101136
public void close() throws IOException {
102137
do { delegate.close(); } while (switchToNext());
@@ -108,14 +143,13 @@ public JsonToken nextToken() throws IOException
108143
if (delegate == null) {
109144
return null;
110145
}
111-
if (_suppressNextToken) {
112-
_suppressNextToken = false;
113-
return delegate.currentToken();
146+
if (_hasToken) {
147+
_hasToken = false;
148+
return delegate.currentToken();
114149
}
115150
JsonToken t = delegate.nextToken();
116-
while ((t == null) && switchToNext()) {
117-
t = delegate.hasCurrentToken()
118-
? delegate.currentToken() : delegate.nextToken();
151+
if (t == null) {
152+
return switchAndReturnNext();
119153
}
120154
return t;
121155
}
@@ -134,27 +168,43 @@ public JsonToken nextToken() throws IOException
134168
public int containedParsersCount() {
135169
return _parsers.length;
136170
}
137-
171+
138172
/*
139173
/*******************************************************
140174
/* Helper methods
141175
/*******************************************************
142176
*/
143177

144178
/**
145-
* Method that will switch active parser from the current one
146-
* to next parser in sequence, if there is another parser left,
147-
* making this the new delegate. Old delegate is returned if
148-
* switch succeeds.
179+
* Method that will switch active delegate parser from the current one
180+
* to the next parser in sequence, if there is another parser left:
181+
* if so, the next parser will become the active delegate parser.
149182
*
150183
* @return True if switch succeeded; false otherwise
184+
*
185+
* @since 2.8
151186
*/
152187
protected boolean switchToNext()
153188
{
154-
if (_nextParser >= _parsers.length) {
155-
return false;
189+
if (_nextParserIndex < _parsers.length) {
190+
delegate = _parsers[_nextParserIndex++];
191+
return true;
192+
}
193+
return false;
194+
}
195+
196+
protected JsonToken switchAndReturnNext() throws IOException
197+
{
198+
while (_nextParserIndex < _parsers.length) {
199+
delegate = _parsers[_nextParserIndex++];
200+
if (_checkForExistingToken && delegate.hasCurrentToken()) {
201+
return delegate.getCurrentToken();
202+
}
203+
JsonToken t = delegate.nextToken();
204+
if (t != null) {
205+
return t;
206+
}
156207
}
157-
delegate = _parsers[_nextParser++];
158-
return true;
208+
return null;
159209
}
160210
}

src/test/java/com/fasterxml/jackson/core/json/ParserSequenceTest.java

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public void testSimple() throws Exception
1111
{
1212
JsonParser p1 = JSON_FACTORY.createParser("[ 1 ]");
1313
JsonParser p2 = JSON_FACTORY.createParser("[ 2 ]");
14-
JsonParserSequence seq = JsonParserSequence.createFlattened(p1, p2);
14+
JsonParserSequence seq = JsonParserSequence.createFlattened(false, p1, p2);
1515
assertEquals(2, seq.containedParsersCount());
1616

1717
assertFalse(p1.isClosed());
@@ -47,19 +47,49 @@ public void testSimple() throws Exception
4747
}
4848

4949
// for [jackson-core#296]
50-
public void testInitialized() throws Exception
50+
public void testInitializationDisabled() throws Exception
5151
{
52+
// // First, with old legacy settings
53+
5254
JsonParser p1 = JSON_FACTORY.createParser("1 2");
53-
JsonParser p2 = JSON_FACTORY.createParser("3 false");
54-
// consume '1', move to '2'
55+
JsonParser p2 = JSON_FACTORY.createParser("3 true");
5556
assertToken(JsonToken.VALUE_NUMBER_INT, p1.nextToken());
57+
assertEquals(1, p1.getIntValue());
58+
assertToken(JsonToken.VALUE_NUMBER_INT, p2.nextToken());
59+
assertEquals(3, p2.getIntValue());
60+
61+
// with legacy settings, will see neither '1' nor '3'
62+
63+
JsonParserSequence seq = JsonParserSequence.createFlattened(false, p1, p2);
64+
assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken());
65+
assertEquals(2, seq.getIntValue());
66+
assertToken(JsonToken.VALUE_TRUE, seq.nextToken());
67+
assertNull(seq.nextToken());
68+
seq.close();
69+
}
70+
71+
// for [jackson-core#296]
72+
public void testInitializationEnabled() throws Exception
73+
{
74+
// // and then with new "check for current":
75+
JsonParser p1 = JSON_FACTORY.createParser("1 2");
76+
JsonParser p2 = JSON_FACTORY.createParser("3 true");
5677
assertToken(JsonToken.VALUE_NUMBER_INT, p1.nextToken());
78+
assertEquals(1, p1.getIntValue());
79+
assertToken(JsonToken.VALUE_NUMBER_INT, p2.nextToken());
80+
assertEquals(3, p2.getIntValue());
5781

58-
JsonParserSequence seq = JsonParserSequence.createFlattened(p1, p2);
82+
// with new settings, both '1' and '3' will be visible
83+
84+
JsonParserSequence seq = JsonParserSequence.createFlattened(true, p1, p2);
85+
assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken());
86+
assertEquals(1, seq.getIntValue());
5987
assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken());
6088
assertEquals(2, seq.getIntValue());
6189
assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken());
6290
assertEquals(3, seq.getIntValue());
91+
assertToken(JsonToken.VALUE_TRUE, seq.nextToken());
92+
assertNull(seq.nextToken());
6393
seq.close();
6494
}
6595
}

0 commit comments

Comments
 (0)