Merge branch '2.8'
diff --git a/.travis.yml b/.travis.yml
index 75db9cc..73b3221 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,8 +14,8 @@
branches:
only:
- master
+ - "2.8"
- "2.7"
- - "2.6"
env:
global:
diff --git a/README.md b/README.md
index b93f417..413657c 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,7 @@
[![Build Status](https://travis-ci.org/FasterXML/jackson-core.svg?branch=master)](https://travis-ci.org/FasterXML/jackson-core) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.jackson.core/jackson-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.jackson.core/jackson-core)
[![Javadoc](https://javadoc-emblem.rhcloud.com/doc/com.fasterxml.jackson.core/jackson-core/badge.svg)](http://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-core)
+[![Coverage Status](https://coveralls.io/repos/github/FasterXML/jackson-core/badge.svg?branch=master)](https://coveralls.io/github/FasterXML/jackson-core?branch=master)
# Get it!
diff --git a/pom.xml b/pom.xml
index 0869333..53ca4bf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,13 +3,13 @@
<parent>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-parent</artifactId>
- <version>2.8</version>
+ <version>2.9-rc1-SNAPSHOT</version>
</parent>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<name>Jackson-core</name>
- <version>2.8.6-SNAPSHOT</version>
+ <version>2.9.0-SNAPSHOT</version>
<packaging>bundle</packaging>
<description>Core Jackson abstractions, basic JSON streaming API implementation</description>
<inceptionYear>2008</inceptionYear>
@@ -23,7 +23,7 @@
</scm>
<properties>
- <!-- 29-Apr-2016, tatu: Retain Java6/JDK1.6 compatibility for streaming for Jackson 2.8 -->
+ <!-- 16-Sep-2016, tatu: Retain Java6/JDK1.6 compatibility for streaming for Jackson 2.x -->
<javac.src.version>1.6</javac.src.version>
<javac.target.version>1.6</javac.target.version>
diff --git a/release-notes/CREDITS b/release-notes/CREDITS
index 4aaca26..fd88262 100644
--- a/release-notes/CREDITS
+++ b/release-notes/CREDITS
@@ -107,3 +107,14 @@
Kevin Gallardo (newkek@github)
* Reported #296: JsonParserSequence skips a token on a switched Parser
(2.8.0)
+
+Alex Yursha (AlexYursha@github)
+ * Contributed #312: Add `JsonProcessingException.clearLocation()` to allow clearing
+ possibly security-sensitive information
+ (2.9.0)
+
+Brad Hess (bdhess@github)
+ * Contributed #323: Add `JsonParser.ALLOW_TRAILING_COMMA` to work for Arrays and Objects
+ (2.9.0)
+ * Reported #325: `DataInput` backed parser should handle `EOFException` at end of doc
+ (2.9.0)
diff --git a/release-notes/VERSION b/release-notes/VERSION
index d902d17..7d53acf 100644
--- a/release-notes/VERSION
+++ b/release-notes/VERSION
@@ -17,6 +17,13 @@
2.9.0 (not yet released)
#304: Optimize `NumberOutput.outputLong()` method
+#312: Add `JsonProcessingException.clearLocation()` to allow clearing
+ possibly security-sensitive information
+ (contributed by Alex Y)
+#323: Add `JsonParser.ALLOW_TRAILING_COMMA` to work for Arrays and Objects
+ (contributed by Brad H)
+#325: `DataInput` backed parser should handle `EOFException` at end of doc
+ (reported by Brad H)
2.8.6 (not yet released)
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonParser.java b/src/main/java/com/fasterxml/jackson/core/JsonParser.java
index 92f16de..36c7df5 100644
--- a/src/main/java/com/fasterxml/jackson/core/JsonParser.java
+++ b/src/main/java/com/fasterxml/jackson/core/JsonParser.java
@@ -227,7 +227,30 @@
*
* @since 2.8
*/
- ALLOW_MISSING_VALUES(false)
+ ALLOW_MISSING_VALUES(false),
+
+ /**
+ * Feature that determines whether {@link JsonParser} will allow for a single trailing
+ * comma following the final value (in an Array) or member (in an Object). These commas
+ * will simply be ignored.
+ * <p>
+ * For example, when this feature is enabled, <code>[true,true,]</code> is equivalent to
+ * <code>[true, true]</code> and <code>{"a": true,}</code> is equivalent to
+ * <code>{"a": true}</code>.
+ * <p>
+ * When combined with <code>ALLOW_MISSING_VALUES</code>, this feature takes priority, and
+ * the final trailing comma in an array declaration does not imply a missing
+ * (<code>null</code>) value. For example, when both <code>ALLOW_MISSING_VALUES</code>
+ * and <code>ALLOW_TRAILING_COMMA</code> are enabled, <code>[true,true,]</code> is
+ * equivalent to <code>[true, true]</code>, and <code>[true,true,,]</code> is equivalent to
+ * <code>[true, true, null]</code>.
+ * <p>
+ * Since the JSON specification does not permit trailing commas, this is a non-standard
+ * feature, and as such disabled by default.
+ *
+ * @since 2.9
+ */
+ ALLOW_TRAILING_COMMA(false)
;
/**
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonPointer.java b/src/main/java/com/fasterxml/jackson/core/JsonPointer.java
index f927336..6681992 100644
--- a/src/main/java/com/fasterxml/jackson/core/JsonPointer.java
+++ b/src/main/java/com/fasterxml/jackson/core/JsonPointer.java
@@ -20,6 +20,13 @@
public class JsonPointer
{
/**
+ * Character used to separate segments.
+ *
+ * @since 2.9
+ */
+ public final static char SEPARATOR = '/';
+
+ /**
* Marker instance used to represent segment that matches current
* node or position (that is, returns true for
* {@link #matches()}).
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonProcessingException.java b/src/main/java/com/fasterxml/jackson/core/JsonProcessingException.java
index 44be053..23599b1 100644
--- a/src/main/java/com/fasterxml/jackson/core/JsonProcessingException.java
+++ b/src/main/java/com/fasterxml/jackson/core/JsonProcessingException.java
@@ -54,6 +54,15 @@
public JsonLocation getLocation() { return _location; }
/**
+ * Method that allows to remove context information from this exception's message.
+ * Useful when you are parsing security-sensitive data and don't want original data excerpts
+ * to be present in Jackson parser error messages.
+ *
+ * @since 2.9
+ */
+ public void clearLocation() { _location = null; }
+
+ /**
* Method that allows accessing the original "message" argument,
* without additional decorations (like location information)
* that overridden {@link #getMessage} adds.
diff --git a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java
index dc80f62..7033d84 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java
@@ -19,6 +19,8 @@
public class ReaderBasedJsonParser // final in 2.3, earlier
extends ParserBase
{
+ protected final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask();
+
// Latin1 encoding is not supported, but we do use 8-bit subset for
// pre-processing task, to simplify first pass, keep it fast.
protected final static int[] _icLatin1 = CharTypes.getInputCodeLatin1();
@@ -443,13 +445,13 @@
@Override
public byte[] getBinaryValue(Base64Variant b64variant) throws IOException
{
- if (_currToken != JsonToken.VALUE_STRING &&
- (_currToken != JsonToken.VALUE_EMBEDDED_OBJECT || _binaryValue == null)) {
+ if ((_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) && (_binaryValue != null)) {
+ return _binaryValue;
+ }
+ if (_currToken != JsonToken.VALUE_STRING) {
_reportError("Current token ("+_currToken+") not VALUE_STRING or VALUE_EMBEDDED_OBJECT, can not access as binary");
}
- /* To ensure that we won't see inconsistent data, better clear up
- * state...
- */
+ // To ensure that we won't see inconsistent data, better clear up state
if (_tokenIncomplete) {
try {
_binaryValue = _decodeBase64(b64variant);
@@ -652,26 +654,22 @@
_binaryValue = null;
// Closing scope?
- if (i == INT_RBRACKET) {
- _updateLocation();
- if (!_parsingContext.inArray()) {
- _reportMismatchedEndMarker(i, '}');
- }
- _parsingContext = _parsingContext.clearAndGetParent();
- return (_currToken = JsonToken.END_ARRAY);
- }
- if (i == INT_RCURLY) {
- _updateLocation();
- if (!_parsingContext.inObject()) {
- _reportMismatchedEndMarker(i, ']');
- }
- _parsingContext = _parsingContext.clearAndGetParent();
- return (_currToken = JsonToken.END_OBJECT);
+ if (i == INT_RBRACKET || i == INT_RCURLY) {
+ _closeScope(i);
+ return _currToken;
}
// Nope: do we then expect a comma?
if (_parsingContext.expectComma()) {
i = _skipComma(i);
+
+ // Was that a trailing comma?
+ if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
+ if ((i == INT_RBRACKET) || (i == INT_RCURLY)) {
+ _closeScope(i);
+ return _currToken;
+ }
+ }
}
/* And should we now have a name? Always true for Object contexts, since
@@ -811,26 +809,22 @@
}
_binaryValue = null;
- if (i == INT_RBRACKET) {
- _updateLocation();
- if (!_parsingContext.inArray()) {
- _reportMismatchedEndMarker(i, '}');
- }
- _parsingContext = _parsingContext.clearAndGetParent();
- _currToken = JsonToken.END_ARRAY;
+ // Closing scope?
+ if (i == INT_RBRACKET || i == INT_RCURLY) {
+ _closeScope(i);
return false;
}
- if (i == INT_RCURLY) {
- _updateLocation();
- if (!_parsingContext.inObject()) {
- _reportMismatchedEndMarker(i, ']');
- }
- _parsingContext = _parsingContext.clearAndGetParent();
- _currToken = JsonToken.END_OBJECT;
- return false;
- }
+
if (_parsingContext.expectComma()) {
i = _skipComma(i);
+
+ // Was that a trailing comma?
+ if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
+ if ((i == INT_RBRACKET) || (i == INT_RCURLY)) {
+ _closeScope(i);
+ return false;
+ }
+ }
}
if (!_parsingContext.inObject()) {
@@ -1898,7 +1892,7 @@
}
return _handleInvalidNumberStart(_inputBuffer[_inputPtr++], false);
}
- // [Issue#77] Try to decode most likely token
+ // [core#77] Try to decode most likely token
if (Character.isJavaIdentifierStart(i)) {
_reportInvalidToken(""+((char) i), "('true', 'false' or 'null')");
}
@@ -2834,4 +2828,29 @@
}
_reportError("Unrecognized token '"+sb.toString()+"': was expecting "+msg);
}
+
+ /*
+ /**********************************************************
+ /* Internal methods, other
+ /**********************************************************
+ */
+
+ private void _closeScope(int i) throws JsonParseException {
+ if (i == INT_RBRACKET) {
+ _updateLocation();
+ if (!_parsingContext.inArray()) {
+ _reportMismatchedEndMarker(i, '}');
+ }
+ _parsingContext = _parsingContext.clearAndGetParent();
+ _currToken = JsonToken.END_ARRAY;
+ }
+ if (i == INT_RCURLY) {
+ _updateLocation();
+ if (!_parsingContext.inObject()) {
+ _reportMismatchedEndMarker(i, ']');
+ }
+ _parsingContext = _parsingContext.clearAndGetParent();
+ _currToken = JsonToken.END_OBJECT;
+ }
+ }
}
diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java
index 8bc3789..66f96e5 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java
@@ -569,25 +569,20 @@
if (_tokenIncomplete) {
_skipString(); // only strings can be partial
}
- int i = _skipWS();
+ int i = _skipWSOrEnd();
+ if (i < 0) { // end-of-input
+ // Close/release things like input source, symbol table and recyclable buffers
+ close();
+ return (_currToken = null);
+ }
// clear any data retained so far
_binaryValue = null;
_tokenInputRow = _currInputRow;
// Closing scope?
- if (i == INT_RBRACKET) {
- if (!_parsingContext.inArray()) {
- _reportMismatchedEndMarker(i, '}');
- }
- _parsingContext = _parsingContext.clearAndGetParent();
- return (_currToken = JsonToken.END_ARRAY);
- }
- if (i == INT_RCURLY) {
- if (!_parsingContext.inObject()) {
- _reportMismatchedEndMarker(i, ']');
- }
- _parsingContext = _parsingContext.clearAndGetParent();
- return (_currToken = JsonToken.END_OBJECT);
+ if (i == INT_RBRACKET || i == INT_RCURLY) {
+ _closeScope(i);
+ return _currToken;
}
// Nope: do we then expect a comma?
@@ -596,6 +591,14 @@
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
i = _skipWS();
+
+ // Was that a trailing comma?
+ if (Feature.ALLOW_TRAILING_COMMA.enabledIn(_features)) {
+ if (i == INT_RBRACKET || i == INT_RCURLY) {
+ _closeScope(i);
+ return _currToken;
+ }
+ }
}
/* And should we now have a name? Always true for
@@ -2199,6 +2202,45 @@
}
}
+ /**
+ * Alternative to {@link #_skipWS} that handles possible {@link EOFException}
+ * caused by trying to read past the end of {@link InputData}.
+ *
+ * @since 2.9
+ */
+ private final int _skipWSOrEnd() throws IOException
+ {
+ int i = _nextByte;
+ if (i < 0) {
+ try {
+ i = _inputData.readUnsignedByte();
+ } catch (EOFException e) {
+ return _eofAsNextChar();
+ }
+ } else {
+ _nextByte = -1;
+ }
+ while (true) {
+ if (i > INT_SPACE) {
+ if (i == INT_SLASH || i == INT_HASH) {
+ return _skipWSComment(i);
+ }
+ return i;
+ } else {
+ // 06-May-2016, tatu: Could verify validity of WS, but for now why bother.
+ // ... but line number is useful thingy
+ if (i == INT_CR || i == INT_LF) {
+ ++_currInputRow;
+ }
+ }
+ try {
+ i = _inputData.readUnsignedByte();
+ } catch (EOFException e) {
+ return _eofAsNextChar();
+ }
+ }
+ }
+
private final int _skipWSComment(int i) throws IOException
{
while (true) {
@@ -2788,6 +2830,23 @@
/**********************************************************
*/
+ private void _closeScope(int i) throws JsonParseException {
+ if (i == INT_RBRACKET) {
+ if (!_parsingContext.inArray()) {
+ _reportMismatchedEndMarker(i, '}');
+ }
+ _parsingContext = _parsingContext.clearAndGetParent();
+ _currToken = JsonToken.END_ARRAY;
+ }
+ if (i == INT_RCURLY) {
+ if (!_parsingContext.inObject()) {
+ _reportMismatchedEndMarker(i, ']');
+ }
+ _parsingContext = _parsingContext.clearAndGetParent();
+ _currToken = JsonToken.END_OBJECT;
+ }
+ }
+
/**
* Helper method needed to fix [Issue#148], masking of 0x00 character
*/
diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java
index 5a0dcda..1533f4a 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java
@@ -28,6 +28,8 @@
// pre-processing task, to simplify first pass, keep it fast.
protected final static int[] _icLatin1 = CharTypes.getInputCodeLatin1();
+ protected final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask();
+
/*
/**********************************************************
/* Configuration
@@ -118,7 +120,7 @@
* buffer.
*/
protected boolean _bufferRecyclable;
-
+
/*
/**********************************************************
/* Life-cycle
@@ -152,7 +154,7 @@
public void setCodec(ObjectCodec c) {
_objectCodec = c;
}
-
+
/*
/**********************************************************
/* Overrides for life-cycle
@@ -176,7 +178,7 @@
public Object getInputSource() {
return _inputStream;
}
-
+
/*
/**********************************************************
/* Overrides, low-level reading
@@ -259,14 +261,12 @@
}
return true;
}
-
+
@Override
protected void _closeInput() throws IOException
{
- /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
- * on the underlying InputStream, unless we "own" it, or auto-closing
- * feature is enabled.
- */
+ // We are not to call close() on the underlying InputStream
+ // unless we "own" it, or auto-closing feature is enabled.
if (_inputStream != null) {
if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_SOURCE)) {
_inputStream.close();
@@ -739,19 +739,11 @@
// Closing scope?
if (i == INT_RBRACKET) {
- _updateLocation();
- if (!_parsingContext.inArray()) {
- _reportMismatchedEndMarker(i, '}');
- }
- _parsingContext = _parsingContext.clearAndGetParent();
+ _closeArrayScope();
return (_currToken = JsonToken.END_ARRAY);
}
if (i == INT_RCURLY) {
- _updateLocation();
- if (!_parsingContext.inObject()) {
- _reportMismatchedEndMarker(i, ']');
- }
- _parsingContext = _parsingContext.clearAndGetParent();
+ _closeObjectScope();
return (_currToken = JsonToken.END_OBJECT);
}
@@ -761,11 +753,16 @@
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
i = _skipWS();
+ // Was that a trailing comma?
+ if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
+ if ((i == INT_RBRACKET) || (i == INT_RCURLY)) {
+ return _closeScope(i);
+ }
+ }
}
- /* And should we now have a name? Always true for
- * Object contexts, since the intermediate 'expect-value'
- * state is never retained.
+ /* And should we now have a name? Always true for Object contexts
+ * since the intermediate 'expect-value' state is never retained.
*/
if (!_parsingContext.inObject()) {
_updateLocation();
@@ -931,20 +928,12 @@
// Closing scope?
if (i == INT_RBRACKET) {
- _updateLocation();
- if (!_parsingContext.inArray()) {
- _reportMismatchedEndMarker(i, '}');
- }
- _parsingContext = _parsingContext.clearAndGetParent();
+ _closeArrayScope();
_currToken = JsonToken.END_ARRAY;
return false;
}
if (i == INT_RCURLY) {
- _updateLocation();
- if (!_parsingContext.inObject()) {
- _reportMismatchedEndMarker(i, ']');
- }
- _parsingContext = _parsingContext.clearAndGetParent();
+ _closeObjectScope();
_currToken = JsonToken.END_OBJECT;
return false;
}
@@ -955,8 +944,15 @@
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
i = _skipWS();
- }
+ // Was that a trailing comma?
+ if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
+ if ((i == INT_RBRACKET) || (i == INT_RCURLY)) {
+ _closeScope(i);
+ return false;
+ }
+ }
+ }
if (!_parsingContext.inObject()) {
_updateLocation();
_nextTokenNotInObject(i);
@@ -1018,20 +1014,12 @@
_binaryValue = null;
if (i == INT_RBRACKET) {
- _updateLocation();
- if (!_parsingContext.inArray()) {
- _reportMismatchedEndMarker(i, '}');
- }
- _parsingContext = _parsingContext.clearAndGetParent();
+ _closeArrayScope();
_currToken = JsonToken.END_ARRAY;
return null;
}
if (i == INT_RCURLY) {
- _updateLocation();
- if (!_parsingContext.inObject()) {
- _reportMismatchedEndMarker(i, ']');
- }
- _parsingContext = _parsingContext.clearAndGetParent();
+ _closeObjectScope();
_currToken = JsonToken.END_OBJECT;
return null;
}
@@ -1042,7 +1030,15 @@
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
i = _skipWS();
+ // Was that a trailing comma?
+ if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
+ if ((i == INT_RBRACKET) || (i == INT_RCURLY)) {
+ _closeScope(i);
+ return null;
+ }
+ }
}
+
if (!_parsingContext.inObject()) {
_updateLocation();
_nextTokenNotInObject(i);
@@ -3497,33 +3493,32 @@
/**********************************************************
*/
- protected void _reportInvalidToken(String matchedPart) throws IOException
- {
- _reportInvalidToken(matchedPart, "'null', 'true', 'false' or NaN");
- }
+ protected void _reportInvalidToken(String matchedPart) throws IOException {
+ _reportInvalidToken(matchedPart, "'null', 'true', 'false' or NaN");
+ }
protected void _reportInvalidToken(String matchedPart, String msg) throws IOException
- {
- StringBuilder sb = new StringBuilder(matchedPart);
+ {
+ StringBuilder sb = new StringBuilder(matchedPart);
- /* Let's just try to find what appears to be the token, using
- * regular Java identifier character rules. It's just a heuristic,
- * nothing fancy here (nor fast).
- */
- while (true) {
- if (_inputPtr >= _inputEnd && !_loadMore()) {
- break;
- }
- int i = (int) _inputBuffer[_inputPtr++];
- char c = (char) _decodeCharForError(i);
- if (!Character.isJavaIdentifierPart(c)) {
- break;
- }
- sb.append(c);
- }
- _reportError("Unrecognized token '"+sb.toString()+"': was expecting "+msg);
- }
-
+ /* Let's just try to find what appears to be the token, using
+ * regular Java identifier character rules. It's just a heuristic,
+ * nothing fancy here (nor fast).
+ */
+ while (true) {
+ if (_inputPtr >= _inputEnd && !_loadMore()) {
+ break;
+ }
+ int i = (int) _inputBuffer[_inputPtr++];
+ char c = (char) _decodeCharForError(i);
+ if (!Character.isJavaIdentifierPart(c)) {
+ break;
+ }
+ sb.append(c);
+ }
+ _reportError("Unrecognized token '"+sb.toString()+"': was expecting "+msg);
+ }
+
protected void _reportInvalidChar(int c)
throws JsonParseException
{
@@ -3733,6 +3728,31 @@
/**********************************************************
*/
+ private final JsonToken _closeScope(int i) throws JsonParseException {
+ if (i == INT_RCURLY) {
+ _closeObjectScope();
+ return (_currToken = JsonToken.END_OBJECT);
+ }
+ _closeArrayScope();
+ return (_currToken = JsonToken.END_ARRAY);
+ }
+
+ private final void _closeArrayScope() throws JsonParseException {
+ _updateLocation();
+ if (!_parsingContext.inArray()) {
+ _reportMismatchedEndMarker(']', '}');
+ }
+ _parsingContext = _parsingContext.clearAndGetParent();
+ }
+
+ private final void _closeObjectScope() throws JsonParseException {
+ _updateLocation();
+ if (!_parsingContext.inObject()) {
+ _reportMismatchedEndMarker('}', ']');
+ }
+ _parsingContext = _parsingContext.clearAndGetParent();
+ }
+
/**
* Helper method needed to fix [Issue#148], masking of 0x00 character
*/
diff --git a/src/main/java/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java b/src/main/java/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java
index 6641151..f5ee928 100644
--- a/src/main/java/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java
+++ b/src/main/java/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java
@@ -364,17 +364,17 @@
*/
@Override
- public void writeObject(Object pojo) throws IOException,JsonProcessingException {
+ public void writeObject(Object pojo) throws IOException {
if (delegateCopyMethods) {
delegate.writeObject(pojo);
return;
}
- // NOTE: copied from
if (pojo == null) {
writeNull();
} else {
- if (getCodec() != null) {
- getCodec().writeValue(this, pojo);
+ ObjectCodec c = getCodec();
+ if (c != null) {
+ c.writeValue(this, pojo);
return;
}
_writeSimpleObject(pojo);
@@ -382,19 +382,20 @@
}
@Override
- public void writeTree(TreeNode rootNode) throws IOException {
+ public void writeTree(TreeNode tree) throws IOException {
if (delegateCopyMethods) {
- delegate.writeTree(rootNode);
+ delegate.writeTree(tree);
return;
}
// As with 'writeObject()', we are not check if write would work
- if (rootNode == null) {
+ if (tree == null) {
writeNull();
} else {
- if (getCodec() == null) {
+ ObjectCodec c = getCodec();
+ if (c == null) {
throw new IllegalStateException("No ObjectCodec defined");
}
- getCodec().writeValue(this, rootNode);
+ c.writeTree(this, tree);
}
}
@@ -413,15 +414,15 @@
*/
@Override
- public void copyCurrentEvent(JsonParser jp) throws IOException {
- if (delegateCopyMethods) delegate.copyCurrentEvent(jp);
- else super.copyCurrentEvent(jp);
+ public void copyCurrentEvent(JsonParser p) throws IOException {
+ if (delegateCopyMethods) delegate.copyCurrentEvent(p);
+ else super.copyCurrentEvent(p);
}
@Override
- public void copyCurrentStructure(JsonParser jp) throws IOException {
- if (delegateCopyMethods) delegate.copyCurrentStructure(jp);
- else super.copyCurrentStructure(jp);
+ public void copyCurrentStructure(JsonParser p) throws IOException {
+ if (delegateCopyMethods) delegate.copyCurrentStructure(p);
+ else super.copyCurrentStructure(p);
}
/*
diff --git a/src/main/java/com/fasterxml/jackson/core/util/VersionUtil.java b/src/main/java/com/fasterxml/jackson/core/util/VersionUtil.java
index 3896001..5bdc1b4 100644
--- a/src/main/java/com/fasterxml/jackson/core/util/VersionUtil.java
+++ b/src/main/java/com/fasterxml/jackson/core/util/VersionUtil.java
@@ -27,33 +27,17 @@
{
private final static Pattern V_SEP = Pattern.compile("[-_./;:]");
- private final Version _v;
-
/*
/**********************************************************
/* Instance life-cycle
/**********************************************************
*/
-
- protected VersionUtil()
- {
- Version v = null;
- try {
- /* Class we pass only matters for resource-loading: can't use this Class
- * (as it's just being loaded at this point), nor anything that depends on it.
- */
- v = VersionUtil.versionFor(getClass());
- } catch (Exception e) { // not good to dump to stderr; but that's all we have at this low level
- System.err.println("ERROR: Failed to load Version information from "+getClass());
- }
- if (v == null) {
- v = Version.unknownVersion();
- }
- _v = v;
- }
- public Version version() { return _v; }
-
+ protected VersionUtil() { }
+
+ @Deprecated // since 2.9
+ public Version version() { return Version.unknownVersion(); }
+
/*
/**********************************************************
/* Static load methods
diff --git a/src/test/java/com/fasterxml/jackson/core/BaseTest.java b/src/test/java/com/fasterxml/jackson/core/BaseTest.java
index ca8ce4f..053869a 100644
--- a/src/test/java/com/fasterxml/jackson/core/BaseTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/BaseTest.java
@@ -422,6 +422,7 @@
{
StringWriter sw = new StringWriter(100);
JsonGenerator g = f.createGenerator(sw);
+ g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT);
try {
while (p.nextToken() != null) {
g.copyCurrentEvent(p);
diff --git a/src/test/java/com/fasterxml/jackson/core/TestExceptions.java b/src/test/java/com/fasterxml/jackson/core/TestExceptions.java
index 0301b19..fbbb74f 100644
--- a/src/test/java/com/fasterxml/jackson/core/TestExceptions.java
+++ b/src/test/java/com/fasterxml/jackson/core/TestExceptions.java
@@ -16,6 +16,20 @@
String orig = exc.getOriginalMessage();
assertEquals("Foobar", orig);
assertTrue(msg.length() > orig.length());
+
+ // and another
+ JsonProcessingException exc2 = new JsonProcessingException("Second",
+ JsonLocation.NA, exc);
+ assertSame(exc, exc2.getCause());
+ exc2.clearLocation();
+ assertNull(exc2.getLocation());
+
+ // and yet with null
+ JsonProcessingException exc3 = new JsonProcessingException(exc);
+ assertNull(exc3.getOriginalMessage());
+ assertEquals("N/A", exc3.getMessage());
+
+ assertEquals("com.fasterxml.jackson.core.JsonProcessingException: N/A", exc3.toString());
}
// [core#198]
diff --git a/src/test/java/com/fasterxml/jackson/core/TestLocation.java b/src/test/java/com/fasterxml/jackson/core/TestLocation.java
new file mode 100644
index 0000000..d993129
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/TestLocation.java
@@ -0,0 +1,21 @@
+package com.fasterxml.jackson.core;
+
+public class TestLocation extends BaseTest
+{
+ public void testBasics()
+ {
+ JsonLocation loc1 = new JsonLocation("src", 10L, 10L, 1, 2);
+ JsonLocation loc2 = new JsonLocation(null, 10L, 10L, 3, 2);
+ assertEquals(loc1, loc1);
+ assertFalse(loc1.equals(null));
+ assertFalse(loc1.equals(loc2));
+ assertFalse(loc2.equals(loc1));
+
+ // don't care about what it is; should not compute to 0 with data above
+ assertTrue(loc1.hashCode() != 0);
+ assertTrue(loc2.hashCode() != 0);
+
+ assertEquals("[Source: src; line: 1, column: 2]", loc1.toString());
+ assertEquals("[Source: UNKNOWN; line: 3, column: 2]", loc2.toString());
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/TestVersions.java b/src/test/java/com/fasterxml/jackson/core/TestVersions.java
index c7b6feb..865be6f 100644
--- a/src/test/java/com/fasterxml/jackson/core/TestVersions.java
+++ b/src/test/java/com/fasterxml/jackson/core/TestVersions.java
@@ -6,7 +6,7 @@
import com.fasterxml.jackson.core.util.BufferRecycler;
/**
- * Tests to verify [JACKSON-278]
+ * Tests to verify functioning of {@link Version} class.
*/
public class TestVersions extends com.fasterxml.jackson.core.BaseTest
{
@@ -22,6 +22,18 @@
jgen.close();
}
+ public void testMisc() {
+ Version unk = Version.unknownVersion();
+ assertEquals("0.0.0", unk.toString());
+ assertEquals("//0.0.0", unk.toFullString());
+ assertTrue(unk.equals(unk));
+
+ Version other = new Version(2, 8, 4, "",
+ "groupId", "artifactId");
+ assertEquals("2.8.4", other.toString());
+ assertEquals("groupId/artifactId/2.8.4", other.toFullString());
+ }
+
/*
/**********************************************************
/* Helper methods
diff --git a/src/test/java/com/fasterxml/jackson/core/base64/Base64BinaryParsingTest.java b/src/test/java/com/fasterxml/jackson/core/base64/Base64BinaryParsingTest.java
index 8693bb9..24b53ea 100644
--- a/src/test/java/com/fasterxml/jackson/core/base64/Base64BinaryParsingTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/base64/Base64BinaryParsingTest.java
@@ -49,6 +49,63 @@
}
}
+ public void testInvalidTokenForBase64() throws IOException
+ {
+ for (int mode : ALL_MODES) {
+
+ // First: illegal padding
+ JsonParser p = createParser(mode, "[ ]");
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ try {
+ p.getBinaryValue();
+ fail("Should not pass");
+ } catch (JsonParseException e) {
+ verifyException(e, "current token");
+ verifyException(e, "can not access as binary");
+ }
+ p.close();
+ }
+ }
+
+ public void testInvalidChar() throws IOException
+ {
+ for (int mode : ALL_MODES) {
+
+ // First: illegal padding
+ JsonParser p = createParser(mode, quote("a==="));
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ try {
+ p.getBinaryValue(Base64Variants.MIME);
+ fail("Should not pass");
+ } catch (JsonParseException e) {
+ verifyException(e, "padding only legal");
+ }
+ p.close();
+
+ // second: invalid space within
+ p = createParser(mode, quote("ab de"));
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ try {
+ p.getBinaryValue(Base64Variants.MIME);
+ fail("Should not pass");
+ } catch (JsonParseException e) {
+ verifyException(e, "illegal white space");
+ }
+ p.close();
+
+ // third: something else
+ p = createParser(mode, quote("ab#?"));
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ try {
+ p.getBinaryValue(Base64Variants.MIME);
+ fail("Should not pass");
+ } catch (JsonParseException e) {
+ verifyException(e, "illegal character '#'");
+ }
+ p.close();
+ }
+ }
+
/*
/**********************************************************
/* Test helper methods
diff --git a/src/test/java/com/fasterxml/jackson/core/base64/Base64CodecTest.java b/src/test/java/com/fasterxml/jackson/core/base64/Base64CodecTest.java
new file mode 100644
index 0000000..6b895ed
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/base64/Base64CodecTest.java
@@ -0,0 +1,113 @@
+package com.fasterxml.jackson.core.base64;
+
+import org.junit.Assert;
+
+import com.fasterxml.jackson.core.*;
+
+public class Base64CodecTest
+ extends com.fasterxml.jackson.core.BaseTest
+{
+ public void testVariantAccess()
+ {
+ for (Base64Variant var : new Base64Variant[] {
+ Base64Variants.MIME,
+ Base64Variants.MIME_NO_LINEFEEDS,
+ Base64Variants.MODIFIED_FOR_URL,
+ Base64Variants.PEM
+ }) {
+ assertSame(var, Base64Variants.valueOf(var.getName()));
+ }
+
+ try {
+ Base64Variants.valueOf("foobar");
+ fail("Should not pass");
+ } catch (IllegalArgumentException e) {
+ verifyException(e, "No Base64Variant with name 'foobar'");
+ }
+ }
+
+ public void testProps()
+ {
+ Base64Variant std = Base64Variants.MIME;
+ // let's verify basic props of std cocec
+ assertEquals("MIME", std.getName());
+ assertEquals("MIME", std.toString());
+ assertTrue(std.usesPadding());
+ assertFalse(std.usesPaddingChar('X'));
+ assertEquals('=', std.getPaddingChar());
+ assertTrue(std.usesPaddingChar('='));
+ assertEquals((byte) '=', std.getPaddingByte());
+ assertEquals(76, std.getMaxLineLength());
+ }
+
+ public void testCharEncoding() throws Exception
+ {
+ Base64Variant std = Base64Variants.MIME;
+ assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Char('?'));
+ assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Char((int) '?'));
+ assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Char((char) 0xA0));
+ assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Char(0xA0));
+
+ assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Byte((byte) '?'));
+ assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Byte((byte) 0xA0));
+
+ assertEquals(0, std.decodeBase64Char('A'));
+ assertEquals(1, std.decodeBase64Char((int) 'B'));
+ assertEquals(2, std.decodeBase64Char((byte)'C'));
+
+ assertEquals(0, std.decodeBase64Byte((byte) 'A'));
+ assertEquals(1, std.decodeBase64Byte((byte) 'B'));
+ assertEquals(2, std.decodeBase64Byte((byte)'C'));
+
+ assertEquals('/', std.encodeBase64BitsAsChar(63));
+ assertEquals((byte) 'b', std.encodeBase64BitsAsByte(27));
+
+ String EXP_STR = "HwdJ";
+ int TRIPLET = 0x1F0749;
+ StringBuilder sb = new StringBuilder();
+ std.encodeBase64Chunk(sb, TRIPLET);
+ assertEquals(EXP_STR, sb.toString());
+
+ byte[] exp = EXP_STR.getBytes("UTF-8");
+ byte[] act = new byte[exp.length];
+ std.encodeBase64Chunk(TRIPLET, act, 0);
+ Assert.assertArrayEquals(exp, act);
+ }
+
+ public void testConvenienceMethods() throws Exception
+ {
+ Base64Variant std = Base64Variants.MIME;
+
+ byte[] input = new byte[] { 1, 2, 34, 127, -1 };
+ String encoded = std.encode(input, false);
+ byte[] decoded = std.decode(encoded);
+ Assert.assertArrayEquals(input, decoded);
+
+ assertEquals(quote(encoded), std.encode(input, true));
+ }
+
+ @SuppressWarnings("unused")
+ public void testErrors() throws Exception
+ {
+ try {
+ Base64Variant b = new Base64Variant("foobar", "xyz", false, '!', 24);
+ fail("Should not pass");
+ } catch (IllegalArgumentException iae) {
+ verifyException(iae, "length must be exactly");
+ }
+ try {
+ Base64Variants.MIME.decode("!@##@%$#%&*^(&)(*");
+ } catch (IllegalArgumentException iae) {
+ verifyException(iae, "Illegal character");
+ }
+
+ // also, for [jackson-core#335]
+ final String BASE64_HELLO = "aGVsbG8=!";
+ try {
+ Base64Variants.MIME.decode(BASE64_HELLO);
+ fail("Should not pass");
+ } catch (IllegalArgumentException iae) {
+ verifyException(iae, "Illegal character");
+ }
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/base64/TestBase64Codec.java b/src/test/java/com/fasterxml/jackson/core/base64/TestBase64Codec.java
deleted file mode 100644
index dabcc39..0000000
--- a/src/test/java/com/fasterxml/jackson/core/base64/TestBase64Codec.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package com.fasterxml.jackson.core.base64;
-
-import org.junit.Assert;
-
-import com.fasterxml.jackson.core.*;
-
-public class TestBase64Codec
- extends com.fasterxml.jackson.core.BaseTest
-{
- public void testProps()
- {
- Base64Variant std = Base64Variants.MIME;
- // let's verify basic props of std cocec
- assertEquals("MIME", std.getName());
- assertEquals("MIME", std.toString());
- assertTrue(std.usesPadding());
- assertFalse(std.usesPaddingChar('X'));
- assertEquals('=', std.getPaddingChar());
- assertTrue(std.usesPaddingChar('='));
- assertEquals((byte) '=', std.getPaddingByte());
- assertEquals(76, std.getMaxLineLength());
- }
-
- public void testCharEncoding() throws Exception
- {
- Base64Variant std = Base64Variants.MIME;
- assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Char('?'));
- assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Char((int) '?'));
- assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Char((byte) '?'));
-
- assertEquals(0, std.decodeBase64Char('A'));
- assertEquals(1, std.decodeBase64Char((int) 'B'));
- assertEquals(2, std.decodeBase64Char((byte)'C'));
-
- assertEquals('/', std.encodeBase64BitsAsChar(63));
- assertEquals((byte) 'b', std.encodeBase64BitsAsByte(27));
-
- String EXP_STR = "HwdJ";
- int TRIPLET = 0x1F0749;
- StringBuilder sb = new StringBuilder();
- std.encodeBase64Chunk(sb, TRIPLET);
- assertEquals(EXP_STR, sb.toString());
-
- byte[] exp = EXP_STR.getBytes("UTF-8");
- byte[] act = new byte[exp.length];
- std.encodeBase64Chunk(TRIPLET, act, 0);
- Assert.assertArrayEquals(exp, act);
- }
-
- @SuppressWarnings("unused")
- public void testErrors() throws Exception
- {
- try {
- Base64Variant b = new Base64Variant("foobar", "xyz", false, '!', 24);
- } catch (IllegalArgumentException iae) {
- verifyException(iae, "length must be exactly");
- }
-
- // also, for [jackson-core#335]
- final String BASE64_HELLO = "aGVsbG8=!";
- try {
- Base64Variants.MIME.decode(BASE64_HELLO);
- fail("Should not pass");
- } catch (IllegalArgumentException iae) {
- verifyException(iae, "Illegal character");
- }
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/filter/BasicParserFilteringTest.java b/src/test/java/com/fasterxml/jackson/core/filter/BasicParserFilteringTest.java
index f7de6b5..7c061ec 100644
--- a/src/test/java/com/fasterxml/jackson/core/filter/BasicParserFilteringTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/filter/BasicParserFilteringTest.java
@@ -91,20 +91,6 @@
}
@SuppressWarnings("resource")
- public void testSingleMatchFilteringWithPath() throws Exception
- {
- JsonParser p0 = JSON_F.createParser(SIMPLE);
- JsonParser p = new FilteringParserDelegate(p0,
- new NameMatchFilter("value"),
- true, // includePath
- false // multipleMatches
- );
- String result = readAndWrite(JSON_F, p);
- assertEquals(aposToQuotes("{'ob':{'value':3}}"), result);
- }
-
-
- @SuppressWarnings("resource")
public void testNotAllowMultipleMatches() throws Exception
{
String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'value':4,'b':true}");
diff --git a/src/test/java/com/fasterxml/jackson/core/io/UTF8WriterTest.java b/src/test/java/com/fasterxml/jackson/core/io/UTF8WriterTest.java
index 32d1c39..ddefff6 100644
--- a/src/test/java/com/fasterxml/jackson/core/io/UTF8WriterTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/io/UTF8WriterTest.java
@@ -2,6 +2,8 @@
import java.io.*;
+import org.junit.Assert;
+
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.io.UTF8Writer;
import com.fasterxml.jackson.core.util.BufferRecycler;
@@ -51,6 +53,7 @@
char[] ch = str.toCharArray();
w.write(ch, 0, ch.length);
+ w.flush(); // trigger different code path for close
w.close();
byte[] data = out.toByteArray();
@@ -68,9 +71,11 @@
UTF8Writer w = new UTF8Writer(ctxt, out);
w.write('X');
+ char[] ch = { 'Y' };
+ w.write(ch);
w.close();
- assertEquals(1, out.size());
+ assertEquals(2, out.size());
// and this ought to be fine...
w.flush();
@@ -78,4 +83,80 @@
w.close();
w.flush();
}
+
+ public void testSurrogatesOk() throws Exception
+ {
+ BufferRecycler rec = new BufferRecycler();
+ IOContext ctxt = new IOContext(rec, null, false);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ UTF8Writer w = new UTF8Writer(ctxt, out);
+
+ // First, valid case, char by char
+ w.write(0xD83D);
+ w.write(0xDE03);
+ w.close();
+ assertEquals(4, out.size());
+ final byte[] EXP_SURROGATES = new byte[] { (byte) 0xF0, (byte) 0x9F,
+ (byte) 0x98, (byte) 0x83 };
+ Assert.assertArrayEquals(EXP_SURROGATES, out.toByteArray());
+
+ // and then as String
+ ctxt = new IOContext(rec, null, false);
+ out = new ByteArrayOutputStream();
+ w = new UTF8Writer(ctxt, out);
+ w.write("\uD83D\uDE03");
+ w.close();
+ assertEquals(4, out.size());
+ Assert.assertArrayEquals(EXP_SURROGATES, out.toByteArray());
+ }
+
+ @SuppressWarnings("resource")
+ public void testSurrogatesFail() throws Exception
+ {
+ BufferRecycler rec = new BufferRecycler();
+ IOContext ctxt;
+ ByteArrayOutputStream out;
+ UTF8Writer w;
+
+ ctxt = new IOContext(rec, null, false);
+ out = new ByteArrayOutputStream();
+ w = new UTF8Writer(ctxt, out);
+ try {
+ w.write(0xDE03);
+ fail("should not pass");
+ } catch (IOException e) {
+ verifyException(e, "Unmatched second part");
+ }
+
+ ctxt = new IOContext(rec, null, false);
+ out = new ByteArrayOutputStream();
+ w = new UTF8Writer(ctxt, out);
+ w.write(0xD83D);
+ try {
+ w.write('a');
+ fail("should not pass");
+ } catch (IOException e) {
+ verifyException(e, "Broken surrogate pair");
+ }
+
+ ctxt = new IOContext(rec, null, false);
+ out = new ByteArrayOutputStream();
+ w = new UTF8Writer(ctxt, out);
+ try {
+ w.write("\uDE03");
+ fail("should not pass");
+ } catch (IOException e) {
+ verifyException(e, "Unmatched second part");
+ }
+
+ ctxt = new IOContext(rec, null, false);
+ out = new ByteArrayOutputStream();
+ w = new UTF8Writer(ctxt, out);
+ try {
+ w.write("\uD83Da");
+ fail("should not pass");
+ } catch (IOException e) {
+ verifyException(e, "Broken surrogate pair");
+ }
+ }
}
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 85363f4..955d3d5 100644
--- a/src/test/java/com/fasterxml/jackson/core/json/ParserSequenceTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/json/ParserSequenceTest.java
@@ -46,6 +46,31 @@
seq.close();
}
+ public void testMultiLevel() throws Exception
+ {
+ JsonParser p1 = JSON_FACTORY.createParser("[ 1 ] ");
+ JsonParser p2 = JSON_FACTORY.createParser(" 5");
+ JsonParser p3 = JSON_FACTORY.createParser(" { } ");
+ JsonParserSequence seq1 = JsonParserSequence.createFlattened(true, p1, p2);
+ JsonParserSequence seq = JsonParserSequence.createFlattened(false, seq1, p3);
+ assertEquals(3, seq.containedParsersCount());
+
+ assertToken(JsonToken.START_ARRAY, seq.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken());
+ assertToken(JsonToken.END_ARRAY, seq.nextToken());
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken());
+
+ assertToken(JsonToken.START_OBJECT, seq.nextToken());
+ assertToken(JsonToken.END_OBJECT, seq.nextToken());
+
+ assertNull(seq.nextToken());
+ assertTrue(p1.isClosed());
+ assertTrue(p2.isClosed());
+ assertTrue(p3.isClosed());
+ assertTrue(seq.isClosed());
+ }
+
// for [jackson-core#296]
public void testInitializationDisabled() throws Exception
{
diff --git a/src/test/java/com/fasterxml/jackson/core/read/DataInputTest.java b/src/test/java/com/fasterxml/jackson/core/read/DataInputTest.java
new file mode 100644
index 0000000..4b90862
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/read/DataInputTest.java
@@ -0,0 +1,42 @@
+package com.fasterxml.jackson.core.read;
+
+import com.fasterxml.jackson.core.*;
+
+/* Additional testing for {@link java.io.DataInput} specific
+ * challenges for parsing.
+ */
+public class DataInputTest
+ extends com.fasterxml.jackson.core.BaseTest
+{
+ private final JsonFactory JSON_F = new JsonFactory();
+
+ public void testEOFAfterArray() throws Exception
+ {
+ JsonParser p = createParser(JSON_F, MODE_DATA_INPUT, "[ 1 ] ");
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertNull(p.nextToken());
+ p.close();
+ }
+
+ public void testEOFAfterObject() throws Exception
+ {
+ JsonParser p = createParser(JSON_F, MODE_DATA_INPUT, "{ \"value\" : true }");
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ assertNull(p.nextToken());
+ p.close();
+ }
+
+ public void testEOFAfterScalar() throws Exception
+ {
+ JsonParser p = createParser(JSON_F, MODE_DATA_INPUT, "\"foobar\" ");
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("foobar", p.getText());
+ assertNull(p.nextToken());
+ p.close();
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/read/NumberCoercionTest.java b/src/test/java/com/fasterxml/jackson/core/read/NumberCoercionTest.java
new file mode 100644
index 0000000..8b83f09
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/read/NumberCoercionTest.java
@@ -0,0 +1,284 @@
+package com.fasterxml.jackson.core.read;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.BaseTest;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonParser.NumberType;
+import com.fasterxml.jackson.core.JsonToken;
+
+public class NumberCoercionTest extends BaseTest
+{
+ /*
+ /**********************************************************
+ /* Numeric coercions, integral
+ /**********************************************************
+ */
+
+ public void testToIntCoercion() throws Exception
+ {
+ for (int mode : ALL_STREAMING_MODES) {
+ JsonParser p;
+
+ // long->int
+ p = createParser(mode, "1");
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(1L, p.getLongValue());
+ assertEquals(1, p.getIntValue());
+ p.close();
+
+ // BigInteger->int
+ p = createParser(mode, "10");
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(BigInteger.TEN, p.getBigIntegerValue());
+ assertEquals(10, p.getIntValue());
+ p.close();
+
+ // double->int
+ p = createParser(mode, "2");
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(2.0, p.getDoubleValue());
+ assertEquals(2, p.getIntValue());
+ p.close();
+
+ // BigDecimal->int
+ p = createParser(mode, "10");
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(BigDecimal.TEN, p.getDecimalValue());
+ assertEquals(10, p.getIntValue());
+ p.close();
+ }
+ }
+
+ @SuppressWarnings("resource")
+ public void testToIntFailing() throws Exception
+ {
+ for (int mode : ALL_STREAMING_MODES) {
+ JsonParser p;
+
+ // long -> error
+ long big = 1L + Integer.MAX_VALUE;
+ p = createParser(mode, String.valueOf(big));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(big, p.getLongValue());
+ try {
+ p.getIntValue();
+ fail("Should not pass");
+ } catch (JsonParseException e) {
+ verifyException(e, "out of range of int");
+ }
+ long small = -1L + Integer.MIN_VALUE;
+ p = createParser(mode, String.valueOf(small));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(Long.valueOf(small), p.getNumberValue());
+ assertEquals(small, p.getLongValue());
+ try {
+ p.getIntValue();
+ fail("Should not pass");
+ } catch (JsonParseException e) {
+ verifyException(e, "out of range of int");
+ }
+
+ // double -> error
+ p = createParser(mode, String.valueOf(big)+".0");
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertEquals((double) big, p.getDoubleValue());
+ try {
+ p.getIntValue();
+ fail("Should not pass");
+ } catch (JsonParseException e) {
+ verifyException(e, "out of range of int");
+ }
+ p = createParser(mode, String.valueOf(small)+".0");
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertEquals((double) small, p.getDoubleValue());
+ try {
+ p.getIntValue();
+ fail("Should not pass");
+ } catch (JsonParseException e) {
+ verifyException(e, "out of range of int");
+ }
+
+ // BigInteger -> error
+ p = createParser(mode, String.valueOf(big));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(BigInteger.valueOf(big), p.getBigIntegerValue());
+ try {
+ p.getIntValue();
+ fail("Should not pass");
+ } catch (JsonParseException e) {
+ verifyException(e, "out of range of int");
+ }
+ p = createParser(mode, String.valueOf(small));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(BigInteger.valueOf(small), p.getBigIntegerValue());
+ try {
+ p.getIntValue();
+ fail("Should not pass");
+ } catch (JsonParseException e) {
+ verifyException(e, "out of range of int");
+ }
+ }
+ }
+
+ public void testToLongCoercion() throws Exception
+ {
+ for (int mode : ALL_STREAMING_MODES) {
+ JsonParser p;
+
+ // int->long
+ p = createParser(mode, "1");
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(1, p.getIntValue());
+ assertEquals(1L, p.getLongValue());
+ p.close();
+
+ // BigInteger->long
+ long biggish = 12345678901L;
+ p = createParser(mode, String.valueOf(biggish));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(BigInteger.valueOf(biggish), p.getBigIntegerValue());
+ assertEquals(biggish, p.getLongValue());
+ p.close();
+
+ // double->long
+ p = createParser(mode, "2");
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(2.0, p.getDoubleValue());
+ assertEquals(2L, p.getLongValue());
+ p.close();
+
+ // BigDecimal->long
+ p = createParser(mode, "10");
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(BigDecimal.TEN, p.getDecimalValue());
+ assertEquals(10, p.getLongValue());
+ p.close();
+ }
+ }
+
+ @SuppressWarnings("resource")
+ public void testToLongFailing() throws Exception
+ {
+ for (int mode : ALL_STREAMING_MODES) {
+ JsonParser p;
+
+ // BigInteger -> error
+ BigInteger big = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.TEN);
+ p = createParser(mode, String.valueOf(big));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(NumberType.BIG_INTEGER, p.getNumberType());
+ assertEquals(big, p.getBigIntegerValue());
+ assertEquals(big, p.getNumberValue());
+ try {
+ p.getLongValue();
+ fail("Should not pass");
+ } catch (JsonParseException e) {
+ verifyException(e, "out of range of long");
+ }
+ BigInteger small = BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.TEN);
+ p = createParser(mode, String.valueOf(small));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(small, p.getBigIntegerValue());
+ try {
+ p.getLongValue();
+ fail("Should not pass");
+ } catch (JsonParseException e) {
+ verifyException(e, "out of range of long");
+ }
+ }
+ }
+
+ public void testToBigIntegerCoercion() throws Exception
+ {
+ for (int mode : ALL_STREAMING_MODES) {
+ JsonParser p;
+
+ p = createParser(mode, "1");
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ // int to BigInteger
+ assertEquals(1, p.getIntValue());
+ assertEquals(BigInteger.ONE, p.getBigIntegerValue());
+ p.close();
+
+ p = createParser(mode, "2.0");
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ // double to BigInteger
+ assertEquals(2.0, p.getDoubleValue());
+ assertEquals(BigInteger.valueOf(2L), p.getBigIntegerValue());
+ p.close();
+
+ p = createParser(mode, String.valueOf(Long.MAX_VALUE));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ // long to BigInteger
+ assertEquals(Long.MAX_VALUE, p.getLongValue());
+ assertEquals(BigInteger.valueOf(Long.MAX_VALUE), p.getBigIntegerValue());
+ p.close();
+
+ p = createParser(mode, " 200.0");
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ // BigDecimal to BigInteger
+ assertEquals(new BigDecimal("200.0"), p.getDecimalValue());
+ assertEquals(BigInteger.valueOf(200L), p.getBigIntegerValue());
+ p.close();
+ }
+ }
+
+ /*
+ /**********************************************************
+ /* Numeric coercions, floating point
+ /**********************************************************
+ */
+
+ public void testToDoubleCoercion() throws Exception
+ {
+ for (int mode : ALL_STREAMING_MODES) {
+ JsonParser p;
+
+ // BigDecimal->double
+ p = createParser(mode, "100.5");
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertEquals(new BigDecimal("100.5"), p.getDecimalValue());
+ assertEquals(100.5, p.getDoubleValue());
+ p.close();
+
+ p = createParser(mode, "10");
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(BigInteger.TEN, p.getBigIntegerValue());
+ assertEquals(10.0, p.getDoubleValue());
+ p.close();
+ }
+ }
+
+ public void testToBigDecimalCoercion() throws Exception
+ {
+ for (int mode : ALL_STREAMING_MODES) {
+ JsonParser p;
+
+ p = createParser(mode, "1");
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ // int to BigDecimal
+ assertEquals(1, p.getIntValue());
+ assertEquals(BigDecimal.ONE, p.getDecimalValue());
+ p.close();
+
+ p = createParser(mode, String.valueOf(Long.MAX_VALUE));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ // long to BigDecimal
+ assertEquals(Long.MAX_VALUE, p.getLongValue());
+ assertEquals(BigDecimal.valueOf(Long.MAX_VALUE), p.getDecimalValue());
+ p.close();
+
+ BigInteger biggie = BigInteger.valueOf(Long.MAX_VALUE).multiply(BigInteger.TEN);
+ p = createParser(mode, String.valueOf(biggie));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ // BigInteger to BigDecimal
+ assertEquals(biggie, p.getBigIntegerValue());
+ assertEquals(new BigDecimal(biggie), p.getDecimalValue());
+ p.close();
+
+ }
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/read/NumberParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/NumberParsingTest.java
index 6c8da43..dab984b 100644
--- a/src/test/java/com/fasterxml/jackson/core/read/NumberParsingTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/read/NumberParsingTest.java
@@ -496,6 +496,7 @@
}
}
+
/*
/**********************************************************
/* Tests for invalid access
@@ -594,6 +595,19 @@
assertNull(p.nextToken());
}
+ public void testInvalidNumber() throws Exception {
+ for (int mode : ALL_MODES) {
+ JsonParser p = createParser(mode, " -foo ");
+ try {
+ p.nextToken();
+ fail("Should not pass");
+ } catch (JsonParseException e) {
+ verifyException(e, "Unexpected character ('f'");
+ }
+ p.close();
+ }
+ }
+
/*
/**********************************************************
/* Helper methods
diff --git a/src/test/java/com/fasterxml/jackson/core/read/ParserScopeMatchingTest.java b/src/test/java/com/fasterxml/jackson/core/read/ParserScopeMatchingTest.java
index 55c261c..fbe42fc 100644
--- a/src/test/java/com/fasterxml/jackson/core/read/ParserScopeMatchingTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/read/ParserScopeMatchingTest.java
@@ -25,20 +25,14 @@
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(2, p.getIntValue());
try {
p.nextToken();
+ fail("Expected an exception for unclosed ARRAY (mode: "+mode+")");
} catch (JsonParseException pe) {
verifyException(pe, "expected close marker for ARRAY");
- return;
- } catch (IOException ie) {
- // DataInput behaves bit differently
- if (mode == MODE_DATA_INPUT) {
- verifyException(ie, "end-of-input");
- return;
- }
}
- fail("Expected an exception for unclosed ARRAY");
}
public void testUnclosedObject() throws Exception
@@ -57,15 +51,9 @@
try {
p.nextToken();
- fail("Expected an exception for unclosed OBJECT");
+ fail("Expected an exception for unclosed OBJECT (mode: "+mode+")");
} catch (JsonParseException pe) {
verifyException(pe, "expected close marker for OBJECT");
- } catch (IOException ie) {
- // DataInput behaves bit differently
- if (mode == MODE_DATA_INPUT) {
- verifyException(ie, "end-of-input");
- return;
- }
}
}
diff --git a/src/test/java/com/fasterxml/jackson/core/read/TrailingCommasTest.java b/src/test/java/com/fasterxml/jackson/core/read/TrailingCommasTest.java
new file mode 100644
index 0000000..5ca9eb3
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/read/TrailingCommasTest.java
@@ -0,0 +1,328 @@
+package com.fasterxml.jackson.core.read;
+
+import com.fasterxml.jackson.core.BaseTest;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonParser.Feature;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.json.UTF8DataInputJsonParser;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class TrailingCommasTest extends BaseTest {
+
+ private final JsonFactory factory;
+ private final HashSet<JsonParser.Feature> features;
+ private final int mode;
+
+ public TrailingCommasTest(int mode, List<Feature> features) {
+ this.factory = new JsonFactory();
+ this.features = new HashSet<JsonParser.Feature>(features);
+
+ for (JsonParser.Feature feature : features) {
+ factory.enable(feature);
+ }
+
+ this.mode = mode;
+ }
+
+ @Parameterized.Parameters(name = "Mode {0}, Features {1}")
+ public static Collection<Object[]> getTestCases() {
+ ArrayList<Object[]> cases = new ArrayList<Object[]>();
+
+ for (int mode : ALL_MODES) {
+ cases.add(new Object[]{mode, Collections.emptyList()});
+ cases.add(new Object[]{mode, Arrays.asList(Feature.ALLOW_MISSING_VALUES)});
+ cases.add(new Object[]{mode, Arrays.asList(Feature.ALLOW_TRAILING_COMMA)});
+ cases.add(new Object[]{mode, Arrays.asList(Feature.ALLOW_MISSING_VALUES, Feature.ALLOW_TRAILING_COMMA)});
+ }
+
+ return cases;
+ }
+
+ @SuppressWarnings("resource")
+ @Test
+ public void testArrayBasic() throws Exception {
+ String json = "[\"a\", \"b\"]";
+
+ JsonParser p = createParser(factory, mode, json);
+
+ assertEquals(JsonToken.START_ARRAY, p.nextToken());
+
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("a", p.getText());
+
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("b", p.getText());
+
+ assertEquals(JsonToken.END_ARRAY, p.nextToken());
+ assertEnd(p);
+ }
+
+ @SuppressWarnings("resource")
+ @Test
+ public void testArrayInnerComma() throws Exception {
+ String json = "[\"a\",, \"b\"]";
+
+ JsonParser p = createParser(factory, mode, json);
+
+ assertEquals(JsonToken.START_ARRAY, p.nextToken());
+
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("a", p.getText());
+
+ if (!features.contains(Feature.ALLOW_MISSING_VALUES)) {
+ assertUnexpected(p, ',');
+ return;
+ }
+
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("b", p.getText());
+
+ assertEquals(JsonToken.END_ARRAY, p.nextToken());
+ assertEnd(p);
+ }
+
+ @SuppressWarnings("resource")
+ @Test
+ public void testArrayLeadingComma() throws Exception {
+ String json = "[,\"a\", \"b\"]";
+
+ JsonParser p = createParser(factory, mode, json);
+
+ assertEquals(JsonToken.START_ARRAY, p.nextToken());
+
+ if (!features.contains(Feature.ALLOW_MISSING_VALUES)) {
+ assertUnexpected(p, ',');
+ return;
+ }
+
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("a", p.getText());
+
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("b", p.getText());
+
+ assertEquals(JsonToken.END_ARRAY, p.nextToken());
+ assertEnd(p);
+ p.close();
+ }
+
+ @Test
+ public void testArrayTrailingComma() throws Exception {
+ String json = "[\"a\", \"b\",]";
+
+ JsonParser p = createParser(factory, mode, json);
+
+ assertEquals(JsonToken.START_ARRAY, p.nextToken());
+
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("a", p.getText());
+
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("b", p.getText());
+
+ // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES
+ if (features.contains(Feature.ALLOW_TRAILING_COMMA)) {
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertEnd(p);
+ } else if (features.contains(Feature.ALLOW_MISSING_VALUES)) {
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertEnd(p);
+ } else {
+ assertUnexpected(p, ']');
+ }
+ p.close();
+ }
+
+ @Test
+ public void testArrayTrailingCommas() throws Exception {
+ String json = "[\"a\", \"b\",,]";
+
+ JsonParser p = createParser(factory, mode, json);
+
+ assertEquals(JsonToken.START_ARRAY, p.nextToken());
+
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("a", p.getText());
+
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("b", p.getText());
+
+ // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES
+ if (features.contains(Feature.ALLOW_MISSING_VALUES) &&
+ features.contains(Feature.ALLOW_TRAILING_COMMA)) {
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertEnd(p);
+ } else if (features.contains(Feature.ALLOW_MISSING_VALUES)) {
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertEnd(p);
+ } else {
+ assertUnexpected(p, ',');
+ }
+ p.close();
+ }
+
+ @Test
+ public void testArrayTrailingCommasTriple() throws Exception {
+ String json = "[\"a\", \"b\",,,]";
+
+ JsonParser p = createParser(factory, mode, json);
+
+ assertEquals(JsonToken.START_ARRAY, p.nextToken());
+
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("a", p.getText());
+
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("b", p.getText());
+
+ // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES
+ if (features.contains(Feature.ALLOW_MISSING_VALUES) &&
+ features.contains(Feature.ALLOW_TRAILING_COMMA)) {
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertEnd(p);
+ } else if (features.contains(Feature.ALLOW_MISSING_VALUES)) {
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertEnd(p);
+ } else {
+ assertUnexpected(p, ',');
+ }
+ p.close();
+ }
+
+ @Test
+ public void testObjectBasic() throws Exception {
+ String json = "{\"a\": true, \"b\": false}";
+
+ JsonParser p = createParser(factory, mode, json);
+
+ assertEquals(JsonToken.START_OBJECT, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("a", p.getText());
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("b", p.getText());
+ assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+
+ assertEquals(JsonToken.END_OBJECT, p.nextToken());
+ assertEnd(p);
+ p.close();
+ }
+
+ @Test
+ public void testObjectInnerComma() throws Exception {
+ String json = "{\"a\": true,, \"b\": false}";
+
+ JsonParser p = createParser(factory, mode, json);
+
+ assertEquals(JsonToken.START_OBJECT, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("a", p.getText());
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+
+ assertUnexpected(p, ',');
+ p.close();
+ }
+
+ @Test
+ public void testObjectLeadingComma() throws Exception {
+ String json = "{,\"a\": true, \"b\": false}";
+
+ JsonParser p = createParser(factory, mode, json);
+
+ assertEquals(JsonToken.START_OBJECT, p.nextToken());
+
+ assertUnexpected(p, ',');
+ p.close();
+ }
+
+ @Test
+ public void testObjectTrailingComma() throws Exception {
+ String json = "{\"a\": true, \"b\": false,}";
+
+ JsonParser p = createParser(factory, mode, json);
+
+ assertEquals(JsonToken.START_OBJECT, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("a", p.getText());
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("b", p.getText());
+ assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+
+ if (features.contains(Feature.ALLOW_TRAILING_COMMA)) {
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ assertEnd(p);
+ } else {
+ assertUnexpected(p, '}');
+ }
+ p.close();
+ }
+
+ @Test
+ public void testObjectTrailingCommas() throws Exception {
+ String json = "{\"a\": true, \"b\": false,,}";
+
+ JsonParser p = createParser(factory, mode, json);
+
+ assertEquals(JsonToken.START_OBJECT, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("a", p.getText());
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("b", p.getText());
+ assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+
+ assertUnexpected(p, ',');
+ p.close();
+ }
+
+ private void assertEnd(JsonParser p) throws IOException {
+ // Issue #325
+ if (!(p instanceof UTF8DataInputJsonParser)) {
+ JsonToken next = p.nextToken();
+ assertNull("expected end of stream but found " + next, next);
+ }
+ }
+
+ private void assertUnexpected(JsonParser p, char c) throws IOException {
+ try {
+ p.nextToken();
+ fail("No exception thrown");
+ } catch (Exception e) {
+ verifyException(e, String.format("Unexpected character ('%s' (code %d))", c, (int) c));
+ }
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/testsupport/MockDataInput.java b/src/test/java/com/fasterxml/jackson/core/testsupport/MockDataInput.java
index bdd71b2..11e8f31 100644
--- a/src/test/java/com/fasterxml/jackson/core/testsupport/MockDataInput.java
+++ b/src/test/java/com/fasterxml/jackson/core/testsupport/MockDataInput.java
@@ -42,7 +42,7 @@
public byte readByte() throws IOException {
int ch = _input.read();
if (ch < 0) {
- throw new IOException("End-of-input for readByte()");
+ throw new EOFException("End-of-input for readByte()");
}
return (byte) ch;
}
diff --git a/src/test/java/com/fasterxml/jackson/core/type/TypeReferenceTest.java b/src/test/java/com/fasterxml/jackson/core/type/TypeReferenceTest.java
index 1bf326b..4dd70d0 100644
--- a/src/test/java/com/fasterxml/jackson/core/type/TypeReferenceTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/type/TypeReferenceTest.java
@@ -7,6 +7,122 @@
// Not much to test, but exercise to prevent code coverage tool from showing all red for package
public class TypeReferenceTest extends BaseTest
{
+ static class BogusResolvedType extends ResolvedType {
+ private final boolean _refType;
+
+ public BogusResolvedType(boolean isRefType) {
+ _refType = isRefType;
+ }
+
+ @Override
+ public Class<?> getRawClass() {
+ return null;
+ }
+
+ @Override
+ public boolean hasRawClass(Class<?> clz) {
+ return false;
+ }
+
+ @Override
+ public boolean isAbstract() {
+ return false;
+ }
+
+ @Override
+ public boolean isConcrete() {
+ return false;
+ }
+
+ @Override
+ public boolean isThrowable() {
+ return false;
+ }
+
+ @Override
+ public boolean isArrayType() {
+ return false;
+ }
+
+ @Override
+ public boolean isEnumType() {
+ return false;
+ }
+
+ @Override
+ public boolean isInterface() {
+ return false;
+ }
+
+ @Override
+ public boolean isPrimitive() {
+ return false;
+ }
+
+ @Override
+ public boolean isFinal() {
+ return false;
+ }
+
+ @Override
+ public boolean isContainerType() {
+ return false;
+ }
+
+ @Override
+ public boolean isCollectionLikeType() {
+ return false;
+ }
+
+ @Override
+ public boolean isMapLikeType() {
+ return false;
+ }
+
+ @Override
+ public boolean hasGenericTypes() {
+ return false;
+ }
+
+ @Override
+ public ResolvedType getKeyType() {
+ return null;
+ }
+
+ @Override
+ public ResolvedType getContentType() {
+ return null;
+ }
+
+ @Override
+ public ResolvedType getReferencedType() {
+ if (_refType) {
+ return this;
+ }
+ return null;
+ }
+
+ @Override
+ public int containedTypeCount() {
+ return 0;
+ }
+
+ @Override
+ public ResolvedType containedType(int index) {
+ return null;
+ }
+
+ @Override
+ public String containedTypeName(int index) {
+ return null;
+ }
+
+ @Override
+ public String toCanonical() {
+ return null;
+ }
+ }
+
public void testSimple()
{
TypeReference<?> ref = new TypeReference<List<String>>() { };
@@ -24,4 +140,11 @@
verifyException(e, "without actual type information");
}
}
+
+ public void testResolvedType() {
+ ResolvedType type1 = new BogusResolvedType(false);
+ assertFalse(type1.isReferenceType());
+ ResolvedType type2 = new BogusResolvedType(true);
+ assertTrue(type2.isReferenceType());
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/core/util/TestDelegates.java b/src/test/java/com/fasterxml/jackson/core/util/TestDelegates.java
index 8fe7db3..e74d715 100644
--- a/src/test/java/com/fasterxml/jackson/core/util/TestDelegates.java
+++ b/src/test/java/com/fasterxml/jackson/core/util/TestDelegates.java
@@ -1,13 +1,177 @@
package com.fasterxml.jackson.core.util;
import java.io.*;
+import java.util.Iterator;
import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.JsonParser.NumberType;
+import com.fasterxml.jackson.core.type.ResolvedType;
+import com.fasterxml.jackson.core.type.TypeReference;
public class TestDelegates extends com.fasterxml.jackson.core.BaseTest
{
+ static class POJO {
+ public int x = 3;
+ }
+
+ static class BogusCodec extends ObjectCodec
+ {
+ public Object pojoWritten;
+ public TreeNode treeWritten;
+
+ @Override
+ public Version version() {
+ return Version.unknownVersion();
+ }
+
+ @Override
+ public <T> T readValue(JsonParser p, Class<T> valueType) {
+ return null;
+ }
+ @Override
+ public <T> T readValue(JsonParser p, TypeReference<?> valueTypeRef) {
+ return null;
+ }
+ @Override
+ public <T> T readValue(JsonParser p, ResolvedType valueType) {
+ return null;
+ }
+ @Override
+ public <T> Iterator<T> readValues(JsonParser p, Class<T> valueType) {
+ return null;
+ }
+ @Override
+ public <T> Iterator<T> readValues(JsonParser p,
+ TypeReference<?> valueTypeRef) throws IOException {
+ return null;
+ }
+ @Override
+ public <T> Iterator<T> readValues(JsonParser p, ResolvedType valueType) {
+ return null;
+ }
+ @Override
+ public void writeValue(JsonGenerator gen, Object value) throws IOException {
+ gen.writeString("pojo");
+ pojoWritten = value;
+ }
+
+ @Override
+ public <T extends TreeNode> T readTree(JsonParser p) {
+ return null;
+ }
+ @Override
+ public void writeTree(JsonGenerator gen, TreeNode tree) throws IOException {
+ gen.writeString("tree");
+ treeWritten = tree;
+ }
+
+ @Override
+ public TreeNode createObjectNode() {
+ return null;
+ }
+ @Override
+ public TreeNode createArrayNode() {
+ return null;
+ }
+ @Override
+ public JsonParser treeAsTokens(TreeNode n) {
+ return null;
+ }
+ @Override
+ public <T> T treeToValue(TreeNode n, Class<T> valueType) {
+ return null;
+ }
+ }
+
+ static class BogusTree implements TreeNode {
+ @Override
+ public JsonToken asToken() {
+ return null;
+ }
+
+ @Override
+ public NumberType numberType() {
+ return null;
+ }
+
+ @Override
+ public int size() {
+ return 0;
+ }
+
+ @Override
+ public boolean isValueNode() {
+ return false;
+ }
+
+ @Override
+ public boolean isContainerNode() {
+ return false;
+ }
+
+ @Override
+ public boolean isMissingNode() {
+ return false;
+ }
+
+ @Override
+ public boolean isArray() {
+ return false;
+ }
+
+ @Override
+ public boolean isObject() {
+ return false;
+ }
+
+ @Override
+ public TreeNode get(String fieldName) {
+ return null;
+ }
+
+ @Override
+ public TreeNode get(int index) {
+ return null;
+ }
+
+ @Override
+ public TreeNode path(String fieldName) {
+ return null;
+ }
+
+ @Override
+ public TreeNode path(int index) {
+ return null;
+ }
+
+ @Override
+ public Iterator<String> fieldNames() {
+ return null;
+ }
+
+ @Override
+ public TreeNode at(JsonPointer ptr) {
+ return null;
+ }
+
+ @Override
+ public TreeNode at(String jsonPointerExpression) {
+ return null;
+ }
+
+ @Override
+ public JsonParser traverse() {
+ return null;
+ }
+
+ @Override
+ public JsonParser traverse(ObjectCodec codec) {
+ return null;
+ }
+ }
+
private final JsonFactory JSON_F = new JsonFactory();
-
+
/**
* Test default, non-overridden parser delegate.
*/
@@ -94,7 +258,7 @@
StringWriter sw = new StringWriter();
JsonGenerator jg = new JsonGeneratorDelegate(JSON_F.createGenerator(sw), false) {
@Override
- public void writeFieldName(String name) throws IOException, JsonGenerationException {
+ public void writeFieldName(String name) throws IOException {
super.writeFieldName(name+"-test");
super.writeBoolean(true);
super.writeFieldName(name);
@@ -107,4 +271,26 @@
jp.close();
jg.close();
}
+
+ @SuppressWarnings("resource")
+ public void testGeneratorWithCodec() throws IOException
+ {
+ BogusCodec codec = new BogusCodec();
+ StringWriter sw = new StringWriter();
+ JsonGenerator g0 = JSON_F.createGenerator(sw);
+ g0.setCodec(codec);
+ JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0, false);
+ del.writeStartArray();
+ POJO pojo = new POJO();
+ del.writeObject(pojo);
+ TreeNode tree = new BogusTree();
+ del.writeTree(tree);
+ del.writeEndArray();
+ del.close();
+
+ assertEquals("[\"pojo\",\"tree\"]", sw.toString());
+
+ assertSame(tree, codec.treeWritten);
+ assertSame(pojo, codec.pojoWritten);
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/core/util/TestTextBuffer.java b/src/test/java/com/fasterxml/jackson/core/util/TestTextBuffer.java
index ab1d23a..997a7cb 100644
--- a/src/test/java/com/fasterxml/jackson/core/util/TestTextBuffer.java
+++ b/src/test/java/com/fasterxml/jackson/core/util/TestTextBuffer.java
@@ -13,6 +13,9 @@
tb.append('a');
tb.append(new char[] { 'X', 'b' }, 1, 1);
tb.append("c", 0, 1);
+ // all fits within one buffer so it is efficient...
+ assertTrue(tb.hasTextAsCharacters());
+
assertEquals(3, tb.contentsAsArray().length);
assertEquals("abc", tb.toString());
@@ -21,7 +24,7 @@
public void testLonger()
{
- TextBuffer tb = new TextBuffer(new BufferRecycler());
+ TextBuffer tb = new TextBuffer(null);
for (int i = 0; i < 2000; ++i) {
tb.append("abc", 0, 3);
}
@@ -31,51 +34,54 @@
tb.resetWithShared(new char[] { 'a' }, 0, 1);
assertEquals(1, tb.toString().length());
+ assertTrue(tb.hasTextAsCharacters());
}
- public void testLongAppend()
- {
- final int len = TextBuffer.MAX_SEGMENT_LEN * 3 / 2;
- StringBuilder sb = new StringBuilder(len);
- for (int i = 0; i < len; ++i) {
- sb.append('x');
- }
- final String STR = sb.toString();
- final String EXP = "a" + STR + "c";
+ public void testLongAppend()
+ {
+ final int len = TextBuffer.MAX_SEGMENT_LEN * 3 / 2;
+ StringBuilder sb = new StringBuilder(len);
+ for (int i = 0; i < len; ++i) {
+ sb.append('x');
+ }
+ final String STR = sb.toString();
+ final String EXP = "a" + STR + "c";
- // ok: first test with String:
- TextBuffer tb = new TextBuffer(new BufferRecycler());
- tb.append('a');
- tb.append(STR, 0, len);
- tb.append('c');
- assertEquals(len+2, tb.size());
- assertEquals(EXP, tb.contentsAsString());
+ // ok: first test with String:
+ TextBuffer tb = new TextBuffer(new BufferRecycler());
+ tb.append('a');
+ tb.append(STR, 0, len);
+ tb.append('c');
+ assertEquals(len+2, tb.size());
+ assertEquals(EXP, tb.contentsAsString());
- // then char[]
- tb = new TextBuffer(new BufferRecycler());
- tb.append('a');
- tb.append(STR.toCharArray(), 0, len);
- tb.append('c');
- assertEquals(len+2, tb.size());
- assertEquals(EXP, tb.contentsAsString());
- }
+ // then char[]
+ tb = new TextBuffer(new BufferRecycler());
+ tb.append('a');
+ tb.append(STR.toCharArray(), 0, len);
+ tb.append('c');
+ assertEquals(len+2, tb.size());
+ assertEquals(EXP, tb.contentsAsString());
+ }
- // [Core#152]
- public void testExpand()
- {
- TextBuffer tb = new TextBuffer(new BufferRecycler());
- char[] buf = tb.getCurrentSegment();
+ // [core#152]
+ public void testExpand()
+ {
+ TextBuffer tb = new TextBuffer(new BufferRecycler());
+ char[] buf = tb.getCurrentSegment();
- while (buf.length < 500 * 1000) {
- char[] old = buf;
- buf = tb.expandCurrentSegment();
- if (old.length >= buf.length) {
- fail("Expected buffer of "+old.length+" to expand, did not, length now "+buf.length);
- }
- }
- }
+ while (buf.length < 500 * 1000) {
+ char[] old = buf;
+ buf = tb.expandCurrentSegment();
+ if (old.length >= buf.length) {
+ fail("Expected buffer of "+old.length+" to expand, did not, length now "+buf.length);
+ }
+ }
+ tb.resetWithString("Foobar");
+ assertEquals("Foobar", tb.contentsAsString());
+ }
- // [Core#182]
+ // [core#182]
public void testEmpty() {
TextBuffer tb = new TextBuffer(new BufferRecycler());
tb.resetWithEmpty();
diff --git a/src/test/java/com/fasterxml/jackson/failing/TokenVerifyingParserFiltering330Test.java b/src/test/java/com/fasterxml/jackson/failing/TokenVerifyingParserFiltering330Test.java
new file mode 100644
index 0000000..88cf4de
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TokenVerifyingParserFiltering330Test.java
@@ -0,0 +1,137 @@
+package com.fasterxml.jackson.failing;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.JsonParser.NumberType;
+import com.fasterxml.jackson.core.filter.FilteringParserDelegate;
+import com.fasterxml.jackson.core.filter.TokenFilter;
+
+// Tests for [core#330]
+public class TokenVerifyingParserFiltering330Test extends BaseTest
+{
+ static class NameMatchFilter extends TokenFilter
+ {
+ private final Set<String> _names;
+
+ public NameMatchFilter(String... names) {
+ _names = new HashSet<String>(Arrays.asList(names));
+ }
+
+ @Override
+ public TokenFilter includeElement(int index) {
+ return this;
+ }
+
+ @Override
+ public TokenFilter includeProperty(String name) {
+ if (_names.contains(name)) {
+ return TokenFilter.INCLUDE_ALL;
+ }
+ return this;
+ }
+
+ @Override
+ protected boolean _includeScalar() { return false; }
+ }
+
+ /*
+ /**********************************************************
+ /* Test methods
+ /**********************************************************
+ */
+
+ private final JsonFactory JSON_F = new JsonFactory();
+
+ private final String SIMPLE = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}");
+
+ @SuppressWarnings("resource")
+ public void testBasicSingleMatchFilteringWithPath() throws Exception
+ {
+ JsonParser p0 = JSON_F.createParser(SIMPLE);
+ JsonParser p = new FilteringParserDelegate(p0,
+ new NameMatchFilter("value"),
+ true, // includePath
+ false // multipleMatches
+ );
+
+// {'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}
+ String result = readAndWrite(JSON_F, p);
+ assertEquals(aposToQuotes("{'ob':{'value':3}}"), result);
+ }
+
+ @SuppressWarnings("resource")
+ public void testTokensSingleMatchWithPath() throws Exception
+ {
+ JsonParser p0 = JSON_F.createParser(SIMPLE);
+ JsonParser p = new FilteringParserDelegate(p0,
+ new NameMatchFilter("value"),
+ true, // includePath
+ false // multipleMatches
+ );
+
+ assertFalse(p.hasCurrentToken());
+ assertNull(p.getCurrentToken());
+ assertEquals(JsonTokenId.ID_NO_TOKEN, p.getCurrentTokenId());
+ assertFalse(p.isExpectedStartObjectToken());
+ assertFalse(p.isExpectedStartArrayToken());
+
+// {'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}
+// String result = readAndWrite(JSON_F, p);
+// assertEquals(aposToQuotes("{'ob':{'value':3}}"), result);
+
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertEquals(JsonToken.START_OBJECT, p.getCurrentToken());
+ assertTrue(p.isExpectedStartObjectToken());
+ assertFalse(p.isExpectedStartArrayToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals(JsonToken.FIELD_NAME, p.getCurrentToken());
+ assertEquals("ob", p.getCurrentName());
+// assertEquals("ob", p.getText());
+
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertEquals("ob", p.getCurrentName());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("value", p.getCurrentName());
+ assertEquals("value", p.getText());
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(JsonToken.VALUE_NUMBER_INT, p.getCurrentToken());
+ assertEquals(NumberType.INT, p.getNumberType());
+ assertEquals(3, p.getIntValue());
+ assertEquals("value", p.getCurrentName());
+
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ assertEquals(JsonToken.END_OBJECT, p.getCurrentToken());
+
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ assertEquals(JsonToken.END_OBJECT, p.getCurrentToken());
+
+ p.clearCurrentToken();
+ assertNull(p.getCurrentToken());
+
+ p.close();
+ }
+
+ @SuppressWarnings("resource")
+ public void testSkippingForSingleWithPath() throws Exception
+ {
+ JsonParser p0 = JSON_F.createParser(SIMPLE);
+ JsonParser p = new FilteringParserDelegate(p0,
+ new NameMatchFilter("value"),
+ true, // includePath
+ false // multipleMatches
+ );
+
+// assertEquals(aposToQuotes("{'ob':{'value':3}}"), result);
+
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ p.skipChildren();
+ assertEquals(JsonToken.END_OBJECT, p.getCurrentToken());
+ assertNull(p.nextToken());
+ }
+}