Skip to content

Commit daf88a8

Browse files
authored
Implement #1441: Add StreamReadFeature.CLEAR_CURRENT_TOKEN_ON_CLOSE (#1442)
1 parent c20ac86 commit daf88a8

File tree

6 files changed

+76
-7
lines changed

6 files changed

+76
-7
lines changed

release-notes/VERSION-2.x

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ a pure JSON library.
1717
2.20.0 (not yet released)
1818

1919
#1438: `ParserBase.close()` does not clear `_currToken`
20+
#1441: Add `StreamReadFeature.CLEAR_CURRENT_TOKEN_ON_CLOSE` (default: true)
2021
- Generate SBOMs [JSTEP-14]
2122

2223
2.19.1 (not yet released)

src/main/java/com/fasterxml/jackson/core/JsonParser.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,9 +405,15 @@ public enum Feature {
405405
*
406406
* @since 2.15
407407
*/
408-
USE_FAST_BIG_NUMBER_PARSER(false)
408+
USE_FAST_BIG_NUMBER_PARSER(false),
409409

410-
;
410+
/**
411+
* See {@link StreamReadFeature#CLEAR_CURRENT_TOKEN_ON_CLOSE}.
412+
*
413+
* @since 2.20
414+
*/
415+
CLEAR_CURRENT_TOKEN_ON_CLOSE(true)
416+
;
411417

412418
/**
413419
* Whether feature is enabled or disabled by default.

src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,19 @@ public enum StreamReadFeature
111111
*
112112
* @since 2.15
113113
*/
114-
USE_FAST_BIG_NUMBER_PARSER(JsonParser.Feature.USE_FAST_BIG_NUMBER_PARSER)
114+
USE_FAST_BIG_NUMBER_PARSER(JsonParser.Feature.USE_FAST_BIG_NUMBER_PARSER),
115115

116+
/**
117+
* Feature that determines whether parser will clear "current token"
118+
* (accessible via JsonParser#currentToken()) when it is closed (via
119+
* {@link JsonParser#close()}).
120+
*<p>
121+
* Feature is enabled by default.
122+
*
123+
* @since 2.20
124+
*/
125+
CLEAR_CURRENT_TOKEN_ON_CLOSE(JsonParser.Feature.CLEAR_CURRENT_TOKEN_ON_CLOSE),
126+
116127
;
117128

118129
/**

src/main/java/com/fasterxml/jackson/core/base/ParserBase.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,9 @@ public void overrideCurrentName(String name) {
404404
_inputPtr = Math.max(_inputPtr, _inputEnd);
405405
_closed = true;
406406
// 30-May-2025, tatu: was missing before 2.20
407-
_currToken = null;
407+
if (JsonParser.Feature.CLEAR_CURRENT_TOKEN_ON_CLOSE.enabledIn(_features)) {
408+
_currToken = null;
409+
}
408410
try {
409411
_closeInput();
410412
} finally {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,9 +354,9 @@ private void verifyCanonicalizationTestResult(JsonParser parser, boolean canonic
354354
if (canonicalize) {
355355
assertSame(field1, field2);
356356
} else {
357-
// n.b. It's possible that this may flake if a garbage collector with string deduplication
357+
// n.b. It's possible that this may flake if a garbage collector with string de-duplication
358358
// enabled is used. Such a failure is unlikely because younger GC generations are typically
359-
// not considered for deduplication due to high churn, but under heavy memory pressure it
359+
// not considered for de-duplication due to high churn, but under heavy memory pressure it
360360
// may be possible. I've left this comment in an attempt to simplify investigation in the
361361
// off-chance that such flakes eventually occur.
362362
assertNotSame(field1, field2);

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

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
import java.util.Collection;
66
import java.util.List;
77

8+
import org.junit.jupiter.api.Test;
89
import org.junit.jupiter.params.ParameterizedTest;
910
import org.junit.jupiter.params.provider.MethodSource;
1011

12+
import com.fasterxml.jackson.core.JUnit5TestBase;
1113
import com.fasterxml.jackson.core.JsonFactory;
1214
import com.fasterxml.jackson.core.JsonParser;
15+
import com.fasterxml.jackson.core.JsonToken;
16+
import com.fasterxml.jackson.core.StreamReadFeature;
1317
import com.fasterxml.jackson.core.io.SerializedString;
1418
import com.fasterxml.jackson.core.testsupport.MockDataInput;
1519

@@ -20,7 +24,9 @@
2024
* Tests asserts that using closed `JsonParser` doesn't cause ArrayIndexOutOfBoundsException
2125
* with `nextXxx()` methods but returns `null` as expected.
2226
*/
23-
public class JsonParserClosedCaseTest {
27+
public class JsonParserClosedCaseTest
28+
extends JUnit5TestBase
29+
{
2430
private static final JsonFactory JSON_F = new JsonFactory();
2531

2632
JsonParser parser;
@@ -73,6 +79,49 @@ void nullReturnedOnClosedParserOnNextValue(String parserName, JsonParser parser)
7379
assertNull(parser.nextValue());
7480
}
7581

82+
// [core#1441]: StreamReadFeature.CLEAR_CURRENT_TOKEN_ON_CLOSE
83+
@Test
84+
void clearCurrentTokenOnCloseEnabled() throws Exception {
85+
JsonFactory f = JsonFactory.builder()
86+
.enable(StreamReadFeature.CLEAR_CURRENT_TOKEN_ON_CLOSE)
87+
.build();
88+
_clearCurrentTokenOnCloseEnabled(f, MODE_INPUT_STREAM);
89+
_clearCurrentTokenOnCloseEnabled(f, MODE_INPUT_STREAM_THROTTLED);
90+
_clearCurrentTokenOnCloseEnabled(f, MODE_READER);
91+
_clearCurrentTokenOnCloseEnabled(f, MODE_DATA_INPUT);
92+
93+
}
94+
95+
@Test
96+
void clearCurrentTokenOnCloseDisabled() throws Exception {
97+
JsonFactory f = JsonFactory.builder()
98+
.disable(StreamReadFeature.CLEAR_CURRENT_TOKEN_ON_CLOSE)
99+
.build();
100+
_clearCurrentTokenOnCloseDisabled(f, MODE_INPUT_STREAM);
101+
_clearCurrentTokenOnCloseDisabled(f, MODE_INPUT_STREAM_THROTTLED);
102+
_clearCurrentTokenOnCloseDisabled(f, MODE_READER);
103+
_clearCurrentTokenOnCloseDisabled(f, MODE_DATA_INPUT);
104+
105+
}
106+
107+
private void _clearCurrentTokenOnCloseEnabled(JsonFactory f, int mode) throws Exception
108+
{
109+
try (JsonParser p = f.createParser("[ 1 ]")) {
110+
assertToken(JsonToken.START_ARRAY, p.nextToken());
111+
p.close();
112+
assertNull(p.currentToken());
113+
}
114+
}
115+
116+
private void _clearCurrentTokenOnCloseDisabled(JsonFactory f, int mode) throws Exception
117+
{
118+
try (JsonParser p = f.createParser("[ 1 ]")) {
119+
assertToken(JsonToken.START_ARRAY, p.nextToken());
120+
p.close();
121+
assertToken(JsonToken.START_ARRAY, p.currentToken());
122+
}
123+
}
124+
76125
private static Collection<Object[]> closeParsers(JsonParser... parsersToClose) throws IOException {
77126
List<Object[]> list = new ArrayList<>();
78127
for (JsonParser p : parsersToClose) {

0 commit comments

Comments
 (0)