Merge branch '2.7'
diff --git a/.travis.yml b/.travis.yml
index d9b2ad4..75db9cc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,7 +14,7 @@
branches:
only:
- master
- - "2.5"
+ - "2.7"
- "2.6"
env:
diff --git a/pom.xml b/pom.xml
index e165e57..5860f49 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,13 +3,13 @@
<parent>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-parent</artifactId>
- <version>2.7</version>
+ <version>2.8</version>
</parent>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<name>Jackson-core</name>
- <version>2.7.7-SNAPSHOT</version>
+ <version>2.8.2-SNAPSHOT</version>
<packaging>bundle</packaging>
<description>Core Jackson abstractions, basic JSON streaming API implementation</description>
<inceptionYear>2008</inceptionYear>
@@ -23,10 +23,16 @@
</scm>
<properties>
- <!-- 02-Oct-2015, tatu: Retain Java6/JDK1.6 compatibility for streaming for Jackson 2.7 -->
+ <!-- 29-Apr-2016, tatu: Retain Java6/JDK1.6 compatibility for streaming for Jackson 2.8 -->
<javac.src.version>1.6</javac.src.version>
<javac.target.version>1.6</javac.target.version>
+ <!-- 04-May-2016, tatu: Bundle-plugin 3.x seems to require Java 7, so to
+ build for Java 6 need to downgrade here to last working 2.x version
+ (2.5.4 had some issues wrt shading)
+ -->
+ <version.plugin.bundle>2.5.3</version.plugin.bundle>
+
<osgi.export>com.fasterxml.jackson.core;version=${project.version},
com.fasterxml.jackson.core.*;version=${project.version}
</osgi.export>
@@ -36,6 +42,14 @@
<packageVersion.package>${project.groupId}.json</packageVersion.package>
</properties>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
<build>
<plugins>
<plugin>
@@ -64,7 +78,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
- <version>3.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -103,8 +116,7 @@
<encoding>UTF-8</encoding>
<maxmemory>1g</maxmemory>
<links>
- <!-- JDK, other Jackson pkgs -->
- <link>http://docs.oracle.com/javase/6/docs/api/</link>
+ <link>http://docs.oracle.com/javase/7/docs/api/</link>
</links>
<excludePackageNames>${javadoc.package.exclude}</excludePackageNames>
<bootclasspath>${sun.boot.class.path}</bootclasspath>
diff --git a/release-notes/CREDITS b/release-notes/CREDITS
index 463d97e..c5e54ca 100644
--- a/release-notes/CREDITS
+++ b/release-notes/CREDITS
@@ -78,6 +78,12 @@
Lokesh Kumar N (LokeshN@github)
* Contributed #209: Make use of `_allowMultipleMatches` in `FilteringParserDelegate`
(2.7.4)
+ * Contributed fix for #117: Support for missing values (non-compliant JSON)
+ (2.8.0)
+ * Contributed implementation for #86: Allow inclusion of request body for JsonParseException
+ (2.8.0)
+ * Contributed implementation for #285: Allow inclusion of request body for JsonParseException
+ (2.8.0)
Tanguy Leroux (tlrx@github)
* Reported, contributed fix for #280: FilteringGeneratorDelegate.writeUTF8String()
@@ -88,3 +94,11 @@
* Reported #307: JsonGenerationException: Split surrogate on writeRaw() input thrown for
input of a certain size
(2.7.7)
+
+Mikael Staldal (mikaelstaldal@github)
+ * Contributed fix for #265: `JsonStringEncoder` should allow passing `CharSequence`
+ (2.8.0)
+
+Kevin Gallardo (newkek@github)
+ * Reported #296: JsonParserSequence skips a token on a switched Parser
+ (2.8.0)
diff --git a/release-notes/VERSION b/release-notes/VERSION
index 1251004..a964afa 100644
--- a/release-notes/VERSION
+++ b/release-notes/VERSION
@@ -14,6 +14,41 @@
=== Releases ===
------------------------------------------------------------------------
+2.8.1 (20-Jul-2016)
+
+No changes since 2.8.0
+
+2.8.0 (04-Jul-2016)
+
+#86: Allow inclusion of request body for `JsonParseException`
+ (contributed by LokeshN)
+#117: Add `JsonParser.Feature.ALLOW_MISSING_VALUES` to support for missing values
+ (contributed by LokeshN)
+#136: Add `JsonpCharacterEscapes` for easier handling of potential problems
+ with JSONP and rare but technically allowed \u2028 and \u2029 linefeed characters
+#253: Add `JsonGenerator. writeEmbeddedObject()` to allow writes of opaque native types
+ (suggested by Gregoire C)
+#255: Relax ownership checks for buffers not to require increase in size
+#257: Add `writeStartObject(Object pojo)` to streamline assignment of current value
+#265: `JsonStringEncoder` should allow passing `CharSequence`
+ (contributed by Mikael S)
+#276: Add support for serializing using `java.io.DataOutput`
+#277: Add new scalar-array write methods for `int`/`long`/`double` cases
+#279: Support `DataInput` for parsing
+#280: Add `JsonParser.finishToken()` to force full, non-lazy reading of current token
+#281: Add `JsonEOFException` as sub-class of `JsonParseException`
+#282: Fail to report error for trying to write field name outside Object (root level)
+#285: Add `JsonParser.getText(Writer)`
+ (contributed by LokesN)
+#290: Add `JsonGenerator.canWriteFormattedNumbers()` for introspection
+#294: Add `JsonGenerator.writeFieldId(long)` method to support binary formats
+ with non-String keys
+#296: `JsonParserSequence` skips a token on a switched Parser
+ (reported by Kevin G)
+- Add `JsonParser.currentToken()` and `JsonParser.currentTokenId()` as replacements
+ for `getCurrentToken()` and `getCurrentTokenId()`, respectively. Existing methods
+ will likely be deprecated in 2.9.
+
2.7.7 (not yet released)
#307: JsonGenerationException: Split surrogate on writeRaw() input thrown for
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java
index b052507..4c975d2 100644
--- a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java
+++ b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java
@@ -442,6 +442,9 @@
* @since 2.1
*/
public boolean canUseSchema(FormatSchema schema) {
+ if (schema == null){
+ return false;
+ }
String ourFormat = getFormatName();
return (ourFormat != null) && ourFormat.equals(schema.getSchemaType());
}
@@ -875,7 +878,7 @@
public JsonParser createParser(String content) throws IOException, JsonParseException {
final int strLen = content.length();
// Actually, let's use this for medium-sized content, up to 64kB chunk (32kb char)
- if (_inputDecorator != null || strLen > 0x8000 || !canUseCharArrays()) {
+ if ((_inputDecorator != null) || (strLen > 0x8000) || !canUseCharArrays()) {
// easier to just wrap in a Reader than extend InputDecorator; or, if content
// is too long for us to copy it over
return createParser(new StringReader(content));
@@ -910,6 +913,14 @@
false);
}
+ /**
+ * @since 2.8
+ */
+ public JsonParser createParser(DataInput in) throws IOException {
+ IOContext ctxt = _createContext(in, false);
+ return _createParser(_decorate(in, ctxt), ctxt);
+ }
+
/*
/**********************************************************
/* Parser factories (old ones, pre-2.2)
@@ -1148,6 +1159,28 @@
return _createGenerator(_decorate(w, ctxt), ctxt);
}
+ /**
+ * Method for constructing generator for writing content using specified
+ * {@link DataOutput} instance.
+ *
+ * @since 2.8
+ */
+ public JsonGenerator createGenerator(DataOutput out, JsonEncoding enc) throws IOException {
+ return createGenerator(_createDataOutputWrapper(out), enc);
+ }
+
+ /**
+ * Convenience method for constructing generator that uses default
+ * encoding of the format (UTF-8 for JSON and most other data formats).
+ *<p>
+ * Note: there are formats that use fixed encoding (like most binary data formats).
+ *
+ * @since 2.8
+ */
+ public JsonGenerator createGenerator(DataOutput out) throws IOException {
+ return createGenerator(_createDataOutputWrapper(out), JsonEncoding.UTF8);
+ }
+
/*
/**********************************************************
/* Generator factories, old (pre-2.2)
@@ -1286,6 +1319,26 @@
_objectCodec, _byteSymbolCanonicalizer, _rootCharSymbols, _factoryFeatures);
}
+ /**
+ * @since 2.8
+ */
+ protected JsonParser _createParser(DataInput input, IOContext ctxt) throws IOException
+ {
+ // 13-May-2016, tatu: Need to take care not to accidentally create JSON parser for
+ // non-JSON input. So, bit unclean but...
+ String format = getFormatName();
+ if (format != FORMAT_NAME_JSON) { // NOTE: only ensure override; full equality NOT needed
+ throw new UnsupportedOperationException(String.format(
+ "InputData source not (yet?) support for this format (%s)", format));
+ }
+ // Also: while we can't do full bootstrapping (due to read-ahead limitations), should
+ // at least handle possible UTF-8 BOM
+ int firstByte = ByteSourceJsonBootstrapper.skipUTF8BOM(input);
+ ByteQuadsCanonicalizer can = _byteSymbolCanonicalizer.makeChild(_factoryFeatures);
+ return new UTF8DataInputJsonParser(ctxt, _parserFeatures, input,
+ _objectCodec, can, firstByte);
+ }
+
/*
/**********************************************************
/* Factory methods used by factory for creating generator instances,
@@ -1368,7 +1421,7 @@
}
return in;
}
-
+
/**
* @since 2.4
*/
@@ -1383,6 +1436,19 @@
}
/**
+ * @since 2.8
+ */
+ protected final DataInput _decorate(DataInput in, IOContext ctxt) throws IOException {
+ if (_inputDecorator != null) {
+ DataInput in2 = _inputDecorator.decorate(ctxt, in);
+ if (in2 != null) {
+ return in2;
+ }
+ }
+ return in;
+ }
+
+ /**
* @since 2.4
*/
protected final OutputStream _decorate(OutputStream out, IOContext ctxt) throws IOException {
@@ -1422,24 +1488,21 @@
*/
public BufferRecycler _getBufferRecycler()
{
- BufferRecycler br;
-
/* 23-Apr-2015, tatu: Let's allow disabling of buffer recycling
* scheme, for cases where it is considered harmful (possibly
* on Android, for example)
*/
if (isEnabled(Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING)) {
SoftReference<BufferRecycler> ref = _recyclerRef.get();
- br = (ref == null) ? null : ref.get();
+ BufferRecycler br = (ref == null) ? null : ref.get();
if (br == null) {
br = new BufferRecycler();
_recyclerRef.set(new SoftReference<BufferRecycler>(br));
}
- } else {
- br = new BufferRecycler();
+ return br;
}
- return br;
+ return new BufferRecycler();
}
/**
@@ -1451,6 +1514,13 @@
}
/**
+ * @since 2.8
+ */
+ protected OutputStream _createDataOutputWrapper(DataOutput out) {
+ return new DataOutputAsStream(out);
+ }
+
+ /**
* Helper methods used for constructing an optimal stream for
* parsers to use, when input is to be read from an URL.
* This helps when reading file content via URL.
@@ -1465,7 +1535,7 @@
*/
String host = url.getHost();
if (host == null || host.length() == 0) {
- // [Issue#48]: Let's try to avoid probs with URL encoded stuff
+ // [core#48]: Let's try to avoid probs with URL encoded stuff
String path = url.getPath();
if (path.indexOf('%') < 0) {
return new FileInputStream(url.getPath());
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java
index a30da35..92be7cd 100644
--- a/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java
+++ b/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java
@@ -290,53 +290,6 @@
/*
/**********************************************************
- /* Forward-compatibility additions in 2.7.5: placeholders
- /* for additions that will be in 2.8.0
- /**********************************************************
- */
-
- // @since 2.7.5 (as placeholder, NOT full impl)
- public boolean canWriteFormattedNumbers() { return false; }
-
- // @since 2.7.5: default impl that should work fine
- public void writeStartObject(Object forValue) throws IOException
- {
- writeStartObject();
- setCurrentValue(forValue);
- }
-
- // @since 2.7.5: default impl that should work fine
- public void writeArray(int[] array, int offset, int length) throws IOException
- {
- writeStartArray();
- for (int i = offset, end = offset+length; i < end; ++i) {
- writeNumber(array[i]);
- }
- writeEndArray();
- }
-
- // @since 2.7.5: default impl that should work fine
- public void writeArray(long[] array, int offset, int length) throws IOException
- {
- writeStartArray();
- for (int i = offset, end = offset+length; i < end; ++i) {
- writeNumber(array[i]);
- }
- writeEndArray();
- }
-
- // @since 2.7.5: default impl that should work fine
- public void writeArray(double[] array, int offset, int length) throws IOException
- {
- writeStartArray();
- for (int i = offset, end = offset+length; i < end; ++i) {
- writeNumber(array[i]);
- }
- writeEndArray();
- }
-
- /*
- /**********************************************************
/* Public API, Feature configuration
/**********************************************************
*/
@@ -394,7 +347,7 @@
*
* @return This parser object, to allow chaining of calls
*
- * @deprecated Since 2.7, use {@link #overrideStdFeatures(int, int)} instead
+ * @deprecated Since 2.7, use {@link #overrideStdFeatures(int, int)} instead -- remove from 2.9
*/
@Deprecated
public abstract JsonGenerator setFeatureMask(int values);
@@ -745,6 +698,20 @@
*/
public boolean canOmitFields() { return true; }
+ /**
+ * Introspection method to call to check whether it is possible
+ * to write numbers using {@link #writeNumber(java.lang.String)}
+ * using possible custom format, or not. Typically textual formats
+ * allow this (and JSON specifically does), whereas binary formats
+ * do not allow this (except by writing them as Strings).
+ * Usual reason for calling this method is to check whether custom
+ * formatting of numbers may be applied by higher-level code (databinding)
+ * or not.
+ *
+ * @since 2.8
+ */
+ public boolean canWriteFormattedNumbers() { return false; }
+
/*
/**********************************************************
/* Public API, write methods, structural
@@ -803,6 +770,26 @@
public abstract void writeStartObject() throws IOException;
/**
+ * Method for writing starting marker of a JSON Object value
+ * (character '{'; plus possible white space decoration
+ * if pretty-printing is enabled), to represent Java given
+ * as the argument. Argument is offered as metadata, but more
+ * importantly it should be assigned as the "current value"
+ * for the Object content that gets constructed and initialized.
+ *<p>
+ * Object values can be written in any context where values
+ * are allowed: meaning everywhere except for when
+ * a field name is expected.
+ *
+ * @since 2.8.
+ */
+ public void writeStartObject(Object forValue) throws IOException
+ {
+ writeStartObject();
+ setCurrentValue(forValue);
+ }
+
+ /**
* Method for writing closing marker of a JSON Object value
* (character '}'; plus possible white space decoration
* if pretty-printing is enabled).
@@ -838,6 +825,98 @@
*/
public abstract void writeFieldName(SerializableString name) throws IOException;
+ /**
+ * Alternative to {@link #writeFieldName(String)} that may be used
+ * in cases where property key is of numeric type; either where
+ * underlying format supports such notion (some binary formats do,
+ * unlike JSON), or for convenient conversion into String presentation.
+ * Default implementation will simply convert id into <code>String</code>
+ * and call {@link #writeFieldName(String)}.
+ *
+ * @since 2.8
+ */
+ public void writeFieldId(long id) throws IOException {
+ writeFieldName(Long.toString(id));
+ }
+
+ /*
+ /**********************************************************
+ /* Public API, write methods, scalar arrays (2.8)
+ /**********************************************************
+ */
+
+ /**
+ * Value write method that can be called to write a single
+ * array (sequence of {@link JsonToken#START_ARRAY}, zero or
+ * more {@link JsonToken#VALUE_NUMBER_INT}, {@link JsonToken#END_ARRAY})
+ *
+ * @since 2.8
+ *
+ * @param array Array that contains values to write
+ * @param offset Offset of the first element to write, within array
+ * @param length Number of elements in array to write, from `offset` to `offset + len - 1`
+ */
+ public void writeArray(int[] array, int offset, int length) throws IOException
+ {
+ if (array == null) {
+ throw new IllegalArgumentException("null array");
+ }
+ _verifyOffsets(array.length, offset, length);
+ writeStartArray();
+ for (int i = offset, end = offset+length; i < end; ++i) {
+ writeNumber(array[i]);
+ }
+ writeEndArray();
+ }
+
+ /**
+ * Value write method that can be called to write a single
+ * array (sequence of {@link JsonToken#START_ARRAY}, zero or
+ * more {@link JsonToken#VALUE_NUMBER_INT}, {@link JsonToken#END_ARRAY})
+ *
+ * @since 2.8
+ *
+ * @param array Array that contains values to write
+ * @param offset Offset of the first element to write, within array
+ * @param length Number of elements in array to write, from `offset` to `offset + len - 1`
+ */
+ public void writeArray(long[] array, int offset, int length) throws IOException
+ {
+ if (array == null) {
+ throw new IllegalArgumentException("null array");
+ }
+ _verifyOffsets(array.length, offset, length);
+ writeStartArray();
+ for (int i = offset, end = offset+length; i < end; ++i) {
+ writeNumber(array[i]);
+ }
+ writeEndArray();
+ }
+
+ /**
+ * Value write method that can be called to write a single
+ * array (sequence of {@link JsonToken#START_ARRAY}, zero or
+ * more {@link JsonToken#VALUE_NUMBER_FLOAT}, {@link JsonToken#END_ARRAY})
+ *
+ * @since 2.8
+ *
+ * @param array Array that contains values to write
+ * @param offset Offset of the first element to write, within array
+ * @param length Number of elements in array to write, from `offset` to `offset + len - 1`
+ */
+ public void writeArray(double[] array, int offset, int length) throws IOException
+ {
+ if (array == null) {
+ throw new IllegalArgumentException("null array");
+ }
+ _verifyOffsets(array.length, offset, length);
+ writeStartArray();
+ for (int i = offset, end = offset+length; i < end; ++i) {
+ writeNumber(array[i]);
+ }
+ writeEndArray();
+ }
+
/*
/**********************************************************
/* Public API, write methods, text/String values
@@ -1109,7 +1188,7 @@
/*
/**********************************************************
- /* Public API, write methods, other value types
+ /* Public API, write methods, numeric
/**********************************************************
*/
@@ -1215,6 +1294,12 @@
*/
public abstract void writeNumber(String encodedValue) throws IOException;
+ /*
+ /**********************************************************
+ /* Public API, write methods, other value types
+ /**********************************************************
+ */
+
/**
* Method for outputting literal JSON boolean value (one of
* Strings 'true' and 'false').
@@ -1234,6 +1319,17 @@
*/
public abstract void writeNull() throws IOException;
+ /**
+ * Method that can be called on backends that support passing opaque datatypes of
+ * non-JSON formats
+ *
+ * @since 2.8
+ */
+ public void writeEmbeddedObject(Object object) throws IOException {
+ throw new JsonGenerationException("No native support for writing embedded objects",
+ this);
+ }
+
/*
/**********************************************************
/* Public API, write methods, Native Ids (type, object)
@@ -1283,6 +1379,73 @@
throw new JsonGenerationException("No native support for writing Type Ids", this);
}
+ // 24-May-2016, tatu: Looks like this won't quite make it in 2.8... too
+ // many open questions on whether return value may be used and such to
+ // really close the loop. But leaving code sample in, in case we can resolve it
+ // it for 2.9.
+
+ /*
+ * Replacement method for {@link #writeTypeId(Object)} which is called
+ * regardless of whether format has native type ids. If it does have native
+ * type ids, those are to be used (if configuration allows this), if not,
+ * structural type id inclusion is to be used. For JSON, for example, no
+ * native type ids exist and structural inclusion is always used.
+ *<p>
+ * NOTE: from databind perspective, only "as-wrapper-array", "as-wrapper-object" and
+ * "as-property" inclusion styles call this method; the remaining "as-external-property"
+ * mechanism always uses writes type id value as simple property.
+ *
+ * @param inclStyle Kind of inclusion; {@link JsonToken#START_ARRAY} for "as-wrapper-array",
+ * {@link JsonToken#START_OBJECT} for "as-wrapper-object" and {@link JsonToken#FIELD_NAME}
+ * for "as-property"
+ * @param forValue Java object for which type is being written; not used by standard mechanism
+ * @param valueShape Expected shape of the value to write, as expressed by the first token (for
+ * structural type), or any of scalar types for non-structured values (typically
+ * just {@link JsonToken#VALUE_STRING} -- exact token not required, just the fact it's scalar)
+ * @param typeId Type id to write
+ * @param propertyName Name of property to use, in case of "as-property" inclusion style
+ *
+ * @since 2.8
+ */
+ /*
+ public Object writeTypeSuffix(JsonToken inclStyle, Object forValue, JsonToken valueShape,
+ String typeId, String propertyName) throws IOException
+ {
+ if (inclStyle == JsonToken.FIELD_NAME) { // as-property
+ if (typeId == null) { // should not include `null` type id in any form with this style
+ writeStartObject();
+ } else if (valueShape == JsonToken.START_OBJECT) {
+ if (canWriteTypeId()) {
+ writeTypeId(typeId);
+ writeStartObject();
+ } else {
+ writeStartObject();
+ writeStringField(propertyName, typeId);
+ }
+ } else if (valueShape == JsonToken.START_ARRAY) {
+ if (canWriteTypeId()) {
+ writeTypeId(typeId);
+ writeStartArray();
+ } else {
+ writeStartArray();
+ writeString(typeId);
+ }
+ } else { // any scalar
+ if (canWriteTypeId()) {
+ writeTypeId(typeId);
+ }
+ }
+ return JsonToken.END_OBJECT;
+ }
+ if (inclStyle == JsonToken.START_ARRAY) { // as-wrapper-array
+ } else if (inclStyle == JsonToken.START_OBJECT) { // as-wrapper-object
+
+ } else {
+ throw new JsonGenerationException("Unrecognized inclusion style: "+inclStyle, this);
+ }
+ }
+ */
+
/*
/**********************************************************
/* Public API, write methods, serializing Java objects
@@ -1518,7 +1681,7 @@
*/
public void copyCurrentEvent(JsonParser p) throws IOException
{
- JsonToken t = p.getCurrentToken();
+ JsonToken t = p.currentToken();
// sanity check; what to do?
if (t == null) {
_reportError("No current event to copy");
@@ -1526,6 +1689,7 @@
switch (t.id()) {
case ID_NOT_AVAILABLE:
_reportError("No current event to copy");
+ break; // never gets here
case ID_START_OBJECT:
writeStartObject();
break;
@@ -1621,7 +1785,7 @@
*/
public void copyCurrentStructure(JsonParser p) throws IOException
{
- JsonToken t = p.getCurrentToken();
+ JsonToken t = p.currentToken();
if (t == null) {
_reportError("No current event to copy");
}
@@ -1730,6 +1894,18 @@
}
/**
+ * @since 2.8
+ */
+ protected final void _verifyOffsets(int arrayLength, int offset, int length)
+ {
+ if ((offset < 0) || (offset + length) > arrayLength) {
+ throw new IllegalArgumentException(String.format(
+ "invalid argument(s) (offset=%d, length=%d) for input array of %d element",
+ offset, length, arrayLength));
+ }
+ }
+
+ /**
* Helper method to try to call appropriate write method for given
* untyped Object. At this point, no structural conversions should be done,
* only simple basic types are to be coerced as necessary.
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonParseException.java b/src/main/java/com/fasterxml/jackson/core/JsonParseException.java
index d373ad7..9176ad2 100644
--- a/src/main/java/com/fasterxml/jackson/core/JsonParseException.java
+++ b/src/main/java/com/fasterxml/jackson/core/JsonParseException.java
@@ -5,6 +5,8 @@
package com.fasterxml.jackson.core;
+import com.fasterxml.jackson.core.util.RequestPayload;
+
/**
* Exception type for parsing problems, used when non-well-formed content
* (content that does not conform to JSON syntax as per specification)
@@ -13,9 +15,19 @@
public class JsonParseException extends JsonProcessingException {
private static final long serialVersionUID = 2L; // 2.7
- // since 2.7.4
+ // transient since 2.7.4
protected transient JsonParser _processor;
+ /**
+ * Optional payload that can be assigned to pass along for error reporting
+ * or handling purposes. Core streaming parser implementations DO NOT
+ * initialize this; it is up to using applications and frameworks to
+ * populate it.
+ *
+ * @since 2.8
+ */
+ protected RequestPayload _requestPayload;
+
@Deprecated // since 2.7
public JsonParseException(String msg, JsonLocation loc) {
super(msg, loc);
@@ -65,6 +77,8 @@
/**
* Fluent method that may be used to assign originating {@link JsonParser},
* to be accessed using {@link #getProcessor()}.
+ *<p>
+ * NOTE: `this` instance is modified and no new instance is constructed.
*
* @since 2.7
*/
@@ -73,8 +87,57 @@
return this;
}
+ /**
+ * Fluent method that may be used to assign payload to this exception,
+ * to let recipient access it for diagnostics purposes.
+ *<p>
+ * NOTE: `this` instance is modified and no new instance is constructed.
+ *
+ * @since 2.8
+ */
+ public JsonParseException withRequestPayload(RequestPayload p) {
+ _requestPayload = p;
+ return this;
+ }
+
@Override
public JsonParser getProcessor() {
return _processor;
}
+
+ /**
+ * Method that may be called to find payload that was being parsed, if
+ * one was specified for parser that threw this Exception.
+ *
+ * @return request body, if payload was specified; `null` otherwise
+ *
+ * @since 2.8
+ */
+ public RequestPayload getRequestPayload() {
+ return _requestPayload;
+ }
+
+ /**
+ * The method returns the String representation of the request payload if
+ * one was specified for parser that threw this Exception.
+ *
+ * @return request body as String, if payload was specified; `null` otherwise
+ *
+ * @since 2.8
+ */
+ public String getRequestPayloadAsString() {
+ return (_requestPayload != null) ? _requestPayload.toString() : null;
+ }
+
+ /**
+ * Overriding the getMessage() to include the request body
+ */
+ @Override
+ public String getMessage() {
+ String msg = super.getMessage();
+ if (_requestPayload != null) {
+ msg += "\nRequest payload : " + _requestPayload.toString();
+ }
+ return msg;
+ }
}
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonParser.java b/src/main/java/com/fasterxml/jackson/core/JsonParser.java
index c941521..2fd0011 100644
--- a/src/main/java/com/fasterxml/jackson/core/JsonParser.java
+++ b/src/main/java/com/fasterxml/jackson/core/JsonParser.java
@@ -11,6 +11,7 @@
import java.util.Iterator;
import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.core.util.RequestPayload;
/**
* Base class that defines public API for reading JSON content.
@@ -208,7 +209,25 @@
*
* @since 2.6
*/
- IGNORE_UNDEFINED(false)
+ IGNORE_UNDEFINED(false),
+
+ /**
+ * Feature allows the support for "missing" values in a JSON array: missing
+ * value meaning sequence of two commas, without value in-between but only
+ * optional white space.
+ * Enabling this feature will expose "missing" values as {@link JsonToken#VALUE_NULL}
+ * tokens, which typically become Java nulls in arrays and {@link java.util.Collection}
+ * in data-binding.
+ * <p>
+ * For example, enabling this feature will represent a JSON array <code>["value1",,"value3",]</code>
+ * as <code>["value1", null, "value3", null]</code>
+ * <p>
+ * Since the JSON specification does not allow missing values this is a non-compliant JSON
+ * feature and is disabled by default.
+ *
+ * @since 2.8
+ */
+ ALLOW_MISSING_VALUES(false)
;
/**
@@ -260,6 +279,13 @@
* are enabled.
*/
protected int _features;
+
+ /**
+ * Optional container that holds the request payload which will be displayed on JSON parsing error.
+ *
+ * @since 2.8
+ */
+ protected transient RequestPayload _requestPayload;
/*
/**********************************************************
@@ -334,12 +360,39 @@
}
}
+ /**
+ * Sets the payload to be passed if {@link JsonParseException} is thrown.
+ *
+ * @since 2.8
+ */
+ public void setRequestPayloadOnError(RequestPayload payload) {
+ _requestPayload = payload;
+ }
+
+ /**
+ * Sets the byte[] request payload and the charset
+ *
+ * @since 2.8
+ */
+ public void setRequestPayloadOnError(byte[] payload, String charset) {
+ _requestPayload = (payload == null) ? null : new RequestPayload(payload, charset);
+ }
+
+ /**
+ * Sets the String request payload
+ *
+ * @since 2.8
+ */
+ public void setRequestPayloadOnError(String payload) {
+ _requestPayload = (payload == null) ? null : new RequestPayload(payload);
+ }
+
/*
/**********************************************************
/* Format support
/**********************************************************
*/
-
+
/**
* Method to call to make this parser use specified schema. Method must
* be called before trying to parse any content, right after parser instance
@@ -605,7 +658,7 @@
* @return Next token from the stream, if any found, or null
* to indicate end-of-input
*/
- public abstract JsonToken nextToken() throws IOException, JsonParseException;
+ public abstract JsonToken nextToken() throws IOException;
/**
* Iteration method that will advance stream enough
@@ -624,7 +677,7 @@
* parsers, {@link JsonToken#NOT_AVAILABLE} if no tokens were
* available yet)
*/
- public abstract JsonToken nextValue() throws IOException, JsonParseException;
+ public abstract JsonToken nextValue() throws IOException;
/**
* Method that fetches next token (as if calling {@link #nextToken}) and
@@ -640,7 +693,7 @@
* @param str Property name to compare next token to (if next token is
* <code>JsonToken.FIELD_NAME</code>)
*/
- public boolean nextFieldName(SerializableString str) throws IOException, JsonParseException {
+ public boolean nextFieldName(SerializableString str) throws IOException {
return (nextToken() == JsonToken.FIELD_NAME) && str.getValue().equals(getCurrentName());
}
@@ -651,7 +704,7 @@
*
* @since 2.5
*/
- public String nextFieldName() throws IOException, JsonParseException {
+ public String nextFieldName() throws IOException {
return (nextToken() == JsonToken.FIELD_NAME) ? getCurrentName() : null;
}
@@ -666,7 +719,7 @@
* but may be faster for parser to process, and can therefore be used if caller
* expects to get a String value next from input.
*/
- public String nextTextValue() throws IOException, JsonParseException {
+ public String nextTextValue() throws IOException {
return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
}
@@ -679,9 +732,9 @@
* return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
*</pre>
* but may be faster for parser to process, and can therefore be used if caller
- * expects to get a String value next from input.
+ * expects to get an int value next from input.
*/
- public int nextIntValue(int defaultValue) throws IOException, JsonParseException {
+ public int nextIntValue(int defaultValue) throws IOException {
return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
}
@@ -694,9 +747,9 @@
* return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
*</pre>
* but may be faster for parser to process, and can therefore be used if caller
- * expects to get a String value next from input.
+ * expects to get a long value next from input.
*/
- public long nextLongValue(long defaultValue) throws IOException, JsonParseException {
+ public long nextLongValue(long defaultValue) throws IOException {
return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
}
@@ -712,9 +765,9 @@
* return null;
*</pre>
* but may be faster for parser to process, and can therefore be used if caller
- * expects to get a String value next from input.
+ * expects to get a Boolean value next from input.
*/
- public Boolean nextBooleanValue() throws IOException, JsonParseException {
+ public Boolean nextBooleanValue() throws IOException {
JsonToken t = nextToken();
if (t == JsonToken.VALUE_TRUE) { return Boolean.TRUE; }
if (t == JsonToken.VALUE_FALSE) { return Boolean.FALSE; }
@@ -735,8 +788,26 @@
* will call {@link #nextToken} to point to the next
* available token, if any.
*/
- public abstract JsonParser skipChildren() throws IOException, JsonParseException;
-
+ public abstract JsonParser skipChildren() throws IOException;
+
+ /**
+ * Method that may be used to force full handling of the current token
+ * so that even if lazy processing is enabled, the whole contents are
+ * read for possible retrieval. This is usually used to ensure that
+ * the token end location is available, as well as token contents
+ * (similar to what calling, say {@link #getTextCharacters()}, would
+ * achieve).
+ *<p>
+ * Note that for many dataformat implementations this method
+ * will not do anything; this is the default implementation unless
+ * overridden by sub-classes.
+ *
+ * @since 2.8
+ */
+ public void finishToken() throws IOException {
+ ; // nothing
+ }
+
/**
* Method that can be called to determine whether this parser
* is closed or not. If it is closed, no new tokens can be
@@ -763,8 +834,12 @@
* if any: null before any tokens have been read, and
* after end-of-input has been encountered, as well as
* if the current token has been explicitly cleared.
+ *
+ * @since 2.8
*/
- public abstract JsonToken getCurrentToken();
+ public JsonToken currentToken() {
+ return getCurrentToken();
+ }
/**
* Method similar to {@link #getCurrentToken()} but that returns an
@@ -775,10 +850,22 @@
* Note, however, that effect may not be big enough to matter: make sure
* to profile performance before deciding to use this method.
*
- * @since 2.3
+ * @since 2.8
*
* @return <code>int</code> matching one of constants from {@link JsonTokenId}.
*/
+ public int currentTokenId() {
+ return getCurrentTokenId();
+ }
+
+ /**
+ * Alias for {@link #currentToken()}, will be deprecated in Jackson 2.9
+ */
+ public abstract JsonToken getCurrentToken();
+
+ /**
+ * Alias for {@link #currentTokenId()}, will be deprecated in Jackson 2.9
+ */
public abstract int getCurrentTokenId();
/**
@@ -797,7 +884,7 @@
/**
* Method that is functionally equivalent to:
*<code>
- * return getCurrentTokenId() == id
+ * return currentTokenId() == id
*</code>
* but may be more efficiently implemented.
*<p>
@@ -812,7 +899,7 @@
/**
* Method that is functionally equivalent to:
*<code>
- * return getCurrentTokenId() == id
+ * return currentToken() == t
*</code>
* but may be more efficiently implemented.
*<p>
@@ -869,7 +956,7 @@
*<p>
* Default implementation is equivalent to:
*<pre>
- * getCurrentToken() == JsonToken.START_ARRAY
+ * currentToken() == JsonToken.START_ARRAY
*</pre>
* but may be overridden by custom parser implementations.
*
@@ -877,15 +964,15 @@
* start-array marker (such {@link JsonToken#START_ARRAY});
* false if not.
*/
- public boolean isExpectedStartArrayToken() { return getCurrentToken() == JsonToken.START_ARRAY; }
+ public boolean isExpectedStartArrayToken() { return currentToken() == JsonToken.START_ARRAY; }
/**
* Similar to {@link #isExpectedStartArrayToken()}, but checks whether stream
* currently points to {@link JsonToken#START_OBJECT}.
- *
+ *
* @since 2.5
*/
- public boolean isExpectedStartObjectToken() { return getCurrentToken() == JsonToken.START_OBJECT; }
+ public boolean isExpectedStartObjectToken() { return currentToken() == JsonToken.START_OBJECT; }
/*
/**********************************************************
@@ -944,6 +1031,31 @@
public abstract String getText() throws IOException;
/**
+ * Method to read the textual representation of the current token in chunks and
+ * pass it to the given Writer.
+ * Conceptually same as calling:
+ *<pre>
+ * writer.write(parser.getText());
+ *</pre>
+ * but should typically be more efficient as longer content does need to
+ * be combined into a single <code>String</code> to return, and write
+ * can occur directly from intermediate buffers Jackson uses.
+ *
+ * @return The number of characters written to the Writer
+ *
+ * @since 2.8
+ */
+ public int getText(Writer writer) throws IOException, UnsupportedOperationException
+ {
+ String str = getText();
+ if (str == null) {
+ return 0;
+ }
+ writer.write(str);
+ return str.length();
+ }
+
+ /**
* Method similar to {@link #getText}, but that will return
* underlying (unmodifiable) character array that contains
* textual value, instead of constructing a String object
@@ -1173,11 +1285,12 @@
* may be thrown to indicate numeric overflow/underflow.
*/
public boolean getBooleanValue() throws IOException {
- JsonToken t = getCurrentToken();
+ JsonToken t = currentToken();
if (t == JsonToken.VALUE_TRUE) return true;
if (t == JsonToken.VALUE_FALSE) return false;
throw new JsonParseException(this,
- String.format("Current token (%s) not of boolean type", t));
+ String.format("Current token (%s) not of boolean type", t))
+ .withRequestPayload(_requestPayload);
}
/**
@@ -1187,9 +1300,12 @@
*<p>
* Note: only some specialized parser implementations support
* embedding of objects (usually ones that are facades on top
- * of non-streaming sources, such as object trees).
+ * of non-streaming sources, such as object trees). One exception
+ * is access to binary content (whether via base64 encoding or not)
+ * which typically is accessible using this method, as well as
+ * {@link #getBinaryValue()}.
*/
- public abstract Object getEmbeddedObject() throws IOException;
+ public Object getEmbeddedObject() throws IOException { return null; }
/*
/**********************************************************
@@ -1583,7 +1699,8 @@
* based on current state of the parser
*/
protected JsonParseException _constructError(String msg) {
- return new JsonParseException(this, msg);
+ return new JsonParseException(this, msg)
+ .withRequestPayload(_requestPayload);
}
/**
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonStreamContext.java b/src/main/java/com/fasterxml/jackson/core/JsonStreamContext.java
index 5e8ff17..ddadc92 100644
--- a/src/main/java/com/fasterxml/jackson/core/JsonStreamContext.java
+++ b/src/main/java/com/fasterxml/jackson/core/JsonStreamContext.java
@@ -77,7 +77,10 @@
* Method for accessing simple type description of current context;
* either ROOT (for root-level values), OBJECT (for field names and
* values of JSON Objects) or ARRAY (for values of JSON Arrays)
+ *
+ * @deprecated Since 2.8 use {@link #typeDesc} instead
*/
+ @Deprecated // since 2.8
public final String getTypeDesc() {
switch (_type) {
case TYPE_ROOT: return "ROOT";
@@ -88,6 +91,18 @@
}
/**
+ * @since 2.8
+ */
+ public String typeDesc() {
+ switch (_type) {
+ case TYPE_ROOT: return "root";
+ case TYPE_ARRAY: return "Array";
+ case TYPE_OBJECT: return "Object";
+ }
+ return "?";
+ }
+
+ /**
* @return Number of entries that are complete and started.
*/
public final int getEntryCount() { return _index + 1; }
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonpCharacterEscapes.java b/src/main/java/com/fasterxml/jackson/core/JsonpCharacterEscapes.java
new file mode 100644
index 0000000..282bb42
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/JsonpCharacterEscapes.java
@@ -0,0 +1,46 @@
+package com.fasterxml.jackson.core;
+
+import com.fasterxml.jackson.core.io.CharacterEscapes;
+import com.fasterxml.jackson.core.io.SerializedString;
+
+/**
+ * Convenience {@link CharacterEscapes} implementation that escapes
+ * Unicode characters `0x2028` and `0x2029` (in addition to characters
+ * escaped otherwise), which are apparently considered linefeeds as
+ * per newer Javascript specifications, and consequently problematic
+ * when using JSONP (see https://en.wikipedia.org/wiki/JSONP).
+ *
+ * @since 2.8
+ */
+public class JsonpCharacterEscapes extends CharacterEscapes
+{
+ private static final long serialVersionUID = 1L;
+
+ private static final int[] asciiEscapes = CharacterEscapes.standardAsciiEscapesForJSON();
+ private static final SerializedString escapeFor2028 = new SerializedString("\\u2028");
+ private static final SerializedString escapeFor2029 = new SerializedString("\\u2029");
+
+ private static final JsonpCharacterEscapes sInstance = new JsonpCharacterEscapes();
+
+ public static JsonpCharacterEscapes instance() {
+ return sInstance;
+ }
+
+ @Override
+ public SerializableString getEscapeSequence(int ch)
+ {
+ switch (ch) {
+ case 0x2028:
+ return escapeFor2028;
+ case 0x2029:
+ return escapeFor2029;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public int[] getEscapeCodesForAscii() {
+ return asciiEscapes;
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/ObjectCodec.java b/src/main/java/com/fasterxml/jackson/core/ObjectCodec.java
index 649c89f..afa6aaa 100644
--- a/src/main/java/com/fasterxml/jackson/core/ObjectCodec.java
+++ b/src/main/java/com/fasterxml/jackson/core/ObjectCodec.java
@@ -26,9 +26,9 @@
{
protected ObjectCodec() { }
- // Since 2.3: need baseline implementation to avoid backwards compatibility
+ // Since 2.3
@Override
- public Version version() { return Version.unknownVersion(); }
+ public abstract Version version();
/*
/**********************************************************
@@ -46,8 +46,8 @@
* The reason is that due to type erasure, key and value types
* can not be introspected when using this method.
*/
- public abstract <T> T readValue(JsonParser jp, Class<T> valueType)
- throws IOException, JsonProcessingException;
+ public abstract <T> T readValue(JsonParser p, Class<T> valueType)
+ throws IOException;
/**
* Method to deserialize JSON content into a Java type, reference
@@ -56,8 +56,8 @@
* and specifically needs to be used if the root type is a
* parameterized (generic) container type.
*/
- public abstract <T> T readValue(JsonParser jp, TypeReference<?> valueTypeRef)
- throws IOException, JsonProcessingException;
+ public abstract <T> T readValue(JsonParser p, TypeReference<?> valueTypeRef)
+ throws IOException;
/**
* Method to deserialize JSON content into a POJO, type specified
@@ -65,30 +65,30 @@
* including containers like {@link java.util.Collection} and
* {@link java.util.Map}).
*/
- public abstract <T> T readValue(JsonParser jp, ResolvedType valueType)
- throws IOException, JsonProcessingException;
+ public abstract <T> T readValue(JsonParser p, ResolvedType valueType)
+ throws IOException;
/**
* Method for reading sequence of Objects from parser stream,
* all with same specified value type.
*/
- public abstract <T> Iterator<T> readValues(JsonParser jp, Class<T> valueType)
- throws IOException, JsonProcessingException;
+ public abstract <T> Iterator<T> readValues(JsonParser p, Class<T> valueType)
+ throws IOException;
/**
* Method for reading sequence of Objects from parser stream,
* all with same specified value type.
*/
- public abstract <T> Iterator<T> readValues(JsonParser jp, TypeReference<?> valueTypeRef)
- throws IOException, JsonProcessingException;
+ public abstract <T> Iterator<T> readValues(JsonParser p, TypeReference<?> valueTypeRef)
+ throws IOException;
/**
* Method for reading sequence of Objects from parser stream,
* all with same specified value type.
*/
- public abstract <T> Iterator<T> readValues(JsonParser jp, ResolvedType valueType)
- throws IOException, JsonProcessingException;
-
+ public abstract <T> Iterator<T> readValues(JsonParser p, ResolvedType valueType)
+ throws IOException;
+
/*
/**********************************************************
/* API for serialization (Object-to-JSON)
@@ -99,8 +99,7 @@
* Method to serialize given Java Object, using generator
* provided.
*/
- public abstract void writeValue(JsonGenerator jgen, Object value)
- throws IOException, JsonProcessingException;
+ public abstract void writeValue(JsonGenerator gen, Object value) throws IOException;
/*
/**********************************************************
@@ -116,15 +115,13 @@
* value event, not container). Empty or whitespace
* documents return null.
*
- * @return next tree from jp, or null if empty.
+ * @return next tree from p, or null if empty.
*/
@Override
- public abstract <T extends TreeNode> T readTree(JsonParser jp)
- throws IOException, JsonProcessingException;
+ public abstract <T extends TreeNode> T readTree(JsonParser p) throws IOException;
@Override
- public abstract void writeTree(JsonGenerator jg, TreeNode tree)
- throws IOException, JsonProcessingException;
+ public abstract void writeTree(JsonGenerator gen, TreeNode tree) throws IOException;
/**
* Method for construct root level Object nodes
diff --git a/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java b/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java
index 128fbcf..d2d05f2 100644
--- a/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java
+++ b/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java
@@ -6,8 +6,8 @@
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.json.DupDetector;
import com.fasterxml.jackson.core.json.JsonWriteContext;
+import com.fasterxml.jackson.core.json.PackageVersion;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
-import com.fasterxml.jackson.core.util.VersionUtil;
/**
* This base class implements part of API that a JSON generator exposes
@@ -125,7 +125,7 @@
* a simple generated class, with information extracted from Maven project file
* during build.
*/
- @Override public Version version() { return VersionUtil.versionFor(getClass()); }
+ @Override public Version version() { return PackageVersion.VERSION; }
@Override
public Object getCurrentValue() {
@@ -262,9 +262,11 @@
*/
/**
- * Note: co-variant return type.
+ * Note: type was co-variant until Jackson 2.7; reverted back to
+ * base type in 2.8 to allow for overriding by subtypes that use
+ * custom context type.
*/
- @Override public JsonWriteContext getOutputContext() { return _writeContext; }
+ @Override public JsonStreamContext getOutputContext() { return _writeContext; }
/*
/**********************************************************
@@ -277,6 +279,16 @@
//public void writeStartObject() throws IOException
//public void writeEndObject() throws IOException
+ @Override // since 2.8
+ public void writeStartObject(Object forValue) throws IOException
+ {
+ writeStartObject();
+ if ((_writeContext != null) && (forValue != null)) {
+ _writeContext.setCurrentValue(forValue);
+ }
+ setCurrentValue(forValue);
+ }
+
/*
/**********************************************************
/* Public API, write methods, textual
diff --git a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java
index df5e5a0..5b3f931 100644
--- a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java
+++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java
@@ -465,9 +465,6 @@
return false;
}
- // No embedded objects with base impl...
- @Override public Object getEmbeddedObject() throws IOException { return null; }
-
@SuppressWarnings("resource")
@Override // since 2.7
public byte[] getBinaryValue(Base64Variant variant) throws IOException
@@ -499,22 +496,10 @@
/*
/**********************************************************
- /* Low-level reading, other
- /**********************************************************
- */
-
- protected final void loadMoreGuaranteed() throws IOException {
- if (!loadMore()) { _reportInvalidEOF(); }
- }
-
- /*
- /**********************************************************
/* Abstract methods needed from sub-classes
/**********************************************************
*/
- protected abstract boolean loadMore() throws IOException;
- protected abstract void _finishString() throws IOException;
protected abstract void _closeInput() throws IOException;
/*
@@ -546,7 +531,12 @@
@Override
protected void _handleEOF() throws JsonParseException {
if (!_parsingContext.inRoot()) {
- _reportInvalidEOF(": expected close marker for "+_parsingContext.getTypeDesc()+" (from "+_parsingContext.getStartLocation(_ioContext.getSourceReference())+")");
+ String marker = _parsingContext.inArray() ? "Array" : "Object";
+ _reportInvalidEOF(String.format(
+ ": expected close marker for %s (start marker at %s)",
+ marker,
+ _parsingContext.getStartLocation(_ioContext.getSourceReference())),
+ null);
}
}
@@ -566,7 +556,7 @@
protected void _reportMismatchedEndMarker(int actCh, char expCh) throws JsonParseException {
String startDesc = ""+_parsingContext.getStartLocation(_ioContext.getSourceReference());
- _reportError("Unexpected close marker '"+((char) actCh)+"': expected '"+expCh+"' (for "+_parsingContext.getTypeDesc()+" starting at "+startDesc+")");
+ _reportError("Unexpected close marker '"+((char) actCh)+"': expected '"+expCh+"' (for "+_parsingContext.typeDesc()+" starting at "+startDesc+")");
}
/*
@@ -1038,9 +1028,8 @@
*/
if ((_numTypesValid & NR_DOUBLE) != 0) {
- /* Let's actually parse from String representation,
- * to avoid rounding errors that non-decimal floating operations
- * would incur
+ /* Let's actually parse from String representation, to avoid
+ * rounding errors that non-decimal floating operations could incur
*/
_numberBigDecimal = NumberInput.parseBigDecimal(getText());
} else if ((_numTypesValid & NR_BIGINT) != 0) {
@@ -1074,11 +1063,13 @@
}
protected void reportOverflowInt() throws IOException {
- _reportError("Numeric value ("+getText()+") out of range of int ("+Integer.MIN_VALUE+" - "+Integer.MAX_VALUE+")");
+ _reportError(String.format("Numeric value (%s) out of range of int (%d - %s)",
+ getText(), Integer.MIN_VALUE, Integer.MAX_VALUE));
}
protected void reportOverflowLong() throws IOException {
- _reportError("Numeric value ("+getText()+") out of range of long ("+Long.MIN_VALUE+" - "+Long.MAX_VALUE+")");
+ _reportError(String.format("Numeric value (%s) out of range of long (%d - %s)",
+ getText(), Long.MIN_VALUE, Long.MAX_VALUE));
}
/*
@@ -1119,7 +1110,6 @@
protected final int _decodeBase64Escape(Base64Variant b64variant, char ch, int index) throws IOException
{
- // 17-May-2011, tatu: As per [JACKSON-xxx], need to handle escaped chars
if (ch != '\\') {
throw reportInvalidBase64Char(b64variant, ch, index);
}
@@ -1163,4 +1153,22 @@
}
return new IllegalArgumentException(base);
}
+
+ /*
+ /**********************************************************
+ /* Stuff that was abstract and required before 2.8, but that
+ /* is not mandatory in 2.8 or above.
+ /**********************************************************
+ */
+
+ @Deprecated // since 2.8
+ protected void loadMoreGuaranteed() throws IOException {
+ if (!loadMore()) { _reportInvalidEOF(); }
+ }
+
+ @Deprecated // since 2.8
+ protected boolean loadMore() throws IOException { return false; }
+
+ // Can't declare as deprecated, for now, but shouldn't be needed
+ protected void _finishString() throws IOException { }
}
diff --git a/src/main/java/com/fasterxml/jackson/core/base/ParserMinimalBase.java b/src/main/java/com/fasterxml/jackson/core/base/ParserMinimalBase.java
index b6810a9..6448296 100644
--- a/src/main/java/com/fasterxml/jackson/core/base/ParserMinimalBase.java
+++ b/src/main/java/com/fasterxml/jackson/core/base/ParserMinimalBase.java
@@ -4,6 +4,7 @@
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.JsonParser.Feature;
+import com.fasterxml.jackson.core.io.JsonEOFException;
import com.fasterxml.jackson.core.io.NumberInput;
import com.fasterxml.jackson.core.util.ByteArrayBuilder;
import com.fasterxml.jackson.core.util.VersionUtil;
@@ -95,8 +96,14 @@
*/
@Override public abstract JsonToken nextToken() throws IOException;
- @Override public JsonToken getCurrentToken() { return _currToken; }
+ @Override public JsonToken currentToken() { return _currToken; }
+ @Override public int currentTokenId() {
+ final JsonToken t = _currToken;
+ return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
+ }
+
+ @Override public JsonToken getCurrentToken() { return _currToken; }
@Override public int getCurrentTokenId() {
final JsonToken t = _currToken;
return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
@@ -250,7 +257,7 @@
case ID_NULL:
return false;
case ID_EMBEDDED_OBJECT:
- Object value = this.getEmbeddedObject();
+ Object value = getEmbeddedObject();
if (value instanceof Boolean) {
return (Boolean) value;
}
@@ -293,7 +300,7 @@
case ID_NULL:
return 0;
case ID_EMBEDDED_OBJECT:
- Object value = this.getEmbeddedObject();
+ Object value = getEmbeddedObject();
if (value instanceof Number) {
return ((Number) value).intValue();
}
@@ -333,7 +340,7 @@
case ID_NULL:
return 0L;
case ID_EMBEDDED_OBJECT:
- Object value = this.getEmbeddedObject();
+ Object value = getEmbeddedObject();
if (value instanceof Number) {
return ((Number) value).longValue();
}
@@ -409,7 +416,6 @@
*/
protected void _decodeBase64(String str, ByteArrayBuilder builder, Base64Variant b64variant) throws IOException
{
- // just call helper method introduced in 2.2.3
try {
b64variant.decode(str, builder);
} catch (IllegalArgumentException e) {
@@ -451,17 +457,48 @@
}
protected void _reportInvalidEOF() throws JsonParseException {
- _reportInvalidEOF(" in "+_currToken);
+ _reportInvalidEOF(" in "+_currToken, _currToken);
}
- protected void _reportInvalidEOF(String msg) throws JsonParseException {
- _reportError("Unexpected end-of-input"+msg);
+ /**
+ * @since 2.8
+ */
+ protected void _reportInvalidEOFInValue(JsonToken type) throws JsonParseException {
+ String msg;
+ if (type == JsonToken.VALUE_STRING) {
+ msg = " in a String value";
+ } else if ((type == JsonToken.VALUE_NUMBER_INT)
+ || (type == JsonToken.VALUE_NUMBER_FLOAT)) {
+ msg = " in a Number value";
+ } else {
+ msg = " in a value";
+ }
+ _reportInvalidEOF(msg, type);
}
+ /**
+ * @since 2.8
+ */
+ protected void _reportInvalidEOF(String msg, JsonToken currToken) throws JsonParseException {
+ throw new JsonEOFException(this, currToken, "Unexpected end-of-input"+msg);
+ }
+
+ /**
+ * @deprecated Since 2.8 use {@link #_reportInvalidEOF(String, JsonToken)} instead
+ */
+ @Deprecated // since 2.8
protected void _reportInvalidEOFInValue() throws JsonParseException {
_reportInvalidEOF(" in a value");
}
-
+
+ /**
+ * @deprecated Since 2.8 use {@link #_reportInvalidEOF(String, JsonToken)} instead
+ */
+ @Deprecated // since 2.8
+ protected void _reportInvalidEOF(String msg) throws JsonParseException {
+ throw new JsonEOFException(this, null, "Unexpected end-of-input"+msg);
+ }
+
protected void _reportMissingRootWS(int ch) throws JsonParseException {
_reportUnexpectedChar(ch, "Expected space separating root-level values");
}
diff --git a/src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java b/src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java
index d395b11..8c23687 100644
--- a/src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java
+++ b/src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java
@@ -146,11 +146,16 @@
*/
@Override public JsonToken getCurrentToken() { return _currToken; }
+ @Override public JsonToken currentToken() { return _currToken; }
@Override public final int getCurrentTokenId() {
final JsonToken t = _currToken;
return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
}
+ @Override public final int currentTokenId() {
+ final JsonToken t = _currToken;
+ return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
+ }
@Override public boolean hasCurrentToken() { return _currToken != null; }
@Override public boolean hasTokenId(int id) {
diff --git a/src/main/java/com/fasterxml/jackson/core/io/DataOutputAsStream.java b/src/main/java/com/fasterxml/jackson/core/io/DataOutputAsStream.java
new file mode 100644
index 0000000..5fa8cad
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/DataOutputAsStream.java
@@ -0,0 +1,44 @@
+package com.fasterxml.jackson.core.io;
+
+import java.io.*;
+
+/**
+ * Helper class to support use of {@link DataOutput} for output, directly,
+ * without caller having to provide for implementation.
+ *
+ * @since 2.8
+ */
+public class DataOutputAsStream extends OutputStream
+{
+ protected final DataOutput _output;
+
+ public DataOutputAsStream(DataOutput out) {
+ super();
+ _output = out;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ _output.write(b);
+ }
+
+ @Override
+ public void write(byte b[]) throws IOException {
+ _output.write(b, 0, b.length);
+ }
+
+ @Override
+ public void write(byte b[], int offset, int length) throws IOException {
+ _output.write(b, offset, length);
+ }
+
+ // These are no-ops, base class impl works fine
+
+ /*
+ @Override
+ public void flush() throws IOException { }
+
+ @Override
+ public void close() throws IOException { }
+ */
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/io/IOContext.java b/src/main/java/com/fasterxml/jackson/core/io/IOContext.java
index a4675be..18f50c8 100644
--- a/src/main/java/com/fasterxml/jackson/core/io/IOContext.java
+++ b/src/main/java/com/fasterxml/jackson/core/io/IOContext.java
@@ -270,12 +270,17 @@
}
protected final void _verifyRelease(byte[] toRelease, byte[] src) {
- if ((toRelease != src) && (toRelease.length <= src.length)) { throw wrongBuf(); }
+ // 07-Mar-2016, tatu: As per [core#255], only prevent shrinking of buffer
+ if ((toRelease != src) && (toRelease.length < src.length)) { throw wrongBuf(); }
}
protected final void _verifyRelease(char[] toRelease, char[] src) {
- if ((toRelease != src) && (toRelease.length <= src.length)) { throw wrongBuf(); }
+ // 07-Mar-2016, tatu: As per [core#255], only prevent shrinking of buffer
+ if ((toRelease != src) && (toRelease.length < src.length)) { throw wrongBuf(); }
}
- private IllegalArgumentException wrongBuf() { return new IllegalArgumentException("Trying to release buffer not owned by the context"); }
+ private IllegalArgumentException wrongBuf() {
+ // sanity check failed; trying to return different, smaller buffer.
+ return new IllegalArgumentException("Trying to release buffer smaller than original");
+ }
}
diff --git a/src/main/java/com/fasterxml/jackson/core/io/InputDecorator.java b/src/main/java/com/fasterxml/jackson/core/io/InputDecorator.java
index cac1162..eb98a8c 100644
--- a/src/main/java/com/fasterxml/jackson/core/io/InputDecorator.java
+++ b/src/main/java/com/fasterxml/jackson/core/io/InputDecorator.java
@@ -49,7 +49,30 @@
*/
public abstract InputStream decorate(IOContext ctxt, byte[] src, int offset, int length)
throws IOException;
-
+
+ /**
+ * Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when
+ * creating parser given an {@link DataInput}, when this decorator
+ * has been registered.
+ *<p>
+ * Default implementation simply throws {@link UnsupportedOperationException}
+ *
+ * @param ctxt IO context in use (provides access to declared encoding).
+ * NOTE: at this point context may not have all information initialized;
+ * specifically auto-detected encoding is only available once parsing starts,
+ * which may occur only after this method is called.
+ * @param input Original input source
+ *
+ * @return InputStream to use; either 'input' as is, or decorator
+ * version that typically delogates to 'input'
+ *
+ * @since 2.8
+ */
+ public DataInput decorate(IOContext ctxt, DataInput input)
+ throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
/**
* Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when
* creating parser given an {@link Reader}, when this decorator
diff --git a/src/main/java/com/fasterxml/jackson/core/io/JsonEOFException.java b/src/main/java/com/fasterxml/jackson/core/io/JsonEOFException.java
new file mode 100644
index 0000000..3660c46
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/JsonEOFException.java
@@ -0,0 +1,37 @@
+package com.fasterxml.jackson.core.io;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+
+/**
+ * Specialized {@link JsonParseException} that is thrown when end-of-input
+ * is reached unexpectedly, either within token being decoded, or during
+ * skipping of intervening white-space that is not between root-level
+ * tokens (that is, is within JSON Object or JSON Array construct).
+ *
+ * @since 2.8
+ */
+public class JsonEOFException extends JsonParseException
+{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Type of token that was being decoded, if parser had enough information
+ * to recognize type (such as starting double-quote for Strings)
+ */
+ protected final JsonToken _token;
+
+ public JsonEOFException(JsonParser p, JsonToken token, String msg) {
+ super(p, msg);
+ _token = token;
+ }
+
+ /**
+ * Accessor for possibly available information about token that was being
+ * decoded while encountering end of input.
+ */
+ public JsonToken getTokenBeingDecoded() {
+ return _token;
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/io/JsonStringEncoder.java b/src/main/java/com/fasterxml/jackson/core/io/JsonStringEncoder.java
index 07956eb..85521cb 100644
--- a/src/main/java/com/fasterxml/jackson/core/io/JsonStringEncoder.java
+++ b/src/main/java/com/fasterxml/jackson/core/io/JsonStringEncoder.java
@@ -149,6 +149,44 @@
}
/**
+ * Method that will quote text contents using JSON standard quoting,
+ * and append results to a supplied {@link StringBuilder}.
+ * Use this variant if you have e.g. a {@link StringBuilder} and want to avoid superfluous copying of it.
+ *
+ * @since 2.8
+ */
+ public void quoteAsString(CharSequence input, StringBuilder output)
+ {
+ final int[] escCodes = CharTypes.get7BitOutputEscapes();
+ final int escCodeCount = escCodes.length;
+ int inPtr = 0;
+ final int inputLen = input.length();
+
+ outer:
+ while (inPtr < inputLen) {
+ tight_loop:
+ while (true) {
+ char c = input.charAt(inPtr);
+ if (c < escCodeCount && escCodes[c] != 0) {
+ break tight_loop;
+ }
+ output.append(c);
+ if (++inPtr >= inputLen) {
+ break outer;
+ }
+ }
+ // something to escape; 2 or 6-char variant?
+ char d = input.charAt(inPtr++);
+ int escCode = escCodes[d];
+ int length = (escCode < 0)
+ ? _appendNumeric(d, _qbuf)
+ : _appendNamed(escCode, _qbuf);
+ ;
+ output.append(_qbuf, 0, length);
+ }
+ }
+
+ /**
* Will quote given JSON String value using standard quoting, encode
* results as UTF-8, and return result as a byte array.
*/
diff --git a/src/main/java/com/fasterxml/jackson/core/io/NumberOutput.java b/src/main/java/com/fasterxml/jackson/core/io/NumberOutput.java
index 0cdf8c7..9c3e04b 100644
--- a/src/main/java/com/fasterxml/jackson/core/io/NumberOutput.java
+++ b/src/main/java/com/fasterxml/jackson/core/io/NumberOutput.java
@@ -2,53 +2,41 @@
public final class NumberOutput
{
- private final static char NC = (char) 0;
-
private static int MILLION = 1000000;
private static int BILLION = 1000000000;
- private static long TEN_BILLION_L = 10000000000L;
- private static long THOUSAND_L = 1000L;
+ private static long BILLION_L = 1000000000L;
private static long MIN_INT_AS_LONG = (long) Integer.MIN_VALUE;
private static long MAX_INT_AS_LONG = (long) Integer.MAX_VALUE;
+ final static String SMALLEST_INT = String.valueOf(Integer.MIN_VALUE);
final static String SMALLEST_LONG = String.valueOf(Long.MIN_VALUE);
- private final static char[] LEAD_3 = new char[4000];
- private final static char[] FULL_3 = new char[4000];
+ /**
+ * Encoded representations of 3-decimal-digit indexed values, where
+ * 3 LSB are ascii characters
+ *
+ * @since 2.8.2
+ */
+ private final static int[] TRIPLET_TO_CHARS = new int[1000];
+
static {
/* Let's fill it with NULLs for ignorable leading digits,
* and digit chars for others
*/
- int ix = 0;
+ int fullIx = 0;
for (int i1 = 0; i1 < 10; ++i1) {
- char f1 = (char) ('0' + i1);
- char l1 = (i1 == 0) ? NC : f1;
for (int i2 = 0; i2 < 10; ++i2) {
- char f2 = (char) ('0' + i2);
- char l2 = (i1 == 0 && i2 == 0) ? NC : f2;
for (int i3 = 0; i3 < 10; ++i3) {
- // Last is never to be empty
- char f3 = (char) ('0' + i3);
- LEAD_3[ix] = l1;
- LEAD_3[ix+1] = l2;
- LEAD_3[ix+2] = f3;
- FULL_3[ix] = f1;
- FULL_3[ix+1] = f2;
- FULL_3[ix+2] = f3;
- ix += 4;
+ int enc = ((i1 + '0') << 16)
+ | ((i2 + '0') << 8)
+ | (i3 + '0');
+ TRIPLET_TO_CHARS[fullIx++] = enc;
}
}
}
}
- private final static byte[] FULL_TRIPLETS_B = new byte[4000];
- static {
- for (int i = 0; i < 4000; ++i) {
- FULL_TRIPLETS_B[i] = (byte) FULL_3[i];
- }
- }
-
private final static String[] sSmallIntStrs = new String[] {
"0","1","2","3","4","5","6","7","8","9","10"
};
@@ -69,10 +57,9 @@
{
if (v < 0) {
if (v == Integer.MIN_VALUE) {
- /* Special case: no matching positive value within range;
- * let's then "upgrade" to long and output as such.
- */
- return outputLong((long) v, b, off);
+ // Special case: no matching positive value within range;
+ // let's then "upgrade" to long and output as such.
+ return _outputSmallestI(b, off);
}
b[off++] = '-';
v = -v;
@@ -81,16 +68,15 @@
if (v < MILLION) { // at most 2 triplets...
if (v < 1000) {
if (v < 10) {
- b[off++] = (char) ('0' + v);
- } else {
- off = leading3(v, b, off);
+ b[off] = (char) ('0' + v);
+ return off+1;
}
- } else {
- int thousands = v / 1000;
- v -= (thousands * 1000); // == value % 1000
- off = leading3(thousands, b, off);
- off = full3(v, b, off);
+ return _leading3(v, b, off);
}
+ int thousands = v / 1000;
+ v -= (thousands * 1000); // == value % 1000
+ off = _leading3(thousands, b, off);
+ off = _full3(v, b, off);
return off;
}
@@ -99,8 +85,7 @@
* handling 3 triplets. This is possible since we know we
* can have at most '2' as billion count.
*/
- boolean hasBillions = (v >= BILLION);
- if (hasBillions) {
+ if (v >= BILLION) {
v -= BILLION;
if (v >= BILLION) {
v -= BILLION;
@@ -108,6 +93,7 @@
} else {
b[off++] = '1';
}
+ return _outputFullBillion(v, b, off);
}
int newValue = v / 1000;
int ones = (v - (newValue * 1000)); // == value % 1000
@@ -115,22 +101,16 @@
newValue /= 1000;
int thousands = (v - (newValue * 1000));
- // value now has millions, which have 1, 2 or 3 digits
- if (hasBillions) {
- off = full3(newValue, b, off);
- } else {
- off = leading3(newValue, b, off);
- }
- off = full3(thousands, b, off);
- off = full3(ones, b, off);
- return off;
+ off = _leading3(newValue, b, off);
+ off = _full3(thousands, b, off);
+ return _full3(ones, b, off);
}
public static int outputInt(int v, byte[] b, int off)
{
if (v < 0) {
if (v == Integer.MIN_VALUE) {
- return outputLong((long) v, b, off);
+ return _outputSmallestI(b, off);
}
b[off++] = '-';
v = -v;
@@ -141,18 +121,17 @@
if (v < 10) {
b[off++] = (byte) ('0' + v);
} else {
- off = leading3(v, b, off);
+ off = _leading3(v, b, off);
}
} else {
int thousands = v / 1000;
v -= (thousands * 1000); // == value % 1000
- off = leading3(thousands, b, off);
- off = full3(v, b, off);
+ off = _leading3(thousands, b, off);
+ off = _full3(v, b, off);
}
return off;
}
- boolean hasB = (v >= BILLION);
- if (hasB) {
+ if (v >= BILLION) {
v -= BILLION;
if (v >= BILLION) {
v -= BILLION;
@@ -160,23 +139,18 @@
} else {
b[off++] = '1';
}
+ return _outputFullBillion(v, b, off);
}
int newValue = v / 1000;
int ones = (v - (newValue * 1000)); // == value % 1000
v = newValue;
newValue /= 1000;
int thousands = (v - (newValue * 1000));
-
- if (hasB) {
- off = full3(newValue, b, off);
- } else {
- off = leading3(newValue, b, off);
- }
- off = full3(thousands, b, off);
- off = full3(ones, b, off);
- return off;
+ off = _leading3(newValue, b, off);
+ off = _full3(thousands, b, off);
+ return _full3(ones, b, off);
}
-
+
/**
* @return Offset within buffer after outputting int
*/
@@ -184,17 +158,11 @@
{
// First: does it actually fit in an int?
if (v < 0L) {
- /* MIN_INT is actually printed as long, just because its
- * negation is not an int but long
- */
if (v > MIN_INT_AS_LONG) {
return outputInt((int) v, b, off);
}
if (v == Long.MIN_VALUE) {
- // Special case: no matching positive value within range
- int len = SMALLEST_LONG.length();
- SMALLEST_LONG.getChars(0, len, b, off);
- return (off + len);
+ return _outputSmallestL(b, off);
}
b[off++] = '-';
v = -v;
@@ -204,34 +172,21 @@
}
}
- /* Ok: real long print. Need to first figure out length
- * in characters, and then print in from end to beginning
- */
- int origOffset = off;
- off += calcLongStrLength(v);
- int ptr = off;
+ // Ok, let's separate last 9 digits (3 x full sets of 3)
+ long upper = v / BILLION_L;
+ v -= (upper * BILLION_L);
- // First, with long arithmetics:
- while (v > MAX_INT_AS_LONG) { // full triplet
- ptr -= 3;
- long newValue = v / THOUSAND_L;
- int triplet = (int) (v - newValue * THOUSAND_L);
- full3(triplet, b, ptr);
- v = newValue;
+ // two integers?
+ if (upper < BILLION_L) {
+ off = _outputUptoBillion((int) upper, b, off);
+ } else {
+ // no, two ints and bits; hi may be about 16 or so
+ long hi = upper / BILLION_L;
+ upper -= (hi * BILLION_L);
+ off = _leading3((int) hi, b, off);
+ off = _outputFullBillion((int) upper, b, off);
}
- // Then with int arithmetics:
- int ivalue = (int) v;
- while (ivalue >= 1000) { // still full triplet
- ptr -= 3;
- int newValue = ivalue / 1000;
- int triplet = ivalue - (newValue * 1000);
- full3(triplet, b, ptr);
- ivalue = newValue;
- }
- // And finally, if anything remains, partial triplet
- leading3(ivalue, b, origOffset);
-
- return off;
+ return _outputFullBillion((int) v, b, off);
}
public static int outputLong(long v, byte[] b, int off)
@@ -241,12 +196,7 @@
return outputInt((int) v, b, off);
}
if (v == Long.MIN_VALUE) {
- // Special case: no matching positive value within range
- int len = SMALLEST_LONG.length();
- for (int i = 0; i < len; ++i) {
- b[off++] = (byte) SMALLEST_LONG.charAt(i);
- }
- return off;
+ return _outputSmallestL(b, off);
}
b[off++] = '-';
v = -v;
@@ -255,41 +205,33 @@
return outputInt((int) v, b, off);
}
}
- int origOff = off;
- off += calcLongStrLength(v);
- int ptr = off;
- // First, with long arithmetics:
- while (v > MAX_INT_AS_LONG) { // full triplet
- ptr -= 3;
- long newV = v / THOUSAND_L;
- int t = (int) (v - newV * THOUSAND_L);
- full3(t, b, ptr);
- v = newV;
+ // Ok, let's separate last 9 digits (3 x full sets of 3)
+ long upper = v / BILLION_L;
+ v -= (upper * BILLION_L);
+
+ // two integers?
+ if (upper < BILLION_L) {
+ off = _outputUptoBillion((int) upper, b, off);
+ } else {
+ // no, two ints and bits; hi may be about 16 or so
+ long hi = upper / BILLION_L;
+ upper -= (hi * BILLION_L);
+ off = _leading3((int) hi, b, off);
+ off = _outputFullBillion((int) upper, b, off);
}
- // Then with int arithmetics:
- int ivalue = (int) v;
- while (ivalue >= 1000) { // still full triplet
- ptr -= 3;
- int newV = ivalue / 1000;
- int t = ivalue - (newV * 1000);
- full3(t, b, ptr);
- ivalue = newV;
- }
- leading3(ivalue, b, origOff);
- return off;
+ return _outputFullBillion((int) v, b, off);
}
-
+
/*
/**********************************************************
- /* Secondary convenience serialization methods
+ /* Convenience serialization methods
/**********************************************************
*/
/* !!! 05-Aug-2008, tatus: Any ways to further optimize
* these? (or need: only called by diagnostics methods?)
*/
-
public static String toString(int v)
{
// Lookup table for small values
@@ -325,78 +267,231 @@
/*
/**********************************************************
- /* Internal methods
+ /* Internal helper methods
/**********************************************************
*/
- private static int leading3(int t, char[] b, int off)
+ private static int _outputUptoBillion(int v, char[] b, int off)
{
- int digitOffset = (t << 2);
- char c = LEAD_3[digitOffset++];
- if (c != NC) {
- b[off++] = c;
- }
- c = LEAD_3[digitOffset++];
- if (c != NC) {
- b[off++] = c;
- }
- // Last is required to be non-empty
- b[off++] = LEAD_3[digitOffset];
- return off;
- }
-
- private static int leading3(int t, byte[] b, int off)
- {
- int digitOffset = (t << 2);
- char c = LEAD_3[digitOffset++];
- if (c != NC) {
- b[off++] = (byte) c;
- }
- c = LEAD_3[digitOffset++];
- if (c != NC) {
- b[off++] = (byte) c;
- }
- // Last is required to be non-empty
- b[off++] = (byte) LEAD_3[digitOffset];
- return off;
- }
-
- private static int full3(int t, char[] b, int off)
- {
- int digitOffset = (t << 2);
- b[off++] = FULL_3[digitOffset++];
- b[off++] = FULL_3[digitOffset++];
- b[off++] = FULL_3[digitOffset];
- return off;
- }
-
- private static int full3(int t, byte[] b, int off)
- {
- int digitOffset = (t << 2);
- b[off++] = FULL_TRIPLETS_B[digitOffset++];
- b[off++] = FULL_TRIPLETS_B[digitOffset++];
- b[off++] = FULL_TRIPLETS_B[digitOffset];
- return off;
- }
-
- /**
- *<p>
- * Pre-conditions: <code>c</code> is positive, and larger than
- * Integer.MAX_VALUE (about 2 billions).
- */
- private static int calcLongStrLength(long v)
- {
- int len = 10;
- long cmp = TEN_BILLION_L;
-
- // 19 is longest, need to worry about overflow
- while (v >= cmp) {
- if (len == 19) {
- break;
+ if (v < MILLION) { // at most 2 triplets...
+ if (v < 1000) {
+ return _leading3(v, b, off);
}
- ++len;
- cmp = (cmp << 3) + (cmp << 1); // 10x
+ int thousands = v / 1000;
+ int ones = v - (thousands * 1000); // == value % 1000
+ return _outputUptoMillion(b, off, thousands, ones);
}
- return len;
+ int thousands = v / 1000;
+ int ones = (v - (thousands * 1000)); // == value % 1000
+ int millions = thousands / 1000;
+ thousands -= (millions * 1000);
+
+ off = _leading3(millions, b, off);
+
+ int enc = TRIPLET_TO_CHARS[thousands];
+ b[off++] = (char) (enc >> 16);
+ b[off++] = (char) ((enc >> 8) & 0x7F);
+ b[off++] = (char) (enc & 0x7F);
+
+ enc = TRIPLET_TO_CHARS[ones];
+ b[off++] = (char) (enc >> 16);
+ b[off++] = (char) ((enc >> 8) & 0x7F);
+ b[off++] = (char) (enc & 0x7F);
+
+ return off;
+ }
+
+ private static int _outputFullBillion(int v, char[] b, int off)
+ {
+ int thousands = v / 1000;
+ int ones = (v - (thousands * 1000)); // == value % 1000
+ int millions = thousands / 1000;
+
+ int enc = TRIPLET_TO_CHARS[millions];
+ b[off++] = (char) (enc >> 16);
+ b[off++] = (char) ((enc >> 8) & 0x7F);
+ b[off++] = (char) (enc & 0x7F);
+
+ thousands -= (millions * 1000);
+ enc = TRIPLET_TO_CHARS[thousands];
+ b[off++] = (char) (enc >> 16);
+ b[off++] = (char) ((enc >> 8) & 0x7F);
+ b[off++] = (char) (enc & 0x7F);
+
+ enc = TRIPLET_TO_CHARS[ones];
+ b[off++] = (char) (enc >> 16);
+ b[off++] = (char) ((enc >> 8) & 0x7F);
+ b[off++] = (char) (enc & 0x7F);
+
+ return off;
+ }
+
+ private static int _outputUptoBillion(int v, byte[] b, int off)
+ {
+ if (v < MILLION) { // at most 2 triplets...
+ if (v < 1000) {
+ return _leading3(v, b, off);
+ }
+ int thousands = v / 1000;
+ int ones = v - (thousands * 1000); // == value % 1000
+ return _outputUptoMillion(b, off, thousands, ones);
+ }
+ int thousands = v / 1000;
+ int ones = (v - (thousands * 1000)); // == value % 1000
+ int millions = thousands / 1000;
+ thousands -= (millions * 1000);
+
+ off = _leading3(millions, b, off);
+
+ int enc = TRIPLET_TO_CHARS[thousands];
+ b[off++] = (byte) (enc >> 16);
+ b[off++] = (byte) (enc >> 8);
+ b[off++] = (byte) enc;
+
+ enc = TRIPLET_TO_CHARS[ones];
+ b[off++] = (byte) (enc >> 16);
+ b[off++] = (byte) (enc >> 8);
+ b[off++] = (byte) enc;
+
+ return off;
+ }
+
+ private static int _outputFullBillion(int v, byte[] b, int off)
+ {
+ int thousands = v / 1000;
+ int ones = (v - (thousands * 1000)); // == value % 1000
+ int millions = thousands / 1000;
+ thousands -= (millions * 1000);
+
+ int enc = TRIPLET_TO_CHARS[millions];
+ b[off++] = (byte) (enc >> 16);
+ b[off++] = (byte) (enc >> 8);
+ b[off++] = (byte) enc;
+
+ enc = TRIPLET_TO_CHARS[thousands];
+ b[off++] = (byte) (enc >> 16);
+ b[off++] = (byte) (enc >> 8);
+ b[off++] = (byte) enc;
+
+ enc = TRIPLET_TO_CHARS[ones];
+ b[off++] = (byte) (enc >> 16);
+ b[off++] = (byte) (enc >> 8);
+ b[off++] = (byte) enc;
+
+ return off;
+ }
+
+ private static int _outputUptoMillion(char[] b, int off, int thousands, int ones)
+ {
+ int enc = TRIPLET_TO_CHARS[thousands];
+ if (thousands > 9) {
+ if (thousands > 99) {
+ b[off++] = (char) (enc >> 16);
+ }
+ b[off++] = (char) ((enc >> 8) & 0x7F);
+ }
+ b[off++] = (char) (enc & 0x7F);
+ // and then full
+ enc = TRIPLET_TO_CHARS[ones];
+ b[off++] = (char) (enc >> 16);
+ b[off++] = (char) ((enc >> 8) & 0x7F);
+ b[off++] = (char) (enc & 0x7F);
+ return off;
+ }
+
+ private static int _outputUptoMillion(byte[] b, int off, int thousands, int ones)
+ {
+ int enc = TRIPLET_TO_CHARS[thousands];
+ if (thousands > 9) {
+ if (thousands > 99) {
+ b[off++] = (byte) (enc >> 16);
+ }
+ b[off++] = (byte) (enc >> 8);
+ }
+ b[off++] = (byte) enc;
+ // and then full
+ enc = TRIPLET_TO_CHARS[ones];
+ b[off++] = (byte) (enc >> 16);
+ b[off++] = (byte) (enc >> 8);
+ b[off++] = (byte) enc;
+ return off;
+ }
+
+ private static int _leading3(int t, char[] b, int off)
+ {
+ int enc = TRIPLET_TO_CHARS[t];
+ if (t > 9) {
+ if (t > 99) {
+ b[off++] = (char) (enc >> 16);
+ }
+ b[off++] = (char) ((enc >> 8) & 0x7F);
+ }
+ b[off++] = (char) (enc & 0x7F);
+ return off;
+ }
+
+ private static int _leading3(int t, byte[] b, int off)
+ {
+ int enc = TRIPLET_TO_CHARS[t];
+ if (t > 9) {
+ if (t > 99) {
+ b[off++] = (byte) (enc >> 16);
+ }
+ b[off++] = (byte) (enc >> 8);
+ }
+ b[off++] = (byte) enc;
+ return off;
+ }
+
+ private static int _full3(int t, char[] b, int off)
+ {
+ int enc = TRIPLET_TO_CHARS[t];
+ b[off++] = (char) (enc >> 16);
+ b[off++] = (char) ((enc >> 8) & 0x7F);
+ b[off++] = (char) (enc & 0x7F);
+ return off;
+ }
+
+ private static int _full3(int t, byte[] b, int off)
+ {
+ int enc = TRIPLET_TO_CHARS[t];
+ b[off++] = (byte) (enc >> 16);
+ b[off++] = (byte) (enc >> 8);
+ b[off++] = (byte) enc;
+ return off;
+ }
+
+ // // // Special cases for where we can not flip the sign bit
+
+ private static int _outputSmallestL(char[] b, int off)
+ {
+ int len = SMALLEST_LONG.length();
+ SMALLEST_LONG.getChars(0, len, b, off);
+ return (off + len);
+ }
+
+ private static int _outputSmallestL(byte[] b, int off)
+ {
+ int len = SMALLEST_LONG.length();
+ for (int i = 0; i < len; ++i) {
+ b[off++] = (byte) SMALLEST_LONG.charAt(i);
+ }
+ return off;
+ }
+
+ private static int _outputSmallestI(char[] b, int off)
+ {
+ int len = SMALLEST_INT.length();
+ SMALLEST_INT.getChars(0, len, b, off);
+ return (off + len);
+ }
+
+ private static int _outputSmallestI(byte[] b, int off)
+ {
+ int len = SMALLEST_INT.length();
+ for (int i = 0; i < len; ++i) {
+ b[off++] = (byte) SMALLEST_INT.charAt(i);
+ }
+ return off;
}
}
diff --git a/src/main/java/com/fasterxml/jackson/core/io/SegmentedStringWriter.java b/src/main/java/com/fasterxml/jackson/core/io/SegmentedStringWriter.java
index 00b52c6..c598a1e 100644
--- a/src/main/java/com/fasterxml/jackson/core/io/SegmentedStringWriter.java
+++ b/src/main/java/com/fasterxml/jackson/core/io/SegmentedStringWriter.java
@@ -15,7 +15,7 @@
*/
public final class SegmentedStringWriter extends Writer
{
- final protected TextBuffer _buffer;
+ final private TextBuffer _buffer;
public SegmentedStringWriter(BufferRecycler br) {
super();
diff --git a/src/main/java/com/fasterxml/jackson/core/json/ByteSourceJsonBootstrapper.java b/src/main/java/com/fasterxml/jackson/core/json/ByteSourceJsonBootstrapper.java
index 6bbe412..6ff84e9 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/ByteSourceJsonBootstrapper.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/ByteSourceJsonBootstrapper.java
@@ -28,9 +28,9 @@
/**********************************************************
*/
- protected final IOContext _context;
+ private final IOContext _context;
- protected final InputStream _in;
+ private final InputStream _in;
/*
/**********************************************************
@@ -38,7 +38,7 @@
/**********************************************************
*/
- protected final byte[] _inputBuffer;
+ private final byte[] _inputBuffer;
private int _inputPtr;
@@ -63,7 +63,7 @@
*<p>
* Note: includes possible BOMs, if those were part of the input.
*/
- protected int _inputProcessed;
+// private int _inputProcessed;
/*
/**********************************************************
@@ -71,9 +71,12 @@
/**********************************************************
*/
- protected boolean _bigEndian = true;
+ /**
+ * Whether input has been detected to be in Big-Endian encoding or not.
+ */
+ private boolean _bigEndian = true;
- protected int _bytesPerChar; // 0 means "dunno yet"
+ private int _bytesPerChar; // 0 means "dunno yet"
/*
/**********************************************************
@@ -86,7 +89,7 @@
_in = in;
_inputBuffer = ctxt.allocReadIOBuffer();
_inputEnd = _inputPtr = 0;
- _inputProcessed = 0;
+// _inputProcessed = 0;
_bufferRecyclable = true;
}
@@ -97,7 +100,7 @@
_inputPtr = inputStart;
_inputEnd = (inputStart + inputLen);
// Need to offset this for correct location info
- _inputProcessed = -inputStart;
+// _inputProcessed = -inputStart;
_bufferRecyclable = false;
}
@@ -172,6 +175,34 @@
return enc;
}
+ /**
+ * Helper method that may be called to see if given {@link DataInput}
+ * has BOM marker, and if so, to skip it.
+ * @throws IOException
+ *
+ * @since 2.8
+ */
+ public static int skipUTF8BOM(DataInput input) throws IOException
+ {
+ int b = input.readUnsignedByte();
+ if (b != 0xEF) {
+ return b;
+ }
+ // since this is not legal byte in JSON otherwise, except
+ // that we do get BOM; if not, report error
+ b = input.readUnsignedByte();
+ if (b != 0xBB) {
+ throw new IOException("Unexpected byte 0x"+Integer.toHexString(b)
+ +" following 0xEF; should get 0xBB as part of UTF-8 BOM");
+ }
+ b = input.readUnsignedByte();
+ if (b != 0xBF) {
+ throw new IOException("Unexpected byte 0x"+Integer.toHexString(b)
+ +" following 0xEF 0xBB; should get 0xBF as part of UTF-8 BOM");
+ }
+ return input.readUnsignedByte();
+ }
+
/*
/**********************************************************
/* Constructing a Reader
@@ -215,7 +246,7 @@
JsonEncoding enc = detectEncoding();
if (enc == JsonEncoding.UTF8) {
- /* and without canonicalization, byte-based approach is not performance; just use std UTF-8 reader
+ /* and without canonicalization, byte-based approach is not performant; just use std UTF-8 reader
* (which is ok for larger input; not so hot for smaller; but this is not a common case)
*/
if (JsonFactory.Feature.CANONICALIZE_FIELD_NAMES.enabledIn(factoryFeatures)) {
@@ -393,8 +424,11 @@
return true;
case 0x0000FFFE: // UCS-4, in-order...
reportWeirdUCS4("2143"); // throws exception
+ break; // never gets here
case 0xFEFF0000: // UCS-4, in-order...
reportWeirdUCS4("3412"); // throws exception
+ break; // never gets here
+ default:
}
// Ok, if not, how about 16-bit encoding BOMs?
int msw = quad >>> 16;
diff --git a/src/main/java/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java b/src/main/java/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java
index 1ede298..ac9e87f 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java
@@ -111,6 +111,17 @@
/*
/**********************************************************
+ /* Versioned
+ /**********************************************************
+ */
+
+ @Override
+ public Version version() {
+ return VersionUtil.versionFor(getClass());
+ }
+
+ /*
+ /**********************************************************
/* Overridden configuration methods
/**********************************************************
*/
@@ -176,17 +187,6 @@
_rootValueSeparator = sep;
return this;
}
-
- /*
- /**********************************************************
- /* Versioned
- /**********************************************************
- */
-
- @Override
- public Version version() {
- return VersionUtil.versionFor(getClass());
- }
/*
/**********************************************************
@@ -202,4 +202,46 @@
writeFieldName(fieldName);
writeString(value);
}
+
+ /*
+ /**********************************************************
+ /* Shared helper methods
+ /**********************************************************
+ */
+
+ protected void _verifyPrettyValueWrite(String typeMsg, int status) throws IOException
+ {
+ // If we have a pretty printer, it knows what to do:
+ switch (status) {
+ case JsonWriteContext.STATUS_OK_AFTER_COMMA: // array
+ _cfgPrettyPrinter.writeArrayValueSeparator(this);
+ break;
+ case JsonWriteContext.STATUS_OK_AFTER_COLON:
+ _cfgPrettyPrinter.writeObjectFieldValueSeparator(this);
+ break;
+ case JsonWriteContext.STATUS_OK_AFTER_SPACE:
+ _cfgPrettyPrinter.writeRootValueSeparator(this);
+ break;
+ case JsonWriteContext.STATUS_OK_AS_IS:
+ // First entry, but of which context?
+ if (_writeContext.inArray()) {
+ _cfgPrettyPrinter.beforeArrayValues(this);
+ } else if (_writeContext.inObject()) {
+ _cfgPrettyPrinter.beforeObjectEntries(this);
+ }
+ break;
+ case JsonWriteContext.STATUS_EXPECT_NAME:
+ _reportCantWriteValueExpectName(typeMsg);
+ break;
+ default:
+ _throwInternal();
+ break;
+ }
+ }
+
+ protected void _reportCantWriteValueExpectName(String typeMsg) throws IOException
+ {
+ _reportError(String.format("Can not %s, expecting field name (context: %s)",
+ typeMsg, _writeContext.typeDesc()));
+ }
}
diff --git a/src/main/java/com/fasterxml/jackson/core/json/JsonReadContext.java b/src/main/java/com/fasterxml/jackson/core/json/JsonReadContext.java
index cf09633..e1b0479 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/JsonReadContext.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/JsonReadContext.java
@@ -229,6 +229,7 @@
sb.append(']');
break;
case TYPE_OBJECT:
+ default:
sb.append('{');
if (_currentName != null) {
sb.append('"');
diff --git a/src/main/java/com/fasterxml/jackson/core/json/JsonWriteContext.java b/src/main/java/com/fasterxml/jackson/core/json/JsonWriteContext.java
index dadec64..c350e22 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/JsonWriteContext.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/JsonWriteContext.java
@@ -44,7 +44,7 @@
*/
/**
- * Name of the field of which value is to be parsed; only
+ * Name of the field of which value is to be written; only
* used for OBJECT contexts
*/
protected String _currentName;
@@ -55,8 +55,8 @@
protected Object _currentValue;
/**
- * Marker used to indicate that we just received a name, and
- * now expect a value
+ * Marker used to indicate that we just wrote a name, and
+ * now expect a value to write
*/
protected boolean _gotName;
@@ -162,7 +162,7 @@
* @return Index of the field entry (0-based)
*/
public int writeFieldName(String name) throws JsonProcessingException {
- if (_gotName) {
+ if ((_type != TYPE_OBJECT) || _gotName) {
return STATUS_EXPECT_VALUE;
}
_gotName = true;
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 64c798d..5f0515a 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java
@@ -163,39 +163,16 @@
@Override public Object getInputSource() { return _reader; }
- @Override
- protected boolean loadMore() throws IOException
- {
- final int bufSize = _inputEnd;
-
- _currInputProcessed += bufSize;
- _currInputRowStart -= bufSize;
-
- // 26-Nov-2015, tatu: Since name-offset requires it too, must offset
- // this increase to avoid "moving" name-offset, resulting most likely
- // in negative value, which is fine as combine value remains unchanged.
- _nameStartOffset -= bufSize;
-
- if (_reader != null) {
- int count = _reader.read(_inputBuffer, 0, _inputBuffer.length);
- if (count > 0) {
- _inputPtr = 0;
- _inputEnd = count;
- return true;
- }
- // End of input
- _closeInput();
- // Should never return 0, so let's fail
- if (count == 0) {
- throw new IOException("Reader returned 0 characters when trying to read "+_inputEnd);
- }
- }
- return false;
- }
-
+ @Deprecated // since 2.8
protected char getNextChar(String eofMsg) throws IOException {
+ return getNextChar(eofMsg, null);
+ }
+
+ protected char getNextChar(String eofMsg, JsonToken forToken) throws IOException {
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) { _reportInvalidEOF(eofMsg); }
+ if (!_loadMore()) {
+ _reportInvalidEOF(eofMsg, forToken);
+ }
}
return _inputBuffer[_inputPtr++];
}
@@ -240,6 +217,45 @@
/*
/**********************************************************
+ /* Low-level access, supporting
+ /**********************************************************
+ */
+
+ protected void _loadMoreGuaranteed() throws IOException {
+ if (!_loadMore()) { _reportInvalidEOF(); }
+ }
+
+ protected boolean _loadMore() throws IOException
+ {
+ final int bufSize = _inputEnd;
+
+ _currInputProcessed += bufSize;
+ _currInputRowStart -= bufSize;
+
+ // 26-Nov-2015, tatu: Since name-offset requires it too, must offset
+ // this increase to avoid "moving" name-offset, resulting most likely
+ // in negative value, which is fine as combine value remains unchanged.
+ _nameStartOffset -= bufSize;
+
+ if (_reader != null) {
+ int count = _reader.read(_inputBuffer, 0, _inputBuffer.length);
+ if (count > 0) {
+ _inputPtr = 0;
+ _inputEnd = count;
+ return true;
+ }
+ // End of input
+ _closeInput();
+ // Should never return 0, so let's fail
+ if (count == 0) {
+ throw new IOException("Reader returned 0 characters when trying to read "+_inputEnd);
+ }
+ }
+ return false;
+ }
+
+ /*
+ /**********************************************************
/* Public API, data access
/**********************************************************
*/
@@ -264,6 +280,33 @@
return _getText2(t);
}
+ @Override // since 2.8
+ public int getText(Writer writer) throws IOException
+ {
+ JsonToken t = _currToken;
+ if (t == JsonToken.VALUE_STRING) {
+ if (_tokenIncomplete) {
+ _tokenIncomplete = false;
+ _finishString(); // only strings can be incomplete
+ }
+ return _textBuffer.contentsToWriter(writer);
+ }
+ if (t == JsonToken.FIELD_NAME) {
+ String n = _parsingContext.getCurrentName();
+ writer.write(n);
+ return n.length();
+ }
+ if (t != null) {
+ if (t.isNumeric()) {
+ return _textBuffer.contentsToWriter(writer);
+ }
+ char[] ch = t.asCharArray();
+ writer.write(ch);
+ return ch.length;
+ }
+ return 0;
+ }
+
// // // Let's override default impls for improved performance
// @since 2.1
@@ -457,7 +500,7 @@
char ch;
do {
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
} while (ch <= INT_SPACE);
@@ -484,7 +527,7 @@
// then second base64 char; can't get padding yet, nor ws
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
bits = b64variant.decodeBase64Char(ch);
@@ -495,7 +538,7 @@
// third base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
bits = b64variant.decodeBase64Char(ch);
@@ -514,7 +557,7 @@
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
// Ok, must get padding
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
if (!b64variant.usesPaddingChar(ch)) {
@@ -530,7 +573,7 @@
decodedData = (decodedData << 6) | bits;
// fourth and last base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
bits = b64variant.decodeBase64Char(ch);
@@ -600,9 +643,8 @@
}
int i = _skipWSOrEnd();
if (i < 0) { // end-of-input
- /* 19-Feb-2009, tatu: Should actually close/release things
- * like input source, symbol table and recyclable buffers now.
- */
+ // Should actually close/release things
+ // like input source, symbol table and recyclable buffers now.
close();
return (_currToken = null);
}
@@ -667,9 +709,8 @@
}
t = JsonToken.START_OBJECT;
break;
- case ']':
case '}':
- // Error: neither is valid at this point; valid closers have
+ // Error: } is not valid at this point; valid closers have
// been handled earlier
_reportUnexpectedChar(i, "expected a value");
case 't':
@@ -734,6 +775,14 @@
return (_currToken = t);
}
+ @Override
+ public void finishToken() throws IOException {
+ if (_tokenIncomplete) {
+ _tokenIncomplete = false;
+ _finishString(); // only strings can be incomplete
+ }
+ }
+
/*
/**********************************************************
/* Public API, nextXxx() overrides
@@ -1070,6 +1119,20 @@
case '8':
case '9':
return (_currToken = _parsePosNumber(i));
+ /*
+ * This check proceeds only if the Feature.ALLOW_MISSING_VALUES is enabled
+ * The Check is for missing values. Incase of missing values in an array, the next token will be either ',' or ']'.
+ * This case, decrements the already incremented _inputPtr in the buffer in case of comma(,)
+ * so that the existing flow goes back to checking the next token which will be comma again and
+ * it continues the parsing.
+ * Also the case returns NULL as current token in case of ',' or ']'.
+ */
+ case ',':
+ case ']':
+ if(isEnabled(Feature.ALLOW_MISSING_VALUES)) {
+ _inputPtr--;
+ return (_currToken = JsonToken.VALUE_NULL);
+ }
}
return (_currToken = _handleOddValue(i));
}
@@ -1384,7 +1447,8 @@
// This is the place to do leading-zero check(s) too:
int intLen = 0;
- char c = (_inputPtr < _inputEnd) ? _inputBuffer[_inputPtr++] : getNextChar("No digit following minus sign");
+ char c = (_inputPtr < _inputEnd) ? _inputBuffer[_inputPtr++]
+ : getNextChar("No digit following minus sign", JsonToken.VALUE_NUMBER_INT);
if (c == '0') {
c = _verifyNoLeadingZeroes();
}
@@ -1399,7 +1463,7 @@
outPtr = 0;
}
outBuf[outPtr++] = c;
- if (_inputPtr >= _inputEnd && !loadMore()) {
+ if (_inputPtr >= _inputEnd && !_loadMore()) {
// EOF is legal for main level int values
c = CHAR_NULL;
eof = true;
@@ -1419,7 +1483,7 @@
fract_loop:
while (true) {
- if (_inputPtr >= _inputEnd && !loadMore()) {
+ if (_inputPtr >= _inputEnd && !_loadMore()) {
eof = true;
break fract_loop;
}
@@ -1470,7 +1534,7 @@
outPtr = 0;
}
outBuf[outPtr++] = c;
- if (_inputPtr >= _inputEnd && !loadMore()) {
+ if (_inputPtr >= _inputEnd && !_loadMore()) {
eof = true;
break exp_loop;
}
@@ -1514,7 +1578,7 @@
private char _verifyNLZ2() throws IOException
{
- if (_inputPtr >= _inputEnd && !loadMore()) {
+ if (_inputPtr >= _inputEnd && !_loadMore()) {
return '0';
}
char ch = _inputBuffer[_inputPtr];
@@ -1527,7 +1591,7 @@
// if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number)
++_inputPtr; // Leading zero to be skipped
if (ch == INT_0) {
- while (_inputPtr < _inputEnd || loadMore()) {
+ while (_inputPtr < _inputEnd || _loadMore()) {
ch = _inputBuffer[_inputPtr];
if (ch < '0' || ch > '9') { // followed by non-number; retain one zero
return '0';
@@ -1549,7 +1613,9 @@
{
if (ch == 'I') {
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) { _reportInvalidEOFInValue(); }
+ if (!_loadMore()) {
+ _reportInvalidEOFInValue(JsonToken.VALUE_NUMBER_INT);
+ }
}
ch = _inputBuffer[_inputPtr++];
if (ch == 'N') {
@@ -1642,8 +1708,8 @@
while (true) {
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
- _reportInvalidEOF(": was expecting closing '"+((char) endChar)+"' for name");
+ if (!_loadMore()) {
+ _reportInvalidEOF(" in field name", JsonToken.FIELD_NAME);
}
}
char c = _inputBuffer[_inputPtr++];
@@ -1782,10 +1848,8 @@
// Most likely an error, unless we are to allow single-quote-strings
switch (i) {
case '\'':
- /* [JACKSON-173]: allow single quotes. Unlike with regular
- * Strings, we'll eagerly parse contents; this so that there's
- * no need to store information on quote char used.
- *
+ /* Allow single quotes? Unlike with regular Strings, we'll eagerly parse
+ * contents; this so that there'sno need to store information on quote char used.
* Also, no separation to fast/slow parsing; we'll just do
* one regular (~= slowish) parsing, to keep code simple
*/
@@ -1793,6 +1857,21 @@
return _handleApos();
}
break;
+ case ']':
+ /* 28-Mar-2016: [core#116]: If Feature.ALLOW_MISSING_VALUES is enabled
+ * we may allow "missing values", that is, encountering a trailing
+ * comma or closing marker where value would be expected
+ */
+ if (!_parsingContext.inArray()) {
+ break;
+ }
+ // fall through
+ case ',':
+ if (isEnabled(Feature.ALLOW_MISSING_VALUES)) {
+ --_inputPtr;
+ return JsonToken.VALUE_NULL;
+ }
+ break;
case 'N':
_matchToken("NaN", 1);
if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
@@ -1809,8 +1888,8 @@
break;
case '+': // note: '-' is taken as number
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
- _reportInvalidEOFInValue();
+ if (!_loadMore()) {
+ _reportInvalidEOFInValue(JsonToken.VALUE_NUMBER_INT);
}
}
return _handleInvalidNumberStart(_inputBuffer[_inputPtr++], false);
@@ -1831,8 +1910,9 @@
while (true) {
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
- _reportInvalidEOF(": was expecting closing quote for a string value");
+ if (!_loadMore()) {
+ _reportInvalidEOF(": was expecting closing quote for a string value",
+ JsonToken.VALUE_STRING);
}
}
char c = _inputBuffer[_inputPtr++];
@@ -1874,7 +1954,7 @@
while (true) {
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) { // acceptable for now (will error out later)
+ if (!_loadMore()) { // acceptable for now (will error out later)
break;
}
}
@@ -1955,8 +2035,9 @@
while (true) {
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
- _reportInvalidEOF(": was expecting closing quote for a string value");
+ if (!_loadMore()) {
+ _reportInvalidEOF(": was expecting closing quote for a string value",
+ JsonToken.VALUE_STRING);
}
}
char c = _inputBuffer[_inputPtr++];
@@ -2001,8 +2082,9 @@
while (true) {
if (inPtr >= inLen) {
_inputPtr = inPtr;
- if (!loadMore()) {
- _reportInvalidEOF(": was expecting closing quote for a string value");
+ if (!_loadMore()) {
+ _reportInvalidEOF(": was expecting closing quote for a string value",
+ JsonToken.VALUE_STRING);
}
inPtr = _inputPtr;
inLen = _inputEnd;
@@ -2042,7 +2124,7 @@
* (to see if we have \n following \r).
*/
protected final void _skipCR() throws IOException {
- if (_inputPtr < _inputEnd || loadMore()) {
+ if (_inputPtr < _inputEnd || _loadMore()) {
if (_inputBuffer[_inputPtr] == '\n') {
++_inputPtr;
}
@@ -2107,10 +2189,7 @@
private final int _skipColon2(boolean gotColon) throws IOException
{
- while (true) {
- if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
- }
+ while (_inputPtr < _inputEnd || _loadMore()) {
int i = (int) _inputBuffer[_inputPtr++];
if (i > INT_SPACE) {
if (i == INT_SLASH) {
@@ -2142,6 +2221,9 @@
}
}
}
+ _reportInvalidEOF(" within/between "+_parsingContext.typeDesc()+" entries",
+ null);
+ return -1;
}
// Variant called when we know there's at least 4 more bytes available
@@ -2196,7 +2278,7 @@
private final int _skipComma(int i) throws IOException
{
if (i != INT_COMMA) {
- _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.getTypeDesc()+" entries");
+ _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
while (_inputPtr < _inputEnd) {
i = (int) _inputBuffer[_inputPtr++];
@@ -2223,7 +2305,7 @@
private final int _skipAfterComma2() throws IOException
{
- while (_inputPtr < _inputEnd || loadMore()) {
+ while (_inputPtr < _inputEnd || _loadMore()) {
int i = (int) _inputBuffer[_inputPtr++];
if (i > INT_SPACE) {
if (i == INT_SLASH) {
@@ -2248,7 +2330,7 @@
}
}
}
- throw _constructError("Unexpected end-of-input within/between "+_parsingContext.getTypeDesc()+" entries");
+ throw _constructError("Unexpected end-of-input within/between "+_parsingContext.typeDesc()+" entries");
}
private final int _skipWSOrEnd() throws IOException
@@ -2256,7 +2338,7 @@
// Let's handle first character separately since it is likely that
// it is either non-whitespace; or we have longer run of white space
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
+ if (!_loadMore()) {
return _eofAsNextChar();
}
}
@@ -2306,7 +2388,7 @@
{
while (true) {
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) { // We ran out of input...
+ if (!_loadMore()) { // We ran out of input...
return _eofAsNextChar();
}
}
@@ -2341,8 +2423,8 @@
_reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)");
}
// First: check which comment (if either) it is:
- if (_inputPtr >= _inputEnd && !loadMore()) {
- _reportInvalidEOF(" in a comment");
+ if (_inputPtr >= _inputEnd && !_loadMore()) {
+ _reportInvalidEOF(" in a comment", null);
}
char c = _inputBuffer[_inputPtr++];
if (c == '/') {
@@ -2357,11 +2439,11 @@
private void _skipCComment() throws IOException
{
// Ok: need the matching '*/'
- while ((_inputPtr < _inputEnd) || loadMore()) {
+ while ((_inputPtr < _inputEnd) || _loadMore()) {
int i = (int) _inputBuffer[_inputPtr++];
if (i <= '*') {
if (i == '*') { // end?
- if ((_inputPtr >= _inputEnd) && !loadMore()) {
+ if ((_inputPtr >= _inputEnd) && !_loadMore()) {
break;
}
if (_inputBuffer[_inputPtr] == INT_SLASH) {
@@ -2382,7 +2464,7 @@
}
}
}
- _reportInvalidEOF(" in a comment");
+ _reportInvalidEOF(" in a comment", null);
}
private boolean _skipYAMLComment() throws IOException
@@ -2397,7 +2479,7 @@
private void _skipLine() throws IOException
{
// Ok: need to find EOF or linefeed
- while ((_inputPtr < _inputEnd) || loadMore()) {
+ while ((_inputPtr < _inputEnd) || _loadMore()) {
int i = (int) _inputBuffer[_inputPtr++];
if (i < INT_SPACE) {
if (i == INT_LF) {
@@ -2418,8 +2500,8 @@
protected char _decodeEscaped() throws IOException
{
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
- _reportInvalidEOF(" in character escape sequence");
+ if (!_loadMore()) {
+ _reportInvalidEOF(" in character escape sequence", JsonToken.VALUE_STRING);
}
}
char c = _inputBuffer[_inputPtr++];
@@ -2454,8 +2536,8 @@
int value = 0;
for (int i = 0; i < 4; ++i) {
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
- _reportInvalidEOF(" in character escape sequence");
+ if (!_loadMore()) {
+ _reportInvalidEOF(" in character escape sequence", JsonToken.VALUE_STRING);
}
}
int ch = (int) _inputBuffer[_inputPtr++];
@@ -2525,7 +2607,7 @@
do {
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
+ if (!_loadMore()) {
_reportInvalidToken(matchStr.substring(0, i));
}
}
@@ -2537,7 +2619,7 @@
// but let's also ensure we either get EOF, or non-alphanum char...
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
+ if (!_loadMore()) {
return;
}
}
@@ -2573,7 +2655,7 @@
char ch;
do {
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
} while (ch <= INT_SPACE);
@@ -2592,7 +2674,7 @@
// then second base64 char; can't get padding yet, nor ws
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
bits = b64variant.decodeBase64Char(ch);
@@ -2603,7 +2685,7 @@
// third base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
bits = b64variant.decodeBase64Char(ch);
@@ -2622,7 +2704,7 @@
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
// Ok, must get more padding chars, then
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
if (!b64variant.usesPaddingChar(ch)) {
@@ -2639,7 +2721,7 @@
decodedData = (decodedData << 6) | bits;
// fourth and last base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
bits = b64variant.decodeBase64Char(ch);
@@ -2735,7 +2817,7 @@
*/
while (true) {
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
+ if (!_loadMore()) {
break;
}
}
diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java
new file mode 100644
index 0000000..8bc3789
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java
@@ -0,0 +1,2797 @@
+package com.fasterxml.jackson.core.json;
+
+import java.io.*;
+import java.util.Arrays;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.base.ParserBase;
+import com.fasterxml.jackson.core.io.CharTypes;
+import com.fasterxml.jackson.core.io.IOContext;
+import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
+import com.fasterxml.jackson.core.util.*;
+
+import static com.fasterxml.jackson.core.JsonTokenId.*;
+
+/**
+ * This is a concrete implementation of {@link JsonParser}, which is
+ * based on a {@link java.io.DataInput} as the input source.
+ *<p>
+ * Due to limitations in look-ahead (basically there's none), as well
+ * as overhead of reading content mostly byte-by-byte,
+ * there are some
+ * minor differences from regular streaming parsing. Specifically:
+ *<ul>
+ * <li>Input location is not being tracked, as offsets would need to
+ * be updated for each read from all over the place; if caller wants
+ * this information, it has to track this with {@link DataInput}.
+ * </li>
+ * <li>As a consequence linefeed handling is removed so all white-space is
+ * equal; and checks are simplified NOT to check for control characters
+ * </li>
+ * </ul>
+ *
+ * @since 2.8
+ */
+public class UTF8DataInputJsonParser
+ extends ParserBase
+{
+ final static byte BYTE_LF = (byte) '\n';
+
+ // This is the main input-code lookup table, fetched eagerly
+ private final static int[] _icUTF8 = CharTypes.getInputCodeUtf8();
+
+ // 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();
+
+ /*
+ /**********************************************************
+ /* Configuration
+ /**********************************************************
+ */
+
+ /**
+ * Codec used for data binding when (if) requested; typically full
+ * <code>ObjectMapper</code>, but that abstract is not part of core
+ * package.
+ */
+ protected ObjectCodec _objectCodec;
+
+ /**
+ * Symbol table that contains field names encountered so far
+ */
+ final protected ByteQuadsCanonicalizer _symbols;
+
+ /*
+ /**********************************************************
+ /* Parsing state
+ /**********************************************************
+ */
+
+ /**
+ * Temporary buffer used for name parsing.
+ */
+ protected int[] _quadBuffer = new int[16];
+
+ /**
+ * Flag that indicates that the current token has not yet
+ * been fully processed, and needs to be finished for
+ * some access (or skipped to obtain the next token)
+ */
+ protected boolean _tokenIncomplete;
+
+ /**
+ * Temporary storage for partially parsed name bytes.
+ */
+ private int _quad1;
+
+ /*
+ /**********************************************************
+ /* Current input data
+ /**********************************************************
+ */
+
+ protected DataInput _inputData;
+
+ /**
+ * Sometimes we need buffering for just a single byte we read but
+ * have to "push back"
+ */
+ protected int _nextByte = -1;
+
+ /*
+ /**********************************************************
+ /* Life-cycle
+ /**********************************************************
+ */
+
+ public UTF8DataInputJsonParser(IOContext ctxt, int features, DataInput inputData,
+ ObjectCodec codec, ByteQuadsCanonicalizer sym,
+ int firstByte)
+ {
+ super(ctxt, features);
+ _objectCodec = codec;
+ _symbols = sym;
+ _inputData = inputData;
+ _nextByte = firstByte;
+ }
+
+ @Override
+ public ObjectCodec getCodec() {
+ return _objectCodec;
+ }
+
+ @Override
+ public void setCodec(ObjectCodec c) {
+ _objectCodec = c;
+ }
+
+ /*
+ /**********************************************************
+ /* Overrides for life-cycle
+ /**********************************************************
+ */
+
+ @Override
+ public int releaseBuffered(OutputStream out) throws IOException {
+ return 0;
+ }
+
+ @Override
+ public Object getInputSource() {
+ return _inputData;
+ }
+
+ /*
+ /**********************************************************
+ /* Overrides, low-level reading
+ /**********************************************************
+ */
+
+ @Override
+ protected void _closeInput() throws IOException { }
+
+ /**
+ * Method called to release internal buffers owned by the base
+ * reader. This may be called along with {@link #_closeInput} (for
+ * example, when explicitly closing this reader instance), or
+ * separately (if need be).
+ */
+ @Override
+ protected void _releaseBuffers() throws IOException
+ {
+ super._releaseBuffers();
+ // Merge found symbols, if any:
+ _symbols.release();
+ }
+
+ /*
+ /**********************************************************
+ /* Public API, data access
+ /**********************************************************
+ */
+
+ @Override
+ public String getText() throws IOException
+ {
+ if (_currToken == JsonToken.VALUE_STRING) {
+ if (_tokenIncomplete) {
+ _tokenIncomplete = false;
+ return _finishAndReturnString(); // only strings can be incomplete
+ }
+ return _textBuffer.contentsAsString();
+ }
+ return _getText2(_currToken);
+ }
+
+ @Override
+ public int getText(Writer writer) throws IOException
+ {
+ JsonToken t = _currToken;
+ if (t == JsonToken.VALUE_STRING) {
+ if (_tokenIncomplete) {
+ _tokenIncomplete = false;
+ _finishString(); // only strings can be incomplete
+ }
+ return _textBuffer.contentsToWriter(writer);
+ }
+ if (t == JsonToken.FIELD_NAME) {
+ String n = _parsingContext.getCurrentName();
+ writer.write(n);
+ return n.length();
+ }
+ if (t != null) {
+ if (t.isNumeric()) {
+ return _textBuffer.contentsToWriter(writer);
+ }
+ char[] ch = t.asCharArray();
+ writer.write(ch);
+ return ch.length;
+ }
+ return 0;
+ }
+
+ // // // Let's override default impls for improved performance
+ @Override
+ public String getValueAsString() throws IOException
+ {
+ if (_currToken == JsonToken.VALUE_STRING) {
+ if (_tokenIncomplete) {
+ _tokenIncomplete = false;
+ return _finishAndReturnString(); // only strings can be incomplete
+ }
+ return _textBuffer.contentsAsString();
+ }
+ if (_currToken == JsonToken.FIELD_NAME) {
+ return getCurrentName();
+ }
+ return super.getValueAsString(null);
+ }
+
+ @Override
+ public String getValueAsString(String defValue) throws IOException
+ {
+ if (_currToken == JsonToken.VALUE_STRING) {
+ if (_tokenIncomplete) {
+ _tokenIncomplete = false;
+ return _finishAndReturnString(); // only strings can be incomplete
+ }
+ return _textBuffer.contentsAsString();
+ }
+ if (_currToken == JsonToken.FIELD_NAME) {
+ return getCurrentName();
+ }
+ return super.getValueAsString(defValue);
+ }
+
+ @Override
+ public int getValueAsInt() throws IOException
+ {
+ JsonToken t = _currToken;
+ if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) {
+ // inlined 'getIntValue()'
+ if ((_numTypesValid & NR_INT) == 0) {
+ if (_numTypesValid == NR_UNKNOWN) {
+ return _parseIntValue();
+ }
+ if ((_numTypesValid & NR_INT) == 0) {
+ convertNumberToInt();
+ }
+ }
+ return _numberInt;
+ }
+ return super.getValueAsInt(0);
+ }
+
+ @Override
+ public int getValueAsInt(int defValue) throws IOException
+ {
+ JsonToken t = _currToken;
+ if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) {
+ // inlined 'getIntValue()'
+ if ((_numTypesValid & NR_INT) == 0) {
+ if (_numTypesValid == NR_UNKNOWN) {
+ return _parseIntValue();
+ }
+ if ((_numTypesValid & NR_INT) == 0) {
+ convertNumberToInt();
+ }
+ }
+ return _numberInt;
+ }
+ return super.getValueAsInt(defValue);
+ }
+
+ protected final String _getText2(JsonToken t)
+ {
+ if (t == null) {
+ return null;
+ }
+ switch (t.id()) {
+ case ID_FIELD_NAME:
+ return _parsingContext.getCurrentName();
+
+ case ID_STRING:
+ // fall through
+ case ID_NUMBER_INT:
+ case ID_NUMBER_FLOAT:
+ return _textBuffer.contentsAsString();
+ default:
+ return t.asString();
+ }
+ }
+
+ @Override
+ public char[] getTextCharacters() throws IOException
+ {
+ if (_currToken != null) { // null only before/after document
+ switch (_currToken.id()) {
+
+ case ID_FIELD_NAME:
+ if (!_nameCopied) {
+ String name = _parsingContext.getCurrentName();
+ int nameLen = name.length();
+ if (_nameCopyBuffer == null) {
+ _nameCopyBuffer = _ioContext.allocNameCopyBuffer(nameLen);
+ } else if (_nameCopyBuffer.length < nameLen) {
+ _nameCopyBuffer = new char[nameLen];
+ }
+ name.getChars(0, nameLen, _nameCopyBuffer, 0);
+ _nameCopied = true;
+ }
+ return _nameCopyBuffer;
+
+ case ID_STRING:
+ if (_tokenIncomplete) {
+ _tokenIncomplete = false;
+ _finishString(); // only strings can be incomplete
+ }
+ // fall through
+ case ID_NUMBER_INT:
+ case ID_NUMBER_FLOAT:
+ return _textBuffer.getTextBuffer();
+
+ default:
+ return _currToken.asCharArray();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public int getTextLength() throws IOException
+ {
+ if (_currToken == JsonToken.VALUE_STRING) {
+ if (_tokenIncomplete) {
+ _tokenIncomplete = false;
+ _finishString(); // only strings can be incomplete
+ }
+ return _textBuffer.size();
+ }
+ if (_currToken == JsonToken.FIELD_NAME) {
+ return _parsingContext.getCurrentName().length();
+ }
+ if (_currToken != null) { // null only before/after document
+ if (_currToken.isNumeric()) {
+ return _textBuffer.size();
+ }
+ return _currToken.asCharArray().length;
+ }
+ return 0;
+ }
+
+ @Override
+ public int getTextOffset() throws IOException
+ {
+ // Most have offset of 0, only some may have other values:
+ if (_currToken != null) {
+ switch (_currToken.id()) {
+ case ID_FIELD_NAME:
+ return 0;
+ case ID_STRING:
+ if (_tokenIncomplete) {
+ _tokenIncomplete = false;
+ _finishString(); // only strings can be incomplete
+ }
+ // fall through
+ case ID_NUMBER_INT:
+ case ID_NUMBER_FLOAT:
+ return _textBuffer.getTextOffset();
+ default:
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public byte[] getBinaryValue(Base64Variant b64variant) throws IOException
+ {
+ if (_currToken != JsonToken.VALUE_STRING &&
+ (_currToken != JsonToken.VALUE_EMBEDDED_OBJECT || _binaryValue == null)) {
+ _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...
+ */
+ if (_tokenIncomplete) {
+ try {
+ _binaryValue = _decodeBase64(b64variant);
+ } catch (IllegalArgumentException iae) {
+ throw _constructError("Failed to decode VALUE_STRING as base64 ("+b64variant+"): "+iae.getMessage());
+ }
+ /* let's clear incomplete only now; allows for accessing other
+ * textual content in error cases
+ */
+ _tokenIncomplete = false;
+ } else { // may actually require conversion...
+ if (_binaryValue == null) {
+ @SuppressWarnings("resource")
+ ByteArrayBuilder builder = _getByteArrayBuilder();
+ _decodeBase64(getText(), builder, b64variant);
+ _binaryValue = builder.toByteArray();
+ }
+ }
+ return _binaryValue;
+ }
+
+ @Override
+ public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException
+ {
+ // if we have already read the token, just use whatever we may have
+ if (!_tokenIncomplete || _currToken != JsonToken.VALUE_STRING) {
+ byte[] b = getBinaryValue(b64variant);
+ out.write(b);
+ return b.length;
+ }
+ // otherwise do "real" incremental parsing...
+ byte[] buf = _ioContext.allocBase64Buffer();
+ try {
+ return _readBinary(b64variant, out, buf);
+ } finally {
+ _ioContext.releaseBase64Buffer(buf);
+ }
+ }
+
+ protected int _readBinary(Base64Variant b64variant, OutputStream out,
+ byte[] buffer) throws IOException
+ {
+ int outputPtr = 0;
+ final int outputEnd = buffer.length - 3;
+ int outputCount = 0;
+
+ while (true) {
+ // first, we'll skip preceding white space, if any
+ int ch;
+ do {
+ ch = _inputData.readUnsignedByte();
+ } while (ch <= INT_SPACE);
+ int bits = b64variant.decodeBase64Char(ch);
+ if (bits < 0) { // reached the end, fair and square?
+ if (ch == INT_QUOTE) {
+ break;
+ }
+ bits = _decodeBase64Escape(b64variant, ch, 0);
+ if (bits < 0) { // white space to skip
+ continue;
+ }
+ }
+
+ // enough room? If not, flush
+ if (outputPtr > outputEnd) {
+ outputCount += outputPtr;
+ out.write(buffer, 0, outputPtr);
+ outputPtr = 0;
+ }
+
+ int decodedData = bits;
+
+ // then second base64 char; can't get padding yet, nor ws
+ ch = _inputData.readUnsignedByte();
+ bits = b64variant.decodeBase64Char(ch);
+ if (bits < 0) {
+ bits = _decodeBase64Escape(b64variant, ch, 1);
+ }
+ decodedData = (decodedData << 6) | bits;
+
+ // third base64 char; can be padding, but not ws
+ ch = _inputData.readUnsignedByte();
+ bits = b64variant.decodeBase64Char(ch);
+
+ // First branch: can get padding (-> 1 byte)
+ if (bits < 0) {
+ if (bits != Base64Variant.BASE64_VALUE_PADDING) {
+ // could also just be 'missing' padding
+ if (ch == '"' && !b64variant.usesPadding()) {
+ decodedData >>= 4;
+ buffer[outputPtr++] = (byte) decodedData;
+ break;
+ }
+ bits = _decodeBase64Escape(b64variant, ch, 2);
+ }
+ if (bits == Base64Variant.BASE64_VALUE_PADDING) {
+ // Ok, must get padding
+ ch = _inputData.readUnsignedByte();
+ if (!b64variant.usesPaddingChar(ch)) {
+ throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
+ }
+ // Got 12 bits, only need 8, need to shift
+ decodedData >>= 4;
+ buffer[outputPtr++] = (byte) decodedData;
+ continue;
+ }
+ }
+ // Nope, 2 or 3 bytes
+ decodedData = (decodedData << 6) | bits;
+ // fourth and last base64 char; can be padding, but not ws
+ ch = _inputData.readUnsignedByte();
+ bits = b64variant.decodeBase64Char(ch);
+ if (bits < 0) {
+ if (bits != Base64Variant.BASE64_VALUE_PADDING) {
+ // could also just be 'missing' padding
+ if (ch == '"' && !b64variant.usesPadding()) {
+ decodedData >>= 2;
+ buffer[outputPtr++] = (byte) (decodedData >> 8);
+ buffer[outputPtr++] = (byte) decodedData;
+ break;
+ }
+ bits = _decodeBase64Escape(b64variant, ch, 3);
+ }
+ if (bits == Base64Variant.BASE64_VALUE_PADDING) {
+ /* With padding we only get 2 bytes; but we have
+ * to shift it a bit so it is identical to triplet
+ * case with partial output.
+ * 3 chars gives 3x6 == 18 bits, of which 2 are
+ * dummies, need to discard:
+ */
+ decodedData >>= 2;
+ buffer[outputPtr++] = (byte) (decodedData >> 8);
+ buffer[outputPtr++] = (byte) decodedData;
+ continue;
+ }
+ }
+ // otherwise, our triplet is now complete
+ decodedData = (decodedData << 6) | bits;
+ buffer[outputPtr++] = (byte) (decodedData >> 16);
+ buffer[outputPtr++] = (byte) (decodedData >> 8);
+ buffer[outputPtr++] = (byte) decodedData;
+ }
+ _tokenIncomplete = false;
+ if (outputPtr > 0) {
+ outputCount += outputPtr;
+ out.write(buffer, 0, outputPtr);
+ }
+ return outputCount;
+ }
+
+ /*
+ /**********************************************************
+ /* Public API, traversal, basic
+ /**********************************************************
+ */
+
+ /**
+ * @return Next token from the stream, if any found, or null
+ * to indicate end-of-input
+ */
+ @Override
+ public JsonToken nextToken() throws IOException
+ {
+ /* First: field names are special -- we will always tokenize
+ * (part of) value along with field name to simplify
+ * state handling. If so, can and need to use secondary token:
+ */
+ if (_currToken == JsonToken.FIELD_NAME) {
+ return _nextAfterName();
+ }
+ // But if we didn't already have a name, and (partially?) decode number,
+ // need to ensure no numeric information is leaked
+ _numTypesValid = NR_UNKNOWN;
+ if (_tokenIncomplete) {
+ _skipString(); // only strings can be partial
+ }
+ int i = _skipWS();
+ // 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);
+ }
+
+ // Nope: do we then expect a comma?
+ if (_parsingContext.expectComma()) {
+ if (i != INT_COMMA) {
+ _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
+ }
+ i = _skipWS();
+ }
+
+ /* And should we now have a name? Always true for
+ * Object contexts, since the intermediate 'expect-value'
+ * state is never retained.
+ */
+ if (!_parsingContext.inObject()) {
+ return _nextTokenNotInObject(i);
+ }
+ // So first parse the field name itself:
+ String n = _parseName(i);
+ _parsingContext.setCurrentName(n);
+ _currToken = JsonToken.FIELD_NAME;
+
+ i = _skipColon();
+
+ // Ok: we must have a value... what is it? Strings are very common, check first:
+ if (i == INT_QUOTE) {
+ _tokenIncomplete = true;
+ _nextToken = JsonToken.VALUE_STRING;
+ return _currToken;
+ }
+ JsonToken t;
+
+ switch (i) {
+ case '-':
+ t = _parseNegNumber();
+ break;
+
+ /* Should we have separate handling for plus? Although
+ * it is not allowed per se, it may be erroneously used,
+ * and could be indicate by a more specific error message.
+ */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ t = _parsePosNumber(i);
+ break;
+ case 'f':
+ _matchToken("false", 1);
+ t = JsonToken.VALUE_FALSE;
+ break;
+ case 'n':
+ _matchToken("null", 1);
+ t = JsonToken.VALUE_NULL;
+ break;
+ case 't':
+ _matchToken("true", 1);
+ t = JsonToken.VALUE_TRUE;
+ break;
+ case '[':
+ t = JsonToken.START_ARRAY;
+ break;
+ case '{':
+ t = JsonToken.START_OBJECT;
+ break;
+
+ default:
+ t = _handleUnexpectedValue(i);
+ }
+ _nextToken = t;
+ return _currToken;
+ }
+
+ private final JsonToken _nextTokenNotInObject(int i) throws IOException
+ {
+ if (i == INT_QUOTE) {
+ _tokenIncomplete = true;
+ return (_currToken = JsonToken.VALUE_STRING);
+ }
+ switch (i) {
+ case '[':
+ _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+ return (_currToken = JsonToken.START_ARRAY);
+ case '{':
+ _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+ return (_currToken = JsonToken.START_OBJECT);
+ case 't':
+ _matchToken("true", 1);
+ return (_currToken = JsonToken.VALUE_TRUE);
+ case 'f':
+ _matchToken("false", 1);
+ return (_currToken = JsonToken.VALUE_FALSE);
+ case 'n':
+ _matchToken("null", 1);
+ return (_currToken = JsonToken.VALUE_NULL);
+ case '-':
+ return (_currToken = _parseNegNumber());
+ /* Should we have separate handling for plus? Although
+ * it is not allowed per se, it may be erroneously used,
+ * and could be indicated by a more specific error message.
+ */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return (_currToken = _parsePosNumber(i));
+ }
+ return (_currToken = _handleUnexpectedValue(i));
+ }
+
+ private final JsonToken _nextAfterName()
+ {
+ _nameCopied = false; // need to invalidate if it was copied
+ JsonToken t = _nextToken;
+ _nextToken = null;
+
+ // Also: may need to start new context?
+ if (t == JsonToken.START_ARRAY) {
+ _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+ } else if (t == JsonToken.START_OBJECT) {
+ _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+ }
+ return (_currToken = t);
+ }
+
+ @Override
+ public void finishToken() throws IOException {
+ if (_tokenIncomplete) {
+ _tokenIncomplete = false;
+ _finishString(); // only strings can be incomplete
+ }
+ }
+
+ /*
+ /**********************************************************
+ /* Public API, traversal, nextXxxValue/nextFieldName
+ /**********************************************************
+ */
+
+ // Can not implement without look-ahead...
+// public boolean nextFieldName(SerializableString str) throws IOException
+
+ @Override
+ public String nextFieldName() throws IOException
+ {
+ // // // Note: this is almost a verbatim copy of nextToken()
+
+ _numTypesValid = NR_UNKNOWN;
+ if (_currToken == JsonToken.FIELD_NAME) {
+ _nextAfterName();
+ return null;
+ }
+ if (_tokenIncomplete) {
+ _skipString();
+ }
+ int i = _skipWS();
+ _binaryValue = null;
+ _tokenInputRow = _currInputRow;
+
+ if (i == INT_RBRACKET) {
+ if (!_parsingContext.inArray()) {
+ _reportMismatchedEndMarker(i, '}');
+ }
+ _parsingContext = _parsingContext.clearAndGetParent();
+ _currToken = JsonToken.END_ARRAY;
+ return null;
+ }
+ if (i == INT_RCURLY) {
+ if (!_parsingContext.inObject()) {
+ _reportMismatchedEndMarker(i, ']');
+ }
+ _parsingContext = _parsingContext.clearAndGetParent();
+ _currToken = JsonToken.END_OBJECT;
+ return null;
+ }
+
+ // Nope: do we then expect a comma?
+ if (_parsingContext.expectComma()) {
+ if (i != INT_COMMA) {
+ _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
+ }
+ i = _skipWS();
+ }
+ if (!_parsingContext.inObject()) {
+ _nextTokenNotInObject(i);
+ return null;
+ }
+
+ final String nameStr = _parseName(i);
+ _parsingContext.setCurrentName(nameStr);
+ _currToken = JsonToken.FIELD_NAME;
+
+ i = _skipColon();
+ if (i == INT_QUOTE) {
+ _tokenIncomplete = true;
+ _nextToken = JsonToken.VALUE_STRING;
+ return nameStr;
+ }
+ JsonToken t;
+ switch (i) {
+ case '-':
+ t = _parseNegNumber();
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ t = _parsePosNumber(i);
+ break;
+ case 'f':
+ _matchToken("false", 1);
+ t = JsonToken.VALUE_FALSE;
+ break;
+ case 'n':
+ _matchToken("null", 1);
+ t = JsonToken.VALUE_NULL;
+ break;
+ case 't':
+ _matchToken("true", 1);
+ t = JsonToken.VALUE_TRUE;
+ break;
+ case '[':
+ t = JsonToken.START_ARRAY;
+ break;
+ case '{':
+ t = JsonToken.START_OBJECT;
+ break;
+
+ default:
+ t = _handleUnexpectedValue(i);
+ }
+ _nextToken = t;
+ return nameStr;
+ }
+
+ @Override
+ public String nextTextValue() throws IOException
+ {
+ // two distinct cases; either got name and we know next type, or 'other'
+ if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
+ _nameCopied = false;
+ JsonToken t = _nextToken;
+ _nextToken = null;
+ _currToken = t;
+ if (t == JsonToken.VALUE_STRING) {
+ if (_tokenIncomplete) {
+ _tokenIncomplete = false;
+ return _finishAndReturnString();
+ }
+ return _textBuffer.contentsAsString();
+ }
+ if (t == JsonToken.START_ARRAY) {
+ _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+ } else if (t == JsonToken.START_OBJECT) {
+ _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+ }
+ return null;
+ }
+ return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
+ }
+
+ @Override
+ public int nextIntValue(int defaultValue) throws IOException
+ {
+ // two distinct cases; either got name and we know next type, or 'other'
+ if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
+ _nameCopied = false;
+ JsonToken t = _nextToken;
+ _nextToken = null;
+ _currToken = t;
+ if (t == JsonToken.VALUE_NUMBER_INT) {
+ return getIntValue();
+ }
+ if (t == JsonToken.START_ARRAY) {
+ _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+ } else if (t == JsonToken.START_OBJECT) {
+ _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+ }
+ return defaultValue;
+ }
+ return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
+ }
+
+ @Override
+ public long nextLongValue(long defaultValue) throws IOException
+ {
+ // two distinct cases; either got name and we know next type, or 'other'
+ if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
+ _nameCopied = false;
+ JsonToken t = _nextToken;
+ _nextToken = null;
+ _currToken = t;
+ if (t == JsonToken.VALUE_NUMBER_INT) {
+ return getLongValue();
+ }
+ if (t == JsonToken.START_ARRAY) {
+ _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+ } else if (t == JsonToken.START_OBJECT) {
+ _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+ }
+ return defaultValue;
+ }
+ return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
+ }
+
+ @Override
+ public Boolean nextBooleanValue() throws IOException
+ {
+ // two distinct cases; either got name and we know next type, or 'other'
+ if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
+ _nameCopied = false;
+ JsonToken t = _nextToken;
+ _nextToken = null;
+ _currToken = t;
+ if (t == JsonToken.VALUE_TRUE) {
+ return Boolean.TRUE;
+ }
+ if (t == JsonToken.VALUE_FALSE) {
+ return Boolean.FALSE;
+ }
+ if (t == JsonToken.START_ARRAY) {
+ _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+ } else if (t == JsonToken.START_OBJECT) {
+ _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+ }
+ return null;
+ }
+
+ JsonToken t = nextToken();
+ if (t == JsonToken.VALUE_TRUE) {
+ return Boolean.TRUE;
+ }
+ if (t == JsonToken.VALUE_FALSE) {
+ return Boolean.FALSE;
+ }
+ return null;
+ }
+
+ /*
+ /**********************************************************
+ /* Internal methods, number parsing
+ /**********************************************************
+ */
+
+ /**
+ * Initial parsing method for number values. It needs to be able
+ * to parse enough input to be able to determine whether the
+ * value is to be considered a simple integer value, or a more
+ * generic decimal value: latter of which needs to be expressed
+ * as a floating point number. The basic rule is that if the number
+ * has no fractional or exponential part, it is an integer; otherwise
+ * a floating point number.
+ *<p>
+ * Because much of input has to be processed in any case, no partial
+ * parsing is done: all input text will be stored for further
+ * processing. However, actual numeric value conversion will be
+ * deferred, since it is usually the most complicated and costliest
+ * part of processing.
+ */
+ protected JsonToken _parsePosNumber(int c) throws IOException
+ {
+ char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
+ int outPtr;
+
+ // One special case: if first char is 0, must not be followed by a digit.
+ // Gets bit tricky as we only want to retain 0 if it's the full value
+ if (c == INT_0) {
+ c = _handleLeadingZeroes();
+ if (c <= INT_9 && c >= INT_0) { // skip if followed by digit
+ outPtr = 0;
+ } else {
+ outBuf[0] = '0';
+ outPtr = 1;
+ }
+ } else {
+ outBuf[0] = (char) c;
+ c = _inputData.readUnsignedByte();
+ outPtr = 1;
+ }
+ int intLen = outPtr;
+
+ // With this, we have a nice and tight loop:
+ while (c <= INT_9 && c >= INT_0) {
+ ++intLen;
+ outBuf[outPtr++] = (char) c;
+ c = _inputData.readUnsignedByte();
+ }
+ if (c == '.' || c == 'e' || c == 'E') {
+ return _parseFloat(outBuf, outPtr, c, false, intLen);
+ }
+ _textBuffer.setCurrentLength(outPtr);
+ // As per [core#105], need separating space between root values; check here
+ if (_parsingContext.inRoot()) {
+ _verifyRootSpace();
+ } else {
+ _nextByte = c;
+ }
+ // And there we have it!
+ return resetInt(false, intLen);
+ }
+
+ protected JsonToken _parseNegNumber() throws IOException
+ {
+ char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
+ int outPtr = 0;
+
+ // Need to prepend sign?
+ outBuf[outPtr++] = '-';
+ int c = _inputData.readUnsignedByte();
+ outBuf[outPtr++] = (char) c;
+ // Note: must be followed by a digit
+ if (c <= INT_0) {
+ // One special case: if first char is 0 need to check no leading zeroes
+ if (c == INT_0) {
+ c = _handleLeadingZeroes();
+ } else {
+ return _handleInvalidNumberStart(c, true);
+ }
+ } else {
+ if (c > INT_9) {
+ return _handleInvalidNumberStart(c, true);
+ }
+ c = _inputData.readUnsignedByte();
+ }
+ // Ok: we can first just add digit we saw first:
+ int intLen = 1;
+
+ // With this, we have a nice and tight loop:
+ while (c <= INT_9 && c >= INT_0) {
+ ++intLen;
+ outBuf[outPtr++] = (char) c;
+ c = _inputData.readUnsignedByte();
+ }
+ if (c == '.' || c == 'e' || c == 'E') {
+ return _parseFloat(outBuf, outPtr, c, true, intLen);
+ }
+ _textBuffer.setCurrentLength(outPtr);
+ // As per [core#105], need separating space between root values; check here
+ _nextByte = c;
+ if (_parsingContext.inRoot()) {
+ _verifyRootSpace();
+ }
+ // And there we have it!
+ return resetInt(true, intLen);
+ }
+
+ /**
+ * Method called when we have seen one zero, and want to ensure
+ * it is not followed by another, or, if leading zeroes allowed,
+ * skipped redundant ones.
+ *
+ * @return Character immediately following zeroes
+ */
+ private final int _handleLeadingZeroes() throws IOException
+ {
+ int ch = _inputData.readUnsignedByte();
+ // if not followed by a number (probably '.'); return zero as is, to be included
+ if (ch < INT_0 || ch > INT_9) {
+ return ch;
+ }
+ // we may want to allow leading zeroes them, after all...
+ if (!isEnabled(Feature.ALLOW_NUMERIC_LEADING_ZEROS)) {
+ reportInvalidNumber("Leading zeroes not allowed");
+ }
+ // if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number)
+ while (ch == INT_0) {
+ ch = _inputData.readUnsignedByte();
+ }
+ return ch;
+ }
+
+ private final JsonToken _parseFloat(char[] outBuf, int outPtr, int c,
+ boolean negative, int integerPartLength) throws IOException
+ {
+ int fractLen = 0;
+
+ // And then see if we get other parts
+ if (c == INT_PERIOD) { // yes, fraction
+ outBuf[outPtr++] = (char) c;
+
+ fract_loop:
+ while (true) {
+ c = _inputData.readUnsignedByte();
+ if (c < INT_0 || c > INT_9) {
+ break fract_loop;
+ }
+ ++fractLen;
+ if (outPtr >= outBuf.length) {
+ outBuf = _textBuffer.finishCurrentSegment();
+ outPtr = 0;
+ }
+ outBuf[outPtr++] = (char) c;
+ }
+ // must be followed by sequence of ints, one minimum
+ if (fractLen == 0) {
+ reportUnexpectedNumberChar(c, "Decimal point not followed by a digit");
+ }
+ }
+
+ int expLen = 0;
+ if (c == INT_e || c == INT_E) { // exponent?
+ if (outPtr >= outBuf.length) {
+ outBuf = _textBuffer.finishCurrentSegment();
+ outPtr = 0;
+ }
+ outBuf[outPtr++] = (char) c;
+ c = _inputData.readUnsignedByte();
+ // Sign indicator?
+ if (c == '-' || c == '+') {
+ if (outPtr >= outBuf.length) {
+ outBuf = _textBuffer.finishCurrentSegment();
+ outPtr = 0;
+ }
+ outBuf[outPtr++] = (char) c;
+ c = _inputData.readUnsignedByte();
+ }
+ while (c <= INT_9 && c >= INT_0) {
+ ++expLen;
+ if (outPtr >= outBuf.length) {
+ outBuf = _textBuffer.finishCurrentSegment();
+ outPtr = 0;
+ }
+ outBuf[outPtr++] = (char) c;
+ c = _inputData.readUnsignedByte();
+ }
+ // must be followed by sequence of ints, one minimum
+ if (expLen == 0) {
+ reportUnexpectedNumberChar(c, "Exponent indicator not followed by a digit");
+ }
+ }
+
+ // Ok; unless we hit end-of-input, need to push last char read back
+ // As per #105, need separating space between root values; check here
+ _nextByte = c;
+ if (_parsingContext.inRoot()) {
+ _verifyRootSpace();
+ }
+ _textBuffer.setCurrentLength(outPtr);
+
+ // And there we have it!
+ return resetFloat(negative, integerPartLength, fractLen, expLen);
+ }
+
+ /**
+ * Method called to ensure that a root-value is followed by a space token,
+ * if possible.
+ *<p>
+ * NOTE: with {@link DataInput} source, not really feasible, up-front.
+ * If we did want, we could rearrange things to require space before
+ * next read, but initially let's just do nothing.
+ */
+ private final void _verifyRootSpace() throws IOException
+ {
+ int ch = _nextByte;
+ if (ch <= INT_SPACE) {
+ _nextByte = -1;
+ if (ch == INT_CR || ch == INT_LF) {
+ ++_currInputRow;
+ }
+ return;
+ }
+ _reportMissingRootWS(ch);
+ }
+
+ /*
+ /**********************************************************
+ /* Internal methods, secondary parsing
+ /**********************************************************
+ */
+
+ protected final String _parseName(int i) throws IOException
+ {
+ if (i != INT_QUOTE) {
+ return _handleOddName(i);
+ }
+ // If so, can also unroll loops nicely
+ /* 25-Nov-2008, tatu: This may seem weird, but here we do
+ * NOT want to worry about UTF-8 decoding. Rather, we'll
+ * assume that part is ok (if not it will get caught
+ * later on), and just handle quotes and backslashes here.
+ */
+ final int[] codes = _icLatin1;
+
+ int q = _inputData.readUnsignedByte();
+
+ if (codes[q] == 0) {
+ i = _inputData.readUnsignedByte();
+ if (codes[i] == 0) {
+ q = (q << 8) | i;
+ i = _inputData.readUnsignedByte();
+ if (codes[i] == 0) {
+ q = (q << 8) | i;
+ i = _inputData.readUnsignedByte();
+ if (codes[i] == 0) {
+ q = (q << 8) | i;
+ i = _inputData.readUnsignedByte();
+ if (codes[i] == 0) {
+ _quad1 = q;
+ return _parseMediumName(i);
+ }
+ if (i == INT_QUOTE) { // 4 byte/char case or broken
+ return findName(q, 4);
+ }
+ return parseName(q, i, 4);
+ }
+ if (i == INT_QUOTE) { // 3 byte/char case or broken
+ return findName(q, 3);
+ }
+ return parseName(q, i, 3);
+ }
+ if (i == INT_QUOTE) { // 2 byte/char case or broken
+ return findName(q, 2);
+ }
+ return parseName(q, i, 2);
+ }
+ if (i == INT_QUOTE) { // one byte/char case or broken
+ return findName(q, 1);
+ }
+ return parseName(q, i, 1);
+ }
+ if (q == INT_QUOTE) { // special case, ""
+ return "";
+ }
+ return parseName(0, q, 0); // quoting or invalid char
+ }
+
+ private final String _parseMediumName(int q2) throws IOException
+ {
+ final int[] codes = _icLatin1;
+
+ // Ok, got 5 name bytes so far
+ int i = _inputData.readUnsignedByte();
+ if (codes[i] != 0) {
+ if (i == INT_QUOTE) { // 5 bytes
+ return findName(_quad1, q2, 1);
+ }
+ return parseName(_quad1, q2, i, 1); // quoting or invalid char
+ }
+ q2 = (q2 << 8) | i;
+ i = _inputData.readUnsignedByte();
+ if (codes[i] != 0) {
+ if (i == INT_QUOTE) { // 6 bytes
+ return findName(_quad1, q2, 2);
+ }
+ return parseName(_quad1, q2, i, 2);
+ }
+ q2 = (q2 << 8) | i;
+ i = _inputData.readUnsignedByte();
+ if (codes[i] != 0) {
+ if (i == INT_QUOTE) { // 7 bytes
+ return findName(_quad1, q2, 3);
+ }
+ return parseName(_quad1, q2, i, 3);
+ }
+ q2 = (q2 << 8) | i;
+ i = _inputData.readUnsignedByte();
+ if (codes[i] != 0) {
+ if (i == INT_QUOTE) { // 8 bytes
+ return findName(_quad1, q2, 4);
+ }
+ return parseName(_quad1, q2, i, 4);
+ }
+ return _parseMediumName2(i, q2);
+ }
+
+ private final String _parseMediumName2(int q3, final int q2) throws IOException
+ {
+ final int[] codes = _icLatin1;
+
+ // Got 9 name bytes so far
+ int i = _inputData.readUnsignedByte();
+ if (codes[i] != 0) {
+ if (i == INT_QUOTE) { // 9 bytes
+ return findName(_quad1, q2, q3, 1);
+ }
+ return parseName(_quad1, q2, q3, i, 1);
+ }
+ q3 = (q3 << 8) | i;
+ i = _inputData.readUnsignedByte();
+ if (codes[i] != 0) {
+ if (i == INT_QUOTE) { // 10 bytes
+ return findName(_quad1, q2, q3, 2);
+ }
+ return parseName(_quad1, q2, q3, i, 2);
+ }
+ q3 = (q3 << 8) | i;
+ i = _inputData.readUnsignedByte();
+ if (codes[i] != 0) {
+ if (i == INT_QUOTE) { // 11 bytes
+ return findName(_quad1, q2, q3, 3);
+ }
+ return parseName(_quad1, q2, q3, i, 3);
+ }
+ q3 = (q3 << 8) | i;
+ i = _inputData.readUnsignedByte();
+ if (codes[i] != 0) {
+ if (i == INT_QUOTE) { // 12 bytes
+ return findName(_quad1, q2, q3, 4);
+ }
+ return parseName(_quad1, q2, q3, i, 4);
+ }
+ return _parseLongName(i, q2, q3);
+ }
+
+ private final String _parseLongName(int q, final int q2, int q3) throws IOException
+ {
+ _quadBuffer[0] = _quad1;
+ _quadBuffer[1] = q2;
+ _quadBuffer[2] = q3;
+
+ // As explained above, will ignore UTF-8 encoding at this point
+ final int[] codes = _icLatin1;
+ int qlen = 3;
+
+ while (true) {
+ int i = _inputData.readUnsignedByte();
+ if (codes[i] != 0) {
+ if (i == INT_QUOTE) {
+ return findName(_quadBuffer, qlen, q, 1);
+ }
+ return parseEscapedName(_quadBuffer, qlen, q, i, 1);
+ }
+
+ q = (q << 8) | i;
+ i = _inputData.readUnsignedByte();
+ if (codes[i] != 0) {
+ if (i == INT_QUOTE) {
+ return findName(_quadBuffer, qlen, q, 2);
+ }
+ return parseEscapedName(_quadBuffer, qlen, q, i, 2);
+ }
+
+ q = (q << 8) | i;
+ i = _inputData.readUnsignedByte();
+ if (codes[i] != 0) {
+ if (i == INT_QUOTE) {
+ return findName(_quadBuffer, qlen, q, 3);
+ }
+ return parseEscapedName(_quadBuffer, qlen, q, i, 3);
+ }
+
+ q = (q << 8) | i;
+ i = _inputData.readUnsignedByte();
+ if (codes[i] != 0) {
+ if (i == INT_QUOTE) {
+ return findName(_quadBuffer, qlen, q, 4);
+ }
+ return parseEscapedName(_quadBuffer, qlen, q, i, 4);
+ }
+
+ // Nope, no end in sight. Need to grow quad array etc
+ if (qlen >= _quadBuffer.length) {
+ _quadBuffer = _growArrayBy(_quadBuffer, qlen);
+ }
+ _quadBuffer[qlen++] = q;
+ q = i;
+ }
+ }
+
+ private final String parseName(int q1, int ch, int lastQuadBytes) throws IOException {
+ return parseEscapedName(_quadBuffer, 0, q1, ch, lastQuadBytes);
+ }
+
+ private final String parseName(int q1, int q2, int ch, int lastQuadBytes) throws IOException {
+ _quadBuffer[0] = q1;
+ return parseEscapedName(_quadBuffer, 1, q2, ch, lastQuadBytes);
+ }
+
+ private final String parseName(int q1, int q2, int q3, int ch, int lastQuadBytes) throws IOException {
+ _quadBuffer[0] = q1;
+ _quadBuffer[1] = q2;
+ return parseEscapedName(_quadBuffer, 2, q3, ch, lastQuadBytes);
+ }
+
+ /**
+ * Slower parsing method which is generally branched to when
+ * an escape sequence is detected (or alternatively for long
+ * names, one crossing input buffer boundary).
+ * Needs to be able to handle more exceptional cases, gets slower,
+ * and hance is offlined to a separate method.
+ */
+ protected final String parseEscapedName(int[] quads, int qlen, int currQuad, int ch,
+ int currQuadBytes) throws IOException
+ {
+ /* 25-Nov-2008, tatu: This may seem weird, but here we do not want to worry about
+ * UTF-8 decoding yet. Rather, we'll assume that part is ok (if not it will get
+ * caught later on), and just handle quotes and backslashes here.
+ */
+ final int[] codes = _icLatin1;
+
+ while (true) {
+ if (codes[ch] != 0) {
+ if (ch == INT_QUOTE) { // we are done
+ break;
+ }
+ // Unquoted white space?
+ if (ch != INT_BACKSLASH) {
+ // As per [JACKSON-208], call can now return:
+ _throwUnquotedSpace(ch, "name");
+ } else {
+ // Nope, escape sequence
+ ch = _decodeEscaped();
+ }
+ /* Oh crap. May need to UTF-8 (re-)encode it, if it's
+ * beyond 7-bit ascii. Gets pretty messy.
+ * If this happens often, may want to use different name
+ * canonicalization to avoid these hits.
+ */
+ if (ch > 127) {
+ // Ok, we'll need room for first byte right away
+ if (currQuadBytes >= 4) {
+ if (qlen >= quads.length) {
+ _quadBuffer = quads = _growArrayBy(quads, quads.length);
+ }
+ quads[qlen++] = currQuad;
+ currQuad = 0;
+ currQuadBytes = 0;
+ }
+ if (ch < 0x800) { // 2-byte
+ currQuad = (currQuad << 8) | (0xc0 | (ch >> 6));
+ ++currQuadBytes;
+ // Second byte gets output below:
+ } else { // 3 bytes; no need to worry about surrogates here
+ currQuad = (currQuad << 8) | (0xe0 | (ch >> 12));
+ ++currQuadBytes;
+ // need room for middle byte?
+ if (currQuadBytes >= 4) {
+ if (qlen >= quads.length) {
+ _quadBuffer = quads = _growArrayBy(quads, quads.length);
+ }
+ quads[qlen++] = currQuad;
+ currQuad = 0;
+ currQuadBytes = 0;
+ }
+ currQuad = (currQuad << 8) | (0x80 | ((ch >> 6) & 0x3f));
+ ++currQuadBytes;
+ }
+ // And same last byte in both cases, gets output below:
+ ch = 0x80 | (ch & 0x3f);
+ }
+ }
+ // Ok, we have one more byte to add at any rate:
+ if (currQuadBytes < 4) {
+ ++currQuadBytes;
+ currQuad = (currQuad << 8) | ch;
+ } else {
+ if (qlen >= quads.length) {
+ _quadBuffer = quads = _growArrayBy(quads, quads.length);
+ }
+ quads[qlen++] = currQuad;
+ currQuad = ch;
+ currQuadBytes = 1;
+ }
+ ch = _inputData.readUnsignedByte();
+ }
+
+ if (currQuadBytes > 0) {
+ if (qlen >= quads.length) {
+ _quadBuffer = quads = _growArrayBy(quads, quads.length);
+ }
+ quads[qlen++] = pad(currQuad, currQuadBytes);
+ }
+ String name = _symbols.findName(quads, qlen);
+ if (name == null) {
+ name = addName(quads, qlen, currQuadBytes);
+ }
+ return name;
+ }
+
+ /**
+ * Method called when we see non-white space character other
+ * than double quote, when expecting a field name.
+ * In standard mode will just throw an exception; but
+ * in non-standard modes may be able to parse name.
+ */
+ protected String _handleOddName(int ch) throws IOException
+ {
+ if (ch == '\'' && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) {
+ return _parseAposName();
+ }
+ if (!isEnabled(Feature.ALLOW_UNQUOTED_FIELD_NAMES)) {
+ char c = (char) _decodeCharForError(ch);
+ _reportUnexpectedChar(c, "was expecting double-quote to start field name");
+ }
+ /* Also: note that although we use a different table here,
+ * it does NOT handle UTF-8 decoding. It'll just pass those
+ * high-bit codes as acceptable for later decoding.
+ */
+ final int[] codes = CharTypes.getInputCodeUtf8JsNames();
+ // Also: must start with a valid character...
+ if (codes[ch] != 0) {
+ _reportUnexpectedChar(ch, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name");
+ }
+
+ /* Ok, now; instead of ultra-optimizing parsing here (as with
+ * regular JSON names), let's just use the generic "slow"
+ * variant. Can measure its impact later on if need be
+ */
+ int[] quads = _quadBuffer;
+ int qlen = 0;
+ int currQuad = 0;
+ int currQuadBytes = 0;
+
+ while (true) {
+ // Ok, we have one more byte to add at any rate:
+ if (currQuadBytes < 4) {
+ ++currQuadBytes;
+ currQuad = (currQuad << 8) | ch;
+ } else {
+ if (qlen >= quads.length) {
+ _quadBuffer = quads = _growArrayBy(quads, quads.length);
+ }
+ quads[qlen++] = currQuad;
+ currQuad = ch;
+ currQuadBytes = 1;
+ }
+ ch = _inputData.readUnsignedByte();
+ if (codes[ch] != 0) {
+ break;
+ }
+ }
+ // Note: we must "push back" character read here for future consumption
+ _nextByte = ch;
+ if (currQuadBytes > 0) {
+ if (qlen >= quads.length) {
+ _quadBuffer = quads = _growArrayBy(quads, quads.length);
+ }
+ quads[qlen++] = currQuad;
+ }
+ String name = _symbols.findName(quads, qlen);
+ if (name == null) {
+ name = addName(quads, qlen, currQuadBytes);
+ }
+ return name;
+ }
+
+ /* Parsing to allow optional use of non-standard single quotes.
+ * Plenty of duplicated code;
+ * main reason being to try to avoid slowing down fast path
+ * for valid JSON -- more alternatives, more code, generally
+ * bit slower execution.
+ */
+ protected String _parseAposName() throws IOException
+ {
+ int ch = _inputData.readUnsignedByte();
+ if (ch == '\'') { // special case, ''
+ return "";
+ }
+ int[] quads = _quadBuffer;
+ int qlen = 0;
+ int currQuad = 0;
+ int currQuadBytes = 0;
+
+ // Copied from parseEscapedFieldName, with minor mods:
+
+ final int[] codes = _icLatin1;
+
+ while (true) {
+ if (ch == '\'') {
+ break;
+ }
+ // additional check to skip handling of double-quotes
+ if (ch != '"' && codes[ch] != 0) {
+ if (ch != '\\') {
+ // Unquoted white space?
+ // As per [JACKSON-208], call can now return:
+ _throwUnquotedSpace(ch, "name");
+ } else {
+ // Nope, escape sequence
+ ch = _decodeEscaped();
+ }
+ /* Oh crap. May need to UTF-8 (re-)encode it, if it's beyond
+ * 7-bit ASCII. Gets pretty messy. If this happens often, may want
+ * to use different name canonicalization to avoid these hits.
+ */
+ if (ch > 127) {
+ // Ok, we'll need room for first byte right away
+ if (currQuadBytes >= 4) {
+ if (qlen >= quads.length) {
+ _quadBuffer = quads = _growArrayBy(quads, quads.length);
+ }
+ quads[qlen++] = currQuad;
+ currQuad = 0;
+ currQuadBytes = 0;
+ }
+ if (ch < 0x800) { // 2-byte
+ currQuad = (currQuad << 8) | (0xc0 | (ch >> 6));
+ ++currQuadBytes;
+ // Second byte gets output below:
+ } else { // 3 bytes; no need to worry about surrogates here
+ currQuad = (currQuad << 8) | (0xe0 | (ch >> 12));
+ ++currQuadBytes;
+ // need room for middle byte?
+ if (currQuadBytes >= 4) {
+ if (qlen >= quads.length) {
+ _quadBuffer = quads = _growArrayBy(quads, quads.length);
+ }
+ quads[qlen++] = currQuad;
+ currQuad = 0;
+ currQuadBytes = 0;
+ }
+ currQuad = (currQuad << 8) | (0x80 | ((ch >> 6) & 0x3f));
+ ++currQuadBytes;
+ }
+ // And same last byte in both cases, gets output below:
+ ch = 0x80 | (ch & 0x3f);
+ }
+ }
+ // Ok, we have one more byte to add at any rate:
+ if (currQuadBytes < 4) {
+ ++currQuadBytes;
+ currQuad = (currQuad << 8) | ch;
+ } else {
+ if (qlen >= quads.length) {
+ _quadBuffer = quads = _growArrayBy(quads, quads.length);
+ }
+ quads[qlen++] = currQuad;
+ currQuad = ch;
+ currQuadBytes = 1;
+ }
+ ch = _inputData.readUnsignedByte();
+ }
+
+ if (currQuadBytes > 0) {
+ if (qlen >= quads.length) {
+ _quadBuffer = quads = _growArrayBy(quads, quads.length);
+ }
+ quads[qlen++] = pad(currQuad, currQuadBytes);
+ }
+ String name = _symbols.findName(quads, qlen);
+ if (name == null) {
+ name = addName(quads, qlen, currQuadBytes);
+ }
+ return name;
+ }
+
+ /*
+ /**********************************************************
+ /* Internal methods, symbol (name) handling
+ /**********************************************************
+ */
+
+ private final String findName(int q1, int lastQuadBytes) throws JsonParseException
+ {
+ q1 = pad(q1, lastQuadBytes);
+ // Usually we'll find it from the canonical symbol table already
+ String name = _symbols.findName(q1);
+ if (name != null) {
+ return name;
+ }
+ // If not, more work. We'll need add stuff to buffer
+ _quadBuffer[0] = q1;
+ return addName(_quadBuffer, 1, lastQuadBytes);
+ }
+
+ private final String findName(int q1, int q2, int lastQuadBytes) throws JsonParseException
+ {
+ q2 = pad(q2, lastQuadBytes);
+ // Usually we'll find it from the canonical symbol table already
+ String name = _symbols.findName(q1, q2);
+ if (name != null) {
+ return name;
+ }
+ // If not, more work. We'll need add stuff to buffer
+ _quadBuffer[0] = q1;
+ _quadBuffer[1] = q2;
+ return addName(_quadBuffer, 2, lastQuadBytes);
+ }
+
+ private final String findName(int q1, int q2, int q3, int lastQuadBytes) throws JsonParseException
+ {
+ q3 = pad(q3, lastQuadBytes);
+ String name = _symbols.findName(q1, q2, q3);
+ if (name != null) {
+ return name;
+ }
+ int[] quads = _quadBuffer;
+ quads[0] = q1;
+ quads[1] = q2;
+ quads[2] = pad(q3, lastQuadBytes);
+ return addName(quads, 3, lastQuadBytes);
+ }
+
+ private final String findName(int[] quads, int qlen, int lastQuad, int lastQuadBytes) throws JsonParseException
+ {
+ if (qlen >= quads.length) {
+ _quadBuffer = quads = _growArrayBy(quads, quads.length);
+ }
+ quads[qlen++] = pad(lastQuad, lastQuadBytes);
+ String name = _symbols.findName(quads, qlen);
+ if (name == null) {
+ return addName(quads, qlen, lastQuadBytes);
+ }
+ return name;
+ }
+
+ /**
+ * This is the main workhorse method used when we take a symbol
+ * table miss. It needs to demultiplex individual bytes, decode
+ * multi-byte chars (if any), and then construct Name instance
+ * and add it to the symbol table.
+ */
+ private final String addName(int[] quads, int qlen, int lastQuadBytes) throws JsonParseException
+ {
+ /* Ok: must decode UTF-8 chars. No other validation is
+ * needed, since unescaping has been done earlier as necessary
+ * (as well as error reporting for unescaped control chars)
+ */
+ // 4 bytes per quad, except last one maybe less
+ int byteLen = (qlen << 2) - 4 + lastQuadBytes;
+
+ /* And last one is not correctly aligned (leading zero bytes instead
+ * need to shift a bit, instead of trailing). Only need to shift it
+ * for UTF-8 decoding; need revert for storage (since key will not
+ * be aligned, to optimize lookup speed)
+ */
+ int lastQuad;
+
+ if (lastQuadBytes < 4) {
+ lastQuad = quads[qlen-1];
+ // 8/16/24 bit left shift
+ quads[qlen-1] = (lastQuad << ((4 - lastQuadBytes) << 3));
+ } else {
+ lastQuad = 0;
+ }
+
+ // Need some working space, TextBuffer works well:
+ char[] cbuf = _textBuffer.emptyAndGetCurrentSegment();
+ int cix = 0;
+
+ for (int ix = 0; ix < byteLen; ) {
+ int ch = quads[ix >> 2]; // current quad, need to shift+mask
+ int byteIx = (ix & 3);
+ ch = (ch >> ((3 - byteIx) << 3)) & 0xFF;
+ ++ix;
+
+ if (ch > 127) { // multi-byte
+ int needed;
+ if ((ch & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
+ ch &= 0x1F;
+ needed = 1;
+ } else if ((ch & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
+ ch &= 0x0F;
+ needed = 2;
+ } else if ((ch & 0xF8) == 0xF0) { // 4 bytes; double-char with surrogates and all...
+ ch &= 0x07;
+ needed = 3;
+ } else { // 5- and 6-byte chars not valid xml chars
+ _reportInvalidInitial(ch);
+ needed = ch = 1; // never really gets this far
+ }
+ if ((ix + needed) > byteLen) {
+ _reportInvalidEOF(" in field name", JsonToken.FIELD_NAME);
+ }
+
+ // Ok, always need at least one more:
+ int ch2 = quads[ix >> 2]; // current quad, need to shift+mask
+ byteIx = (ix & 3);
+ ch2 = (ch2 >> ((3 - byteIx) << 3));
+ ++ix;
+
+ if ((ch2 & 0xC0) != 0x080) {
+ _reportInvalidOther(ch2);
+ }
+ ch = (ch << 6) | (ch2 & 0x3F);
+ if (needed > 1) {
+ ch2 = quads[ix >> 2];
+ byteIx = (ix & 3);
+ ch2 = (ch2 >> ((3 - byteIx) << 3));
+ ++ix;
+
+ if ((ch2 & 0xC0) != 0x080) {
+ _reportInvalidOther(ch2);
+ }
+ ch = (ch << 6) | (ch2 & 0x3F);
+ if (needed > 2) { // 4 bytes? (need surrogates on output)
+ ch2 = quads[ix >> 2];
+ byteIx = (ix & 3);
+ ch2 = (ch2 >> ((3 - byteIx) << 3));
+ ++ix;
+ if ((ch2 & 0xC0) != 0x080) {
+ _reportInvalidOther(ch2 & 0xFF);
+ }
+ ch = (ch << 6) | (ch2 & 0x3F);
+ }
+ }
+ if (needed > 2) { // surrogate pair? once again, let's output one here, one later on
+ ch -= 0x10000; // to normalize it starting with 0x0
+ if (cix >= cbuf.length) {
+ cbuf = _textBuffer.expandCurrentSegment();
+ }
+ cbuf[cix++] = (char) (0xD800 + (ch >> 10));
+ ch = 0xDC00 | (ch & 0x03FF);
+ }
+ }
+ if (cix >= cbuf.length) {
+ cbuf = _textBuffer.expandCurrentSegment();
+ }
+ cbuf[cix++] = (char) ch;
+ }
+
+ // Ok. Now we have the character array, and can construct the String
+ String baseName = new String(cbuf, 0, cix);
+ // And finally, un-align if necessary
+ if (lastQuadBytes < 4) {
+ quads[qlen-1] = lastQuad;
+ }
+ return _symbols.addName(baseName, quads, qlen);
+ }
+
+ /*
+ /**********************************************************
+ /* Internal methods, String value parsing
+ /**********************************************************
+ */
+
+ @Override
+ protected void _finishString() throws IOException
+ {
+ int outPtr = 0;
+ char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
+ final int[] codes = _icUTF8;
+ final int outEnd = outBuf.length;
+
+ do {
+ int c = _inputData.readUnsignedByte();
+ if (codes[c] != 0) {
+ if (c == INT_QUOTE) {
+ _textBuffer.setCurrentLength(outPtr);
+ return;
+ }
+ _finishString2(outBuf, outPtr, c);
+ return;
+ }
+ outBuf[outPtr++] = (char) c;
+ } while (outPtr < outEnd);
+ _finishString2(outBuf, outPtr, _inputData.readUnsignedByte());
+ }
+
+ private String _finishAndReturnString() throws IOException
+ {
+ int outPtr = 0;
+ char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
+ final int[] codes = _icUTF8;
+ final int outEnd = outBuf.length;
+
+ do {
+ int c = _inputData.readUnsignedByte();
+ if (codes[c] != 0) {
+ if (c == INT_QUOTE) {
+ return _textBuffer.setCurrentAndReturn(outPtr);
+ }
+ _finishString2(outBuf, outPtr, c);
+ return _textBuffer.contentsAsString();
+ }
+ outBuf[outPtr++] = (char) c;
+ } while (outPtr < outEnd);
+ _finishString2(outBuf, outPtr, _inputData.readUnsignedByte());
+ return _textBuffer.contentsAsString();
+ }
+
+ private final void _finishString2(char[] outBuf, int outPtr, int c)
+ throws IOException
+ {
+ // Here we do want to do full decoding, hence:
+ final int[] codes = _icUTF8;
+ int outEnd = outBuf.length;
+
+ main_loop:
+ for (;; c = _inputData.readUnsignedByte()) {
+ // Then the tight ASCII non-funny-char loop:
+ while (codes[c] == 0) {
+ if (outPtr >= outEnd) {
+ outBuf = _textBuffer.finishCurrentSegment();
+ outPtr = 0;
+ outEnd = outBuf.length;
+ }
+ outBuf[outPtr++] = (char) c;
+ c = _inputData.readUnsignedByte();
+ }
+ // Ok: end marker, escape or multi-byte?
+ if (c == INT_QUOTE) {
+ break main_loop;
+ }
+ switch (codes[c]) {
+ case 1: // backslash
+ c = _decodeEscaped();
+ break;
+ case 2: // 2-byte UTF
+ c = _decodeUtf8_2(c);
+ break;
+ case 3: // 3-byte UTF
+ c = _decodeUtf8_3(c);
+ break;
+ case 4: // 4-byte UTF
+ c = _decodeUtf8_4(c);
+ // Let's add first part right away:
+ outBuf[outPtr++] = (char) (0xD800 | (c >> 10));
+ if (outPtr >= outBuf.length) {
+ outBuf = _textBuffer.finishCurrentSegment();
+ outPtr = 0;
+ outEnd = outBuf.length;
+ }
+ c = 0xDC00 | (c & 0x3FF);
+ // And let the other char output down below
+ break;
+ default:
+ if (c < INT_SPACE) {
+ _throwUnquotedSpace(c, "string value");
+ } else {
+ // Is this good enough error message?
+ _reportInvalidChar(c);
+ }
+ }
+ // Need more room?
+ if (outPtr >= outBuf.length) {
+ outBuf = _textBuffer.finishCurrentSegment();
+ outPtr = 0;
+ outEnd = outBuf.length;
+ }
+ // Ok, let's add char to output:
+ outBuf[outPtr++] = (char) c;
+ }
+ _textBuffer.setCurrentLength(outPtr);
+ }
+
+ /**
+ * Method called to skim through rest of unparsed String value,
+ * if it is not needed. This can be done bit faster if contents
+ * need not be stored for future access.
+ */
+ protected void _skipString() throws IOException
+ {
+ _tokenIncomplete = false;
+
+ // Need to be fully UTF-8 aware here:
+ final int[] codes = _icUTF8;
+
+ main_loop:
+ while (true) {
+ int c;
+
+ ascii_loop:
+ while (true) {
+ c = _inputData.readUnsignedByte();
+ if (codes[c] != 0) {
+ break ascii_loop;
+ }
+ }
+ // Ok: end marker, escape or multi-byte?
+ if (c == INT_QUOTE) {
+ break main_loop;
+ }
+
+ switch (codes[c]) {
+ case 1: // backslash
+ _decodeEscaped();
+ break;
+ case 2: // 2-byte UTF
+ _skipUtf8_2();
+ break;
+ case 3: // 3-byte UTF
+ _skipUtf8_3();
+ break;
+ case 4: // 4-byte UTF
+ _skipUtf8_4();
+ break;
+ default:
+ if (c < INT_SPACE) {
+ _throwUnquotedSpace(c, "string value");
+ } else {
+ // Is this good enough error message?
+ _reportInvalidChar(c);
+ }
+ }
+ }
+ }
+
+ /**
+ * Method for handling cases where first non-space character
+ * of an expected value token is not legal for standard JSON content.
+ */
+ protected JsonToken _handleUnexpectedValue(int c)
+ throws IOException
+ {
+ // Most likely an error, unless we are to allow single-quote-strings
+ switch (c) {
+ case ']':
+ if (!_parsingContext.inArray()) {
+ break;
+ }
+ // fall through
+ case ',':
+ /* !!! TODO: 08-May-2016, tatu: To support `Feature.ALLOW_MISSING_VALUES` would
+ * need handling here...
+ */
+ if (isEnabled(Feature.ALLOW_MISSING_VALUES)) {
+// _inputPtr--;
+ _nextByte = c;
+ return JsonToken.VALUE_NULL;
+ }
+ // fall through
+ case '}':
+ // Error: neither is valid at this point; valid closers have
+ // been handled earlier
+ _reportUnexpectedChar(c, "expected a value");
+ case '\'':
+ if (isEnabled(Feature.ALLOW_SINGLE_QUOTES)) {
+ return _handleApos();
+ }
+ break;
+ case 'N':
+ _matchToken("NaN", 1);
+ if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
+ return resetAsNaN("NaN", Double.NaN);
+ }
+ _reportError("Non-standard token 'NaN': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
+ break;
+ case 'I':
+ _matchToken("Infinity", 1);
+ if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
+ return resetAsNaN("Infinity", Double.POSITIVE_INFINITY);
+ }
+ _reportError("Non-standard token 'Infinity': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
+ break;
+ case '+': // note: '-' is taken as number
+ return _handleInvalidNumberStart(_inputData.readUnsignedByte(), false);
+ }
+ // [core#77] Try to decode most likely token
+ if (Character.isJavaIdentifierStart(c)) {
+ _reportInvalidToken(c, ""+((char) c), "('true', 'false' or 'null')");
+ }
+ // but if it doesn't look like a token:
+ _reportUnexpectedChar(c, "expected a valid value (number, String, array, object, 'true', 'false' or 'null')");
+ return null;
+ }
+
+ protected JsonToken _handleApos() throws IOException
+ {
+ int c = 0;
+ // Otherwise almost verbatim copy of _finishString()
+ int outPtr = 0;
+ char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
+
+ // Here we do want to do full decoding, hence:
+ final int[] codes = _icUTF8;
+
+ main_loop:
+ while (true) {
+ // Then the tight ascii non-funny-char loop:
+ ascii_loop:
+ while (true) {
+ int outEnd = outBuf.length;
+ if (outPtr >= outBuf.length) {
+ outBuf = _textBuffer.finishCurrentSegment();
+ outPtr = 0;
+ outEnd = outBuf.length;
+ }
+ do {
+ c = _inputData.readUnsignedByte();
+ if (c == '\'') {
+ break main_loop;
+ }
+ if (codes[c] != 0) {
+ break ascii_loop;
+ }
+ outBuf[outPtr++] = (char) c;
+ } while (outPtr < outEnd);
+ }
+ switch (codes[c]) {
+ case 1: // backslash
+ c = _decodeEscaped();
+ break;
+ case 2: // 2-byte UTF
+ c = _decodeUtf8_2(c);
+ break;
+ case 3: // 3-byte UTF
+ c = _decodeUtf8_3(c);
+ break;
+ case 4: // 4-byte UTF
+ c = _decodeUtf8_4(c);
+ // Let's add first part right away:
+ outBuf[outPtr++] = (char) (0xD800 | (c >> 10));
+ if (outPtr >= outBuf.length) {
+ outBuf = _textBuffer.finishCurrentSegment();
+ outPtr = 0;
+ }
+ c = 0xDC00 | (c & 0x3FF);
+ // And let the other char output down below
+ break;
+ default:
+ if (c < INT_SPACE) {
+ _throwUnquotedSpace(c, "string value");
+ }
+ // Is this good enough error message?
+ _reportInvalidChar(c);
+ }
+ // Need more room?
+ if (outPtr >= outBuf.length) {
+ outBuf = _textBuffer.finishCurrentSegment();
+ outPtr = 0;
+ }
+ // Ok, let's add char to output:
+ outBuf[outPtr++] = (char) c;
+ }
+ _textBuffer.setCurrentLength(outPtr);
+
+ return JsonToken.VALUE_STRING;
+ }
+
+ /**
+ * Method called if expected numeric value (due to leading sign) does not
+ * look like a number
+ */
+ protected JsonToken _handleInvalidNumberStart(int ch, boolean neg)
+ throws IOException
+ {
+ while (ch == 'I') {
+ ch = _inputData.readUnsignedByte();
+ String match;
+ if (ch == 'N') {
+ match = neg ? "-INF" :"+INF";
+ } else if (ch == 'n') {
+ match = neg ? "-Infinity" :"+Infinity";
+ } else {
+ break;
+ }
+ _matchToken(match, 3);
+ if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
+ return resetAsNaN(match, neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
+ }
+ _reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
+ }
+ reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value");
+ return null;
+ }
+
+ protected final void _matchToken(String matchStr, int i) throws IOException
+ {
+ final int len = matchStr.length();
+ do {
+ int ch = _inputData.readUnsignedByte();
+ if (ch != matchStr.charAt(i)) {
+ _reportInvalidToken(ch, matchStr.substring(0, i));
+ }
+ } while (++i < len);
+
+ int ch = _inputData.readUnsignedByte();
+ if (ch >= '0' && ch != ']' && ch != '}') { // expected/allowed chars
+ _checkMatchEnd(matchStr, i, ch);
+ }
+ _nextByte = ch;
+ }
+
+ private final void _checkMatchEnd(String matchStr, int i, int ch) throws IOException {
+ // but actually only alphanums are problematic
+ char c = (char) _decodeCharForError(ch);
+ if (Character.isJavaIdentifierPart(c)) {
+ _reportInvalidToken(c, matchStr.substring(0, i));
+ }
+ }
+
+ /*
+ /**********************************************************
+ /* Internal methods, ws skipping, escape/unescape
+ /**********************************************************
+ */
+
+ private final int _skipWS() throws IOException
+ {
+ int i = _nextByte;
+ if (i < 0) {
+ i = _inputData.readUnsignedByte();
+ } 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;
+ }
+ }
+ i = _inputData.readUnsignedByte();
+ }
+ }
+
+ private final int _skipWSComment(int i) throws IOException
+ {
+ while (true) {
+ if (i > INT_SPACE) {
+ if (i == INT_SLASH) {
+ _skipComment();
+ } else if (i == INT_HASH) {
+ if (!_skipYAMLComment()) {
+ return i;
+ }
+ } else {
+ 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;
+ }
+ /*
+ if ((i != INT_SPACE) && (i != INT_LF) && (i != INT_CR)) {
+ _throwInvalidSpace(i);
+ }
+ */
+ }
+ i = _inputData.readUnsignedByte();
+ }
+ }
+
+ private final int _skipColon() throws IOException
+ {
+ int i = _nextByte;
+ if (i < 0) {
+ i = _inputData.readUnsignedByte();
+ } else {
+ _nextByte = -1;
+ }
+ // Fast path: colon with optional single-space/tab before and/or after:
+ if (i == INT_COLON) { // common case, no leading space
+ i = _inputData.readUnsignedByte();
+ if (i > INT_SPACE) { // nor trailing
+ if (i == INT_SLASH || i == INT_HASH) {
+ return _skipColon2(i, true);
+ }
+ return i;
+ }
+ if (i == INT_SPACE || i == INT_TAB) {
+ i = _inputData.readUnsignedByte();
+ if (i > INT_SPACE) {
+ if (i == INT_SLASH || i == INT_HASH) {
+ return _skipColon2(i, true);
+ }
+ return i;
+ }
+ }
+ return _skipColon2(i, true); // true -> skipped colon
+ }
+ if (i == INT_SPACE || i == INT_TAB) {
+ i = _inputData.readUnsignedByte();
+ }
+ if (i == INT_COLON) {
+ i = _inputData.readUnsignedByte();
+ if (i > INT_SPACE) {
+ if (i == INT_SLASH || i == INT_HASH) {
+ return _skipColon2(i, true);
+ }
+ return i;
+ }
+ if (i == INT_SPACE || i == INT_TAB) {
+ i = _inputData.readUnsignedByte();
+ if (i > INT_SPACE) {
+ if (i == INT_SLASH || i == INT_HASH) {
+ return _skipColon2(i, true);
+ }
+ return i;
+ }
+ }
+ return _skipColon2(i, true);
+ }
+ return _skipColon2(i, false);
+ }
+
+ private final int _skipColon2(int i, boolean gotColon) throws IOException
+ {
+ for (;; i = _inputData.readUnsignedByte()) {
+ if (i > INT_SPACE) {
+ if (i == INT_SLASH) {
+ _skipComment();
+ continue;
+ }
+ if (i == INT_HASH) {
+ if (_skipYAMLComment()) {
+ continue;
+ }
+ }
+ if (gotColon) {
+ return i;
+ }
+ if (i != INT_COLON) {
+ _reportUnexpectedChar(i, "was expecting a colon to separate field name and value");
+ }
+ gotColon = true;
+ } 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;
+ }
+ }
+ }
+ }
+
+ private final void _skipComment() throws IOException
+ {
+ if (!isEnabled(Feature.ALLOW_COMMENTS)) {
+ _reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)");
+ }
+ int c = _inputData.readUnsignedByte();
+ if (c == '/') {
+ _skipLine();
+ } else if (c == '*') {
+ _skipCComment();
+ } else {
+ _reportUnexpectedChar(c, "was expecting either '*' or '/' for a comment");
+ }
+ }
+
+ private final void _skipCComment() throws IOException
+ {
+ // Need to be UTF-8 aware here to decode content (for skipping)
+ final int[] codes = CharTypes.getInputCodeComment();
+ int i = _inputData.readUnsignedByte();
+
+ // Ok: need the matching '*/'
+ main_loop:
+ while (true) {
+ int code = codes[i];
+ if (code != 0) {
+ switch (code) {
+ case '*':
+ i = _inputData.readUnsignedByte();
+ if (i == INT_SLASH) {
+ return;
+ }
+ continue main_loop;
+ case INT_LF:
+ case INT_CR:
+ ++_currInputRow;
+ break;
+ case 2: // 2-byte UTF
+ _skipUtf8_2();
+ break;
+ case 3: // 3-byte UTF
+ _skipUtf8_3();
+ break;
+ case 4: // 4-byte UTF
+ _skipUtf8_4();
+ break;
+ default: // e.g. -1
+ // Is this good enough error message?
+ _reportInvalidChar(i);
+ }
+ }
+ i = _inputData.readUnsignedByte();
+ }
+ }
+
+ private final boolean _skipYAMLComment() throws IOException
+ {
+ if (!isEnabled(Feature.ALLOW_YAML_COMMENTS)) {
+ return false;
+ }
+ _skipLine();
+ return true;
+ }
+
+ /**
+ * Method for skipping contents of an input line; usually for CPP
+ * and YAML style comments.
+ */
+ private final void _skipLine() throws IOException
+ {
+ // Ok: need to find EOF or linefeed
+ final int[] codes = CharTypes.getInputCodeComment();
+ while (true) {
+ int i = _inputData.readUnsignedByte();
+ int code = codes[i];
+ if (code != 0) {
+ switch (code) {
+ case INT_LF:
+ case INT_CR:
+ ++_currInputRow;
+ return;
+ case '*': // nop for these comments
+ break;
+ case 2: // 2-byte UTF
+ _skipUtf8_2();
+ break;
+ case 3: // 3-byte UTF
+ _skipUtf8_3();
+ break;
+ case 4: // 4-byte UTF
+ _skipUtf8_4();
+ break;
+ default: // e.g. -1
+ if (code < 0) {
+ // Is this good enough error message?
+ _reportInvalidChar(i);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ protected char _decodeEscaped() throws IOException
+ {
+ int c = _inputData.readUnsignedByte();
+
+ switch (c) {
+ // First, ones that are mapped
+ case 'b':
+ return '\b';
+ case 't':
+ return '\t';
+ case 'n':
+ return '\n';
+ case 'f':
+ return '\f';
+ case 'r':
+ return '\r';
+
+ // And these are to be returned as they are
+ case '"':
+ case '/':
+ case '\\':
+ return (char) c;
+
+ case 'u': // and finally hex-escaped
+ break;
+
+ default:
+ return _handleUnrecognizedCharacterEscape((char) _decodeCharForError(c));
+ }
+
+ // Ok, a hex escape. Need 4 characters
+ int value = 0;
+ for (int i = 0; i < 4; ++i) {
+ int ch = _inputData.readUnsignedByte();
+ int digit = CharTypes.charToHex(ch);
+ if (digit < 0) {
+ _reportUnexpectedChar(ch, "expected a hex-digit for character escape sequence");
+ }
+ value = (value << 4) | digit;
+ }
+ return (char) value;
+ }
+
+ protected int _decodeCharForError(int firstByte) throws IOException
+ {
+ int c = firstByte & 0xFF;
+ if (c > 0x7F) { // if >= 0, is ascii and fine as is
+ int needed;
+
+ // Ok; if we end here, we got multi-byte combination
+ if ((c & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
+ c &= 0x1F;
+ needed = 1;
+ } else if ((c & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
+ c &= 0x0F;
+ needed = 2;
+ } else if ((c & 0xF8) == 0xF0) {
+ // 4 bytes; double-char with surrogates and all...
+ c &= 0x07;
+ needed = 3;
+ } else {
+ _reportInvalidInitial(c & 0xFF);
+ needed = 1; // never gets here
+ }
+
+ int d = _inputData.readUnsignedByte();
+ if ((d & 0xC0) != 0x080) {
+ _reportInvalidOther(d & 0xFF);
+ }
+ c = (c << 6) | (d & 0x3F);
+
+ if (needed > 1) { // needed == 1 means 2 bytes total
+ d = _inputData.readUnsignedByte(); // 3rd byte
+ if ((d & 0xC0) != 0x080) {
+ _reportInvalidOther(d & 0xFF);
+ }
+ c = (c << 6) | (d & 0x3F);
+ if (needed > 2) { // 4 bytes? (need surrogates)
+ d = _inputData.readUnsignedByte();
+ if ((d & 0xC0) != 0x080) {
+ _reportInvalidOther(d & 0xFF);
+ }
+ c = (c << 6) | (d & 0x3F);
+ }
+ }
+ }
+ return c;
+ }
+
+ /*
+ /**********************************************************
+ /* Internal methods,UTF8 decoding
+ /**********************************************************
+ */
+
+ private final int _decodeUtf8_2(int c) throws IOException
+ {
+ int d = _inputData.readUnsignedByte();
+ if ((d & 0xC0) != 0x080) {
+ _reportInvalidOther(d & 0xFF);
+ }
+ return ((c & 0x1F) << 6) | (d & 0x3F);
+ }
+
+ private final int _decodeUtf8_3(int c1) throws IOException
+ {
+ c1 &= 0x0F;
+ int d = _inputData.readUnsignedByte();
+ if ((d & 0xC0) != 0x080) {
+ _reportInvalidOther(d & 0xFF);
+ }
+ int c = (c1 << 6) | (d & 0x3F);
+ d = _inputData.readUnsignedByte();
+ if ((d & 0xC0) != 0x080) {
+ _reportInvalidOther(d & 0xFF);
+ }
+ c = (c << 6) | (d & 0x3F);
+ return c;
+ }
+
+ /**
+ * @return Character value <b>minus 0x10000</c>; this so that caller
+ * can readily expand it to actual surrogates
+ */
+ private final int _decodeUtf8_4(int c) throws IOException
+ {
+ int d = _inputData.readUnsignedByte();
+ if ((d & 0xC0) != 0x080) {
+ _reportInvalidOther(d & 0xFF);
+ }
+ c = ((c & 0x07) << 6) | (d & 0x3F);
+ d = _inputData.readUnsignedByte();
+ if ((d & 0xC0) != 0x080) {
+ _reportInvalidOther(d & 0xFF);
+ }
+ c = (c << 6) | (d & 0x3F);
+ d = _inputData.readUnsignedByte();
+ if ((d & 0xC0) != 0x080) {
+ _reportInvalidOther(d & 0xFF);
+ }
+
+ /* note: won't change it to negative here, since caller
+ * already knows it'll need a surrogate
+ */
+ return ((c << 6) | (d & 0x3F)) - 0x10000;
+ }
+
+ private final void _skipUtf8_2() throws IOException
+ {
+ int c = _inputData.readUnsignedByte();
+ if ((c & 0xC0) != 0x080) {
+ _reportInvalidOther(c & 0xFF);
+ }
+ }
+
+ /* Alas, can't heavily optimize skipping, since we still have to
+ * do validity checks...
+ */
+ private final void _skipUtf8_3() throws IOException
+ {
+ //c &= 0x0F;
+ int c = _inputData.readUnsignedByte();
+ if ((c & 0xC0) != 0x080) {
+ _reportInvalidOther(c & 0xFF);
+ }
+ c = _inputData.readUnsignedByte();
+ if ((c & 0xC0) != 0x080) {
+ _reportInvalidOther(c & 0xFF);
+ }
+ }
+
+ private final void _skipUtf8_4() throws IOException
+ {
+ int d = _inputData.readUnsignedByte();
+ if ((d & 0xC0) != 0x080) {
+ _reportInvalidOther(d & 0xFF);
+ }
+ d = _inputData.readUnsignedByte();
+ if ((d & 0xC0) != 0x080) {
+ _reportInvalidOther(d & 0xFF);
+ }
+ d = _inputData.readUnsignedByte();
+ if ((d & 0xC0) != 0x080) {
+ _reportInvalidOther(d & 0xFF);
+ }
+ }
+
+ /*
+ /**********************************************************
+ /* Internal methods, error reporting
+ /**********************************************************
+ */
+
+ protected void _reportInvalidToken(int ch, String matchedPart) throws IOException
+ {
+ _reportInvalidToken(ch, matchedPart, "'null', 'true', 'false' or NaN");
+ }
+
+ protected void _reportInvalidToken(int ch, String matchedPart, String msg)
+ throws IOException
+ {
+ 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) {
+ char c = (char) _decodeCharForError(ch);
+ if (!Character.isJavaIdentifierPart(c)) {
+ break;
+ }
+ sb.append(c);
+ ch = _inputData.readUnsignedByte();
+ }
+ _reportError("Unrecognized token '"+sb.toString()+"': was expecting "+msg);
+ }
+
+ protected void _reportInvalidChar(int c)
+ throws JsonParseException
+ {
+ // Either invalid WS or illegal UTF-8 start char
+ if (c < INT_SPACE) {
+ _throwInvalidSpace(c);
+ }
+ _reportInvalidInitial(c);
+ }
+
+ protected void _reportInvalidInitial(int mask)
+ throws JsonParseException
+ {
+ _reportError("Invalid UTF-8 start byte 0x"+Integer.toHexString(mask));
+ }
+
+ private void _reportInvalidOther(int mask)
+ throws JsonParseException
+ {
+ _reportError("Invalid UTF-8 middle byte 0x"+Integer.toHexString(mask));
+ }
+
+ private static int[] _growArrayBy(int[] arr, int more)
+ {
+ if (arr == null) {
+ return new int[more];
+ }
+ return Arrays.copyOf(arr, arr.length + more);
+ }
+
+ /*
+ /**********************************************************
+ /* Internal methods, binary access
+ /**********************************************************
+ */
+
+ /**
+ * Efficient handling for incremental parsing of base64-encoded
+ * textual content.
+ */
+ @SuppressWarnings("resource")
+ protected final byte[] _decodeBase64(Base64Variant b64variant) throws IOException
+ {
+ ByteArrayBuilder builder = _getByteArrayBuilder();
+
+ //main_loop:
+ while (true) {
+ // first, we'll skip preceding white space, if any
+ int ch;
+ do {
+ ch = _inputData.readUnsignedByte();
+ } while (ch <= INT_SPACE);
+ int bits = b64variant.decodeBase64Char(ch);
+ if (bits < 0) { // reached the end, fair and square?
+ if (ch == INT_QUOTE) {
+ return builder.toByteArray();
+ }
+ bits = _decodeBase64Escape(b64variant, ch, 0);
+ if (bits < 0) { // white space to skip
+ continue;
+ }
+ }
+ int decodedData = bits;
+
+ // then second base64 char; can't get padding yet, nor ws
+ ch = _inputData.readUnsignedByte();
+ bits = b64variant.decodeBase64Char(ch);
+ if (bits < 0) {
+ bits = _decodeBase64Escape(b64variant, ch, 1);
+ }
+ decodedData = (decodedData << 6) | bits;
+ // third base64 char; can be padding, but not ws
+ ch = _inputData.readUnsignedByte();
+ bits = b64variant.decodeBase64Char(ch);
+
+ // First branch: can get padding (-> 1 byte)
+ if (bits < 0) {
+ if (bits != Base64Variant.BASE64_VALUE_PADDING) {
+ // could also just be 'missing' padding
+ if (ch == '"' && !b64variant.usesPadding()) {
+ decodedData >>= 4;
+ builder.append(decodedData);
+ return builder.toByteArray();
+ }
+ bits = _decodeBase64Escape(b64variant, ch, 2);
+ }
+ if (bits == Base64Variant.BASE64_VALUE_PADDING) {
+ ch = _inputData.readUnsignedByte();
+ if (!b64variant.usesPaddingChar(ch)) {
+ throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
+ }
+ // Got 12 bits, only need 8, need to shift
+ decodedData >>= 4;
+ builder.append(decodedData);
+ continue;
+ }
+ }
+ // Nope, 2 or 3 bytes
+ decodedData = (decodedData << 6) | bits;
+ // fourth and last base64 char; can be padding, but not ws
+ ch = _inputData.readUnsignedByte();
+ bits = b64variant.decodeBase64Char(ch);
+ if (bits < 0) {
+ if (bits != Base64Variant.BASE64_VALUE_PADDING) {
+ // could also just be 'missing' padding
+ if (ch == '"' && !b64variant.usesPadding()) {
+ decodedData >>= 2;
+ builder.appendTwoBytes(decodedData);
+ return builder.toByteArray();
+ }
+ bits = _decodeBase64Escape(b64variant, ch, 3);
+ }
+ if (bits == Base64Variant.BASE64_VALUE_PADDING) {
+ /* With padding we only get 2 bytes; but we have
+ * to shift it a bit so it is identical to triplet
+ * case with partial output.
+ * 3 chars gives 3x6 == 18 bits, of which 2 are
+ * dummies, need to discard:
+ */
+ decodedData >>= 2;
+ builder.appendTwoBytes(decodedData);
+ continue;
+ }
+ }
+ // otherwise, our triplet is now complete
+ decodedData = (decodedData << 6) | bits;
+ builder.appendThreeBytes(decodedData);
+ }
+ }
+
+ /*
+ /**********************************************************
+ /* Improved location updating (refactored in 2.7)
+ /**********************************************************
+ */
+
+ @Override
+ public JsonLocation getTokenLocation() {
+ final Object src = _ioContext.getSourceReference();
+ return new JsonLocation(src,
+ -1L, -1L, _tokenInputRow, -1);
+ }
+
+ @Override
+ public JsonLocation getCurrentLocation() {
+ final Object src = _ioContext.getSourceReference();
+ return new JsonLocation(src,
+ -1L, -1L, _currInputRow, -1);
+ }
+
+ /*
+ /**********************************************************
+ /* Internal methods, other
+ /**********************************************************
+ */
+
+ /**
+ * Helper method needed to fix [Issue#148], masking of 0x00 character
+ */
+ private final static int pad(int q, int bytes) {
+ return (bytes == 4) ? q : (q | (-1 << (bytes << 3)));
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java
index 5524691..80559d4 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java
@@ -22,7 +22,6 @@
private final static byte BYTE_BACKSLASH = (byte) '\\';
private final static byte BYTE_COMMA = (byte) ',';
private final static byte BYTE_COLON = (byte) ':';
- private final static byte BYTE_QUOTE = (byte) '"';
// intermediate copies only made up to certain length...
private final static int MAX_BYTES_TO_BUFFER = 512;
@@ -35,7 +34,7 @@
/*
/**********************************************************
- /* Output buffering
+ /* Configuration
/**********************************************************
*/
@@ -45,6 +44,20 @@
final protected OutputStream _outputStream;
/**
+ * Character used for quoting JSON Object property names
+ * and String values.
+ *
+ * @since 2.8
+ */
+ protected byte _quoteChar = '"'; // TODO: make configurable
+
+ /*
+ /**********************************************************
+ /* Output buffering
+ /**********************************************************
+ */
+
+ /**
* Intermediate buffer in which contents are buffered before
* being written using {@link #_outputStream}.
*/
@@ -193,7 +206,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
// But as one segment, or multiple?
if (len <= _outputMaxContiguous) {
if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
@@ -207,7 +220,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -234,7 +247,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
int len = name.appendQuotedUTF8(_outputBuffer, _outputTail);
if (len < 0) { // couldn't append, bit longer processing
_writeBytes(name.asQuotedUTF8());
@@ -244,7 +257,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
private final void _writeUnq(SerializableString name) throws IOException {
@@ -281,7 +294,7 @@
public final void writeEndArray() throws IOException
{
if (!_writeContext.inArray()) {
- _reportError("Current context not an ARRAY but "+_writeContext.getTypeDesc());
+ _reportError("Current context not Array but "+_writeContext.typeDesc());
}
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount());
@@ -309,11 +322,30 @@
}
}
+ @Override // since 2.8
+ public void writeStartObject(Object forValue) throws IOException
+ {
+ _verifyValueWrite("start an object");
+ JsonWriteContext ctxt = _writeContext.createChildObjectContext();
+ _writeContext = ctxt;
+ if (forValue != null) {
+ ctxt.setCurrentValue(forValue);
+ }
+ if (_cfgPrettyPrinter != null) {
+ _cfgPrettyPrinter.writeStartObject(this);
+ } else {
+ if (_outputTail >= _outputEnd) {
+ _flushBuffer();
+ }
+ _outputBuffer[_outputTail++] = '{';
+ }
+ }
+
@Override
public final void writeEndObject() throws IOException
{
if (!_writeContext.inObject()) {
- _reportError("Current context not an object but "+_writeContext.getTypeDesc());
+ _reportError("Current context not Object but "+_writeContext.typeDesc());
}
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount());
@@ -353,7 +385,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
name.getChars(0, len, _charBuffer, 0);
// But as one segment, or multiple?
if (len <= _outputMaxContiguous) {
@@ -367,7 +399,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
protected final void _writePPFieldName(SerializableString name) throws IOException
@@ -387,14 +419,14 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
_writeBytes(name.asQuotedUTF8());
if (addQuotes) {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
}
@@ -421,12 +453,12 @@
if ((_outputTail + len) >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
_writeStringSegment(text, 0, len); // we checked space already above
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -436,7 +468,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
// One or multiple segments?
if (len <= _outputMaxContiguous) {
if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
@@ -450,7 +482,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -460,7 +492,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
int len = text.appendQuotedUTF8(_outputBuffer, _outputTail);
if (len < 0) {
_writeBytes(text.asQuotedUTF8());
@@ -470,7 +502,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -480,12 +512,12 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
_writeBytes(text, offset, length);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -495,7 +527,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
// One or multiple segments?
if (len <= _outputMaxContiguous) {
_writeUTF8Segment(text, offset, len);
@@ -505,7 +537,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
/*
@@ -733,13 +765,13 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
_writeBinary(b64variant, data, offset, offset+len);
// and closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -752,7 +784,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
byte[] encodingBuffer = _ioContext.allocBase64Buffer();
int bytes;
try {
@@ -772,7 +804,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
return bytes;
}
@@ -801,9 +833,9 @@
if ((_outputTail + 8) >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
_outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail);
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -826,9 +858,9 @@
if ((_outputTail + 13) >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
_outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -851,9 +883,9 @@
if ((_outputTail + 23) >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
_outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -929,12 +961,12 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
writeRaw(value);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -966,67 +998,39 @@
@Override
protected final void _verifyValueWrite(String typeMsg) throws IOException
{
- int status = _writeContext.writeValue();
- if (status == JsonWriteContext.STATUS_EXPECT_NAME) {
- _reportError("Can not "+typeMsg+", expecting field name");
- }
- if (_cfgPrettyPrinter == null) {
- byte b;
- switch (status) {
- case JsonWriteContext.STATUS_OK_AFTER_COMMA:
- b = BYTE_COMMA;
- break;
- case JsonWriteContext.STATUS_OK_AFTER_COLON:
- b = BYTE_COLON;
- break;
- case JsonWriteContext.STATUS_OK_AFTER_SPACE: // root-value separator
- if (_rootValueSeparator != null) {
- byte[] raw = _rootValueSeparator.asUnquotedUTF8();
- if (raw.length > 0) {
- _writeBytes(raw);
- }
- }
- return;
- case JsonWriteContext.STATUS_OK_AS_IS:
- default:
- return;
- }
- if (_outputTail >= _outputEnd) {
- _flushBuffer();
- }
- _outputBuffer[_outputTail] = b;
- ++_outputTail;
+ final int status = _writeContext.writeValue();
+ if (_cfgPrettyPrinter != null) {
+ // Otherwise, pretty printer knows what to do...
+ _verifyPrettyValueWrite(typeMsg, status);
return;
}
- // Otherwise, pretty printer knows what to do...
- _verifyPrettyValueWrite(typeMsg, status);
- }
-
- protected final void _verifyPrettyValueWrite(String typeMsg, int status) throws IOException
- {
- // If we have a pretty printer, it knows what to do:
+ byte b;
switch (status) {
- case JsonWriteContext.STATUS_OK_AFTER_COMMA: // array
- _cfgPrettyPrinter.writeArrayValueSeparator(this);
+ case JsonWriteContext.STATUS_OK_AS_IS:
+ default:
+ return;
+ case JsonWriteContext.STATUS_OK_AFTER_COMMA:
+ b = BYTE_COMMA;
break;
case JsonWriteContext.STATUS_OK_AFTER_COLON:
- _cfgPrettyPrinter.writeObjectFieldValueSeparator(this);
+ b = BYTE_COLON;
break;
- case JsonWriteContext.STATUS_OK_AFTER_SPACE:
- _cfgPrettyPrinter.writeRootValueSeparator(this);
- break;
- case JsonWriteContext.STATUS_OK_AS_IS:
- // First entry, but of which context?
- if (_writeContext.inArray()) {
- _cfgPrettyPrinter.beforeArrayValues(this);
- } else if (_writeContext.inObject()) {
- _cfgPrettyPrinter.beforeObjectEntries(this);
+ case JsonWriteContext.STATUS_OK_AFTER_SPACE: // root-value separator
+ if (_rootValueSeparator != null) {
+ byte[] raw = _rootValueSeparator.asUnquotedUTF8();
+ if (raw.length > 0) {
+ _writeBytes(raw);
+ }
}
- break;
- default:
- _throwInternal();
- break;
+ return;
+ case JsonWriteContext.STATUS_EXPECT_NAME:
+ _reportCantWriteValueExpectName(typeMsg);
+ return;
}
+ if (_outputTail >= _outputEnd) {
+ _flushBuffer();
+ }
+ _outputBuffer[_outputTail++] = b;
}
/*
@@ -1158,7 +1162,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
int left = text.length();
@@ -1178,7 +1182,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = BYTE_QUOTE;
+ _outputBuffer[_outputTail++] = _quoteChar;
}
}
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 3ad528c..83e6309 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java
@@ -15,8 +15,6 @@
/**
* This is a concrete implementation of {@link JsonParser}, which is
* based on a {@link java.io.InputStream} as the input source.
- *<p>
- * Note: non-final since version 2.3.
*/
public class UTF8StreamJsonParser
extends ParserBase
@@ -185,8 +183,7 @@
/**********************************************************
*/
- @Override
- protected final boolean loadMore() throws IOException
+ protected final boolean _loadMore() throws IOException
{
final int bufSize = _inputEnd;
@@ -322,6 +319,33 @@
return _getText2(_currToken);
}
+ @Override // since 2.8
+ public int getText(Writer writer) throws IOException
+ {
+ JsonToken t = _currToken;
+ if (t == JsonToken.VALUE_STRING) {
+ if (_tokenIncomplete) {
+ _tokenIncomplete = false;
+ _finishString(); // only strings can be incomplete
+ }
+ return _textBuffer.contentsToWriter(writer);
+ }
+ if (t == JsonToken.FIELD_NAME) {
+ String n = _parsingContext.getCurrentName();
+ writer.write(n);
+ return n.length();
+ }
+ if (t != null) {
+ if (t.isNumeric()) {
+ return _textBuffer.contentsToWriter(writer);
+ }
+ char[] ch = t.asCharArray();
+ writer.write(ch);
+ return ch.length;
+ }
+ return 0;
+ }
+
// // // Let's override default impls for improved performance
// @since 2.1
@@ -563,7 +587,7 @@
int ch;
do {
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = (int) _inputBuffer[_inputPtr++] & 0xFF;
} while (ch <= INT_SPACE);
@@ -590,7 +614,7 @@
// then second base64 char; can't get padding yet, nor ws
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
bits = b64variant.decodeBase64Char(ch);
@@ -601,7 +625,7 @@
// third base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
bits = b64variant.decodeBase64Char(ch);
@@ -620,7 +644,7 @@
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
// Ok, must get padding
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
if (!b64variant.usesPaddingChar(ch)) {
@@ -636,7 +660,7 @@
decodedData = (decodedData << 6) | bits;
// fourth and last base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
bits = b64variant.decodeBase64Char(ch);
@@ -734,7 +758,7 @@
// Nope: do we then expect a comma?
if (_parsingContext.expectComma()) {
if (i != INT_COMMA) {
- _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.getTypeDesc()+" entries");
+ _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
i = _skipWS();
}
@@ -870,7 +894,15 @@
}
return (_currToken = t);
}
-
+
+ @Override
+ public void finishToken() throws IOException {
+ if (_tokenIncomplete) {
+ _tokenIncomplete = false;
+ _finishString(); // only strings can be incomplete
+ }
+ }
+
/*
/**********************************************************
/* Public API, traversal, nextXxxValue/nextFieldName
@@ -920,7 +952,7 @@
// Nope: do we then expect a comma?
if (_parsingContext.expectComma()) {
if (i != INT_COMMA) {
- _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.getTypeDesc()+" entries");
+ _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
i = _skipWS();
}
@@ -1007,7 +1039,7 @@
// Nope: do we then expect a comma?
if (_parsingContext.expectComma()) {
if (i != INT_COMMA) {
- _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.getTypeDesc()+" entries");
+ _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
i = _skipWS();
}
@@ -1406,7 +1438,7 @@
outBuf[outPtr++] = '-';
// Must have something after sign too
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
int c = (int) _inputBuffer[_inputPtr++] & 0xFF;
// Note: must be followed by a digit
@@ -1467,7 +1499,7 @@
{
// Ok, parse the rest
while (true) {
- if (_inputPtr >= _inputEnd && !loadMore()) {
+ if (_inputPtr >= _inputEnd && !_loadMore()) {
_textBuffer.setCurrentLength(outPtr);
return resetInt(negative, intPartLength);
}
@@ -1504,7 +1536,7 @@
private final int _verifyNoLeadingZeroes() throws IOException
{
// Ok to have plain "0"
- if (_inputPtr >= _inputEnd && !loadMore()) {
+ if (_inputPtr >= _inputEnd && !_loadMore()) {
return INT_0;
}
int ch = _inputBuffer[_inputPtr] & 0xFF;
@@ -1519,7 +1551,7 @@
// if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number)
++_inputPtr; // Leading zero to be skipped
if (ch == INT_0) {
- while (_inputPtr < _inputEnd || loadMore()) {
+ while (_inputPtr < _inputEnd || _loadMore()) {
ch = _inputBuffer[_inputPtr] & 0xFF;
if (ch < INT_0 || ch > INT_9) { // followed by non-number; retain one zero
return INT_0;
@@ -1545,7 +1577,7 @@
fract_loop:
while (true) {
- if (_inputPtr >= _inputEnd && !loadMore()) {
+ if (_inputPtr >= _inputEnd && !_loadMore()) {
eof = true;
break fract_loop;
}
@@ -1575,7 +1607,7 @@
outBuf[outPtr++] = (char) c;
// Not optional, can require that we get one more char
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
c = (int) _inputBuffer[_inputPtr++] & 0xFF;
// Sign indicator?
@@ -1587,7 +1619,7 @@
outBuf[outPtr++] = (char) c;
// Likewise, non optional:
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
c = (int) _inputBuffer[_inputPtr++] & 0xFF;
}
@@ -1600,7 +1632,7 @@
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
- if (_inputPtr >= _inputEnd && !loadMore()) {
+ if (_inputPtr >= _inputEnd && !_loadMore()) {
eof = true;
break exp_loop;
}
@@ -1874,8 +1906,8 @@
protected String slowParseName() throws IOException
{
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
- _reportInvalidEOF(": was expecting closing '\"' for name");
+ if (!_loadMore()) {
+ _reportInvalidEOF(": was expecting closing '\"' for name", JsonToken.FIELD_NAME);
}
}
int i = _inputBuffer[_inputPtr++] & 0xFF;
@@ -1905,7 +1937,7 @@
* an escape sequence is detected (or alternatively for long
* names, one crossing input buffer boundary).
* Needs to be able to handle more exceptional cases, gets slower,
- * and hance is offlined to a separate method.
+ * and hence is offlined to a separate method.
*/
protected final String parseEscapedName(int[] quads, int qlen, int currQuad, int ch,
int currQuadBytes) throws IOException
@@ -1930,7 +1962,7 @@
ch = _decodeEscaped();
}
/* Oh crap. May need to UTF-8 (re-)encode it, if it's
- * beyond 7-bit ascii. Gets pretty messy.
+ * beyond 7-bit ASCII. Gets pretty messy.
* If this happens often, may want to use different name
* canonicalization to avoid these hits.
*/
@@ -1980,8 +2012,8 @@
currQuadBytes = 1;
}
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
- _reportInvalidEOF(" in field name");
+ if (!_loadMore()) {
+ _reportInvalidEOF(" in field name", JsonToken.FIELD_NAME);
}
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
@@ -2050,8 +2082,8 @@
currQuadBytes = 1;
}
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
- _reportInvalidEOF(" in field name");
+ if (!_loadMore()) {
+ _reportInvalidEOF(" in field name", JsonToken.FIELD_NAME);
}
}
ch = _inputBuffer[_inputPtr] & 0xFF;
@@ -2082,8 +2114,8 @@
protected String _parseAposName() throws IOException
{
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
- _reportInvalidEOF(": was expecting closing '\'' for name");
+ if (!_loadMore()) {
+ _reportInvalidEOF(": was expecting closing '\'' for field name", JsonToken.FIELD_NAME);
}
}
int ch = _inputBuffer[_inputPtr++] & 0xFF;
@@ -2164,8 +2196,8 @@
currQuadBytes = 1;
}
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
- _reportInvalidEOF(" in field name");
+ if (!_loadMore()) {
+ _reportInvalidEOF(" in field name", JsonToken.FIELD_NAME);
}
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
@@ -2300,7 +2332,7 @@
needed = ch = 1; // never really gets this far
}
if ((ix + needed) > byteLen) {
- _reportInvalidEOF(" in field name");
+ _reportInvalidEOF(" in field name", JsonToken.FIELD_NAME);
}
// Ok, always need at least one more:
@@ -2364,13 +2396,17 @@
/**********************************************************
*/
+ protected void _loadMoreGuaranteed() throws IOException {
+ if (!_loadMore()) { _reportInvalidEOF(); }
+ }
+
@Override
protected void _finishString() throws IOException
{
// First, single tight loop for ASCII content, not split across input buffer boundary:
int ptr = _inputPtr;
if (ptr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
ptr = _inputPtr;
}
int outPtr = 0;
@@ -2404,7 +2440,7 @@
// First, single tight loop for ASCII content, not split across input buffer boundary:
int ptr = _inputPtr;
if (ptr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
ptr = _inputPtr;
}
int outPtr = 0;
@@ -2446,7 +2482,7 @@
while (true) {
int ptr = _inputPtr;
if (ptr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
ptr = _inputPtr;
}
if (outPtr >= outBuf.length) {
@@ -2536,7 +2572,7 @@
int ptr = _inputPtr;
int max = _inputEnd;
if (ptr >= max) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
ptr = _inputPtr;
max = _inputEnd;
}
@@ -2587,7 +2623,29 @@
{
// Most likely an error, unless we are to allow single-quote-strings
switch (c) {
+ /*
+ * This check proceeds only if the Feature.ALLOW_MISSING_VALUES is enabled
+ * The Check is for missing values. Incase of missing values in an array, the next token will be either ',' or ']'.
+ * This case, decrements the already incremented _inputPtr in the buffer in case of comma(,)
+ * so that the existing flow goes back to checking the next token which will be comma again and
+ * it continues the parsing.
+ * Also the case returns NULL as current token in case of ',' or ']'.
+ */
case ']':
+ if (!_parsingContext.inArray()) {
+ break;
+ }
+ // fall through
+ case ',':
+ /* 28-Mar-2016: [core#116]: If Feature.ALLOW_MISSING_VALUES is enabled
+ * we may allow "missing values", that is, encountering a trailing
+ * comma or closing marker where value would be expected
+ */
+ if (isEnabled(Feature.ALLOW_MISSING_VALUES)) {
+ _inputPtr--;
+ return JsonToken.VALUE_NULL;
+ }
+ // fall through
case '}':
// Error: neither is valid at this point; valid closers have
// been handled earlier
@@ -2613,13 +2671,13 @@
break;
case '+': // note: '-' is taken as number
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
- _reportInvalidEOFInValue();
+ if (!_loadMore()) {
+ _reportInvalidEOFInValue(JsonToken.VALUE_NUMBER_INT);
}
}
return _handleInvalidNumberStart(_inputBuffer[_inputPtr++] & 0xFF, false);
}
- // [Issue#77] Try to decode most likely token
+ // [core#77] Try to decode most likely token
if (Character.isJavaIdentifierStart(c)) {
_reportInvalidToken(""+((char) c), "('true', 'false' or 'null')");
}
@@ -2646,7 +2704,7 @@
ascii_loop:
while (true) {
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
@@ -2727,8 +2785,8 @@
{
while (ch == 'I') {
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
- _reportInvalidEOFInValue();
+ if (!_loadMore()) {
+ _reportInvalidEOFInValue(JsonToken.VALUE_NUMBER_FLOAT); // possibly?
}
}
ch = _inputBuffer[_inputPtr++];
@@ -2774,7 +2832,7 @@
{
final int len = matchStr.length();
do {
- if (((_inputPtr >= _inputEnd) && !loadMore())
+ if (((_inputPtr >= _inputEnd) && !_loadMore())
|| (_inputBuffer[_inputPtr] != matchStr.charAt(i))) {
_reportInvalidToken(matchStr.substring(0, i));
}
@@ -2782,7 +2840,7 @@
} while (++i < len);
// but let's also ensure we either get EOF, or non-alphanum char...
- if (_inputPtr >= _inputEnd && !loadMore()) {
+ if (_inputPtr >= _inputEnd && !_loadMore()) {
return;
}
int ch = _inputBuffer[_inputPtr] & 0xFF;
@@ -2832,7 +2890,7 @@
private final int _skipWS2() throws IOException
{
- while (_inputPtr < _inputEnd || loadMore()) {
+ while (_inputPtr < _inputEnd || _loadMore()) {
int i = _inputBuffer[_inputPtr++] & 0xFF;
if (i > INT_SPACE) {
if (i == INT_SLASH) {
@@ -2857,7 +2915,7 @@
}
}
}
- throw _constructError("Unexpected end-of-input within/between "+_parsingContext.getTypeDesc()+" entries");
+ throw _constructError("Unexpected end-of-input within/between "+_parsingContext.typeDesc()+" entries");
}
private final int _skipWSOrEnd() throws IOException
@@ -2865,7 +2923,7 @@
// Let's handle first character separately since it is likely that
// it is either non-whitespace; or we have longer run of white space
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
+ if (!_loadMore()) {
return _eofAsNextChar();
}
}
@@ -2913,7 +2971,7 @@
private final int _skipWSOrEnd2() throws IOException
{
- while ((_inputPtr < _inputEnd) || loadMore()) {
+ while ((_inputPtr < _inputEnd) || _loadMore()) {
int i = _inputBuffer[_inputPtr++] & 0xFF;
if (i > INT_SPACE) {
if (i == INT_SLASH) {
@@ -2998,7 +3056,7 @@
private final int _skipColon2(boolean gotColon) throws IOException
{
- while (_inputPtr < _inputEnd || loadMore()) {
+ while (_inputPtr < _inputEnd || _loadMore()) {
int i = _inputBuffer[_inputPtr++] & 0xFF;
if (i > INT_SPACE) {
@@ -3029,7 +3087,9 @@
}
}
}
- throw _constructError("Unexpected end-of-input within/between "+_parsingContext.getTypeDesc()+" entries");
+ _reportInvalidEOF(" within/between "+_parsingContext.typeDesc()+" entries",
+ null);
+ return -1;
}
private final void _skipComment() throws IOException
@@ -3038,8 +3098,8 @@
_reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)");
}
// First: check which comment (if either) it is:
- if (_inputPtr >= _inputEnd && !loadMore()) {
- _reportInvalidEOF(" in a comment");
+ if (_inputPtr >= _inputEnd && !_loadMore()) {
+ _reportInvalidEOF(" in a comment", null);
}
int c = _inputBuffer[_inputPtr++] & 0xFF;
if (c == '/') {
@@ -3058,13 +3118,13 @@
// Ok: need the matching '*/'
main_loop:
- while ((_inputPtr < _inputEnd) || loadMore()) {
+ while ((_inputPtr < _inputEnd) || _loadMore()) {
int i = (int) _inputBuffer[_inputPtr++] & 0xFF;
int code = codes[i];
if (code != 0) {
switch (code) {
case '*':
- if (_inputPtr >= _inputEnd && !loadMore()) {
+ if (_inputPtr >= _inputEnd && !_loadMore()) {
break main_loop;
}
if (_inputBuffer[_inputPtr] == INT_SLASH) {
@@ -3094,7 +3154,7 @@
}
}
}
- _reportInvalidEOF(" in a comment");
+ _reportInvalidEOF(" in a comment", null);
}
private final boolean _skipYAMLComment() throws IOException
@@ -3114,7 +3174,7 @@
{
// Ok: need to find EOF or linefeed
final int[] codes = CharTypes.getInputCodeComment();
- while ((_inputPtr < _inputEnd) || loadMore()) {
+ while ((_inputPtr < _inputEnd) || _loadMore()) {
int i = (int) _inputBuffer[_inputPtr++] & 0xFF;
int code = codes[i];
if (code != 0) {
@@ -3151,8 +3211,8 @@
protected char _decodeEscaped() throws IOException
{
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
- _reportInvalidEOF(" in character escape sequence");
+ if (!_loadMore()) {
+ _reportInvalidEOF(" in character escape sequence", JsonToken.VALUE_STRING);
}
}
int c = (int) _inputBuffer[_inputPtr++];
@@ -3187,8 +3247,8 @@
int value = 0;
for (int i = 0; i < 4; ++i) {
if (_inputPtr >= _inputEnd) {
- if (!loadMore()) {
- _reportInvalidEOF(" in character escape sequence");
+ if (!_loadMore()) {
+ _reportInvalidEOF(" in character escape sequence", JsonToken.VALUE_STRING);
}
}
int ch = (int) _inputBuffer[_inputPtr++];
@@ -3256,7 +3316,7 @@
private final int _decodeUtf8_2(int c) throws IOException
{
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
@@ -3268,7 +3328,7 @@
private final int _decodeUtf8_3(int c1) throws IOException
{
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
c1 &= 0x0F;
int d = (int) _inputBuffer[_inputPtr++];
@@ -3277,7 +3337,7 @@
}
int c = (c1 << 6) | (d & 0x3F);
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
@@ -3310,7 +3370,7 @@
private final int _decodeUtf8_4(int c) throws IOException
{
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
@@ -3319,7 +3379,7 @@
c = ((c & 0x07) << 6) | (d & 0x3F);
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
@@ -3327,7 +3387,7 @@
}
c = (c << 6) | (d & 0x3F);
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
@@ -3343,7 +3403,7 @@
private final void _skipUtf8_2() throws IOException
{
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
int c = (int) _inputBuffer[_inputPtr++];
if ((c & 0xC0) != 0x080) {
@@ -3357,7 +3417,7 @@
private final void _skipUtf8_3() throws IOException
{
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
//c &= 0x0F;
int c = (int) _inputBuffer[_inputPtr++];
@@ -3365,7 +3425,7 @@
_reportInvalidOther(c & 0xFF, _inputPtr);
}
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
c = (int) _inputBuffer[_inputPtr++];
if ((c & 0xC0) != 0x080) {
@@ -3376,21 +3436,21 @@
private final void _skipUtf8_4(int c) throws IOException
{
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
@@ -3410,7 +3470,7 @@
*/
protected final void _skipCR() throws IOException
{
- if (_inputPtr < _inputEnd || loadMore()) {
+ if (_inputPtr < _inputEnd || _loadMore()) {
if (_inputBuffer[_inputPtr] == BYTE_LF) {
++_inputPtr;
}
@@ -3422,7 +3482,7 @@
private int nextByte() throws IOException
{
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
return _inputBuffer[_inputPtr++] & 0xFF;
}
@@ -3447,7 +3507,7 @@
* nothing fancy here (nor fast).
*/
while (true) {
- if (_inputPtr >= _inputEnd && !loadMore()) {
+ if (_inputPtr >= _inputEnd && !_loadMore()) {
break;
}
int i = (int) _inputBuffer[_inputPtr++];
@@ -3518,7 +3578,7 @@
int ch;
do {
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = (int) _inputBuffer[_inputPtr++] & 0xFF;
} while (ch <= INT_SPACE);
@@ -3537,7 +3597,7 @@
// then second base64 char; can't get padding yet, nor ws
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
bits = b64variant.decodeBase64Char(ch);
@@ -3548,7 +3608,7 @@
// third base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
bits = b64variant.decodeBase64Char(ch);
@@ -3567,7 +3627,7 @@
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
// Ok, must get padding
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
if (!b64variant.usesPaddingChar(ch)) {
@@ -3583,7 +3643,7 @@
decodedData = (decodedData << 6) | bits;
// fourth and last base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
- loadMoreGuaranteed();
+ _loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
bits = b64variant.decodeBase64Char(ch);
diff --git a/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java
index 2340ea2..180497a 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java
@@ -20,13 +20,27 @@
/*
/**********************************************************
- /* Output buffering
+ /* Configuration
/**********************************************************
*/
final protected Writer _writer;
/**
+ * Character used for quoting JSON Object property names
+ * and String values.
+ *
+ * @since 2.8
+ */
+ protected char _quoteChar = '"'; // TODO: make configurable
+
+ /*
+ /**********************************************************
+ /* Output buffering
+ /**********************************************************
+ */
+
+ /**
* Intermediate buffer in which contents are buffered before
* being written using {@link #_writer}.
*/
@@ -60,8 +74,7 @@
* internally to hold a reference to currently used escape
*/
protected SerializableString _currentEscape;
-
-
+
/*
/**********************************************************
/* Life-cycle
@@ -79,7 +92,7 @@
/*
/**********************************************************
- /* Overridden configuration methods
+ /* Overridden configuration, introspection methods
/**********************************************************
*/
@@ -95,6 +108,10 @@
return Math.max(0, len);
}
+ // json does allow this so
+ @Override
+ public boolean canWriteFormattedNumbers() { return true; }
+
/*
/**********************************************************
/* Overridden methods
@@ -141,16 +158,16 @@
return;
}
// we know there's room for at least one more char
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
// The beef:
_writeString(name);
// and closing quotes; need room for one more char:
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
}
-
+
protected void _writeFieldName(SerializableString name, boolean commaBefore) throws IOException
{
if (_cfgPrettyPrinter != null) {
@@ -171,7 +188,7 @@
return;
}
// we know there's room for at least one more char
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
// The beef:
final int qlen = quoted.length;
if ((_outputTail + qlen + 1) >= _outputEnd) {
@@ -180,11 +197,11 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
} else {
System.arraycopy(quoted, 0, _outputBuffer, _outputTail, qlen);
_outputTail += qlen;
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
}
}
@@ -195,7 +212,7 @@
*/
@Override
- public void writeStartArray() throws IOException, JsonGenerationException
+ public void writeStartArray() throws IOException
{
_verifyValueWrite("start an array");
_writeContext = _writeContext.createChildArrayContext();
@@ -210,10 +227,10 @@
}
@Override
- public void writeEndArray() throws IOException, JsonGenerationException
+ public void writeEndArray() throws IOException
{
if (!_writeContext.inArray()) {
- _reportError("Current context not an ARRAY but "+_writeContext.getTypeDesc());
+ _reportError("Current context not Array but "+_writeContext.typeDesc());
}
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount());
@@ -226,8 +243,27 @@
_writeContext = _writeContext.clearAndGetParent();
}
+ @Override // since 2.8
+ public void writeStartObject(Object forValue) throws IOException
+ {
+ _verifyValueWrite("start an object");
+ JsonWriteContext ctxt = _writeContext.createChildObjectContext();
+ _writeContext = ctxt;
+ if (forValue != null) {
+ ctxt.setCurrentValue(forValue);
+ }
+ if (_cfgPrettyPrinter != null) {
+ _cfgPrettyPrinter.writeStartObject(this);
+ } else {
+ if (_outputTail >= _outputEnd) {
+ _flushBuffer();
+ }
+ _outputBuffer[_outputTail++] = '{';
+ }
+ }
+
@Override
- public void writeStartObject() throws IOException, JsonGenerationException
+ public void writeStartObject() throws IOException
{
_verifyValueWrite("start an object");
_writeContext = _writeContext.createChildObjectContext();
@@ -242,10 +278,10 @@
}
@Override
- public void writeEndObject() throws IOException, JsonGenerationException
+ public void writeEndObject() throws IOException
{
if (!_writeContext.inObject()) {
- _reportError("Current context not an object but "+_writeContext.getTypeDesc());
+ _reportError("Current context not Object but "+_writeContext.typeDesc());
}
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount());
@@ -276,12 +312,12 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
_writeString(name);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
}
}
@@ -300,12 +336,12 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
writeRaw(quoted, 0, quoted.length);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
}
}
@@ -326,13 +362,13 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
_writeString(text);
// And finally, closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -342,13 +378,13 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
_writeString(text, offset, len);
// And finally, closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -358,7 +394,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
// Note: copied from writeRaw:
char[] text = sstr.asQuotedChars();
final int len = text.length;
@@ -378,7 +414,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -511,13 +547,13 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
_writeBinary(b64variant, data, offset, offset+len);
// and closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -530,7 +566,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
byte[] encodingBuffer = _ioContext.allocBase64Buffer();
int bytes;
try {
@@ -550,7 +586,7 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
return bytes;
}
@@ -579,9 +615,9 @@
if ((_outputTail + 8) >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
_outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail);
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -603,9 +639,9 @@
if ((_outputTail + 13) >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
_outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
}
@Override
@@ -627,9 +663,9 @@
if ((_outputTail + 23) >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
_outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
}
// !!! 05-Aug-2008, tatus: Any ways to optimize these?
@@ -706,14 +742,14 @@
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
writeRaw(value);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail++] = '"';
+ _outputBuffer[_outputTail++] = _quoteChar;
}
-
+
@Override
public void writeBoolean(boolean state) throws IOException
{
@@ -753,17 +789,17 @@
@Override
protected void _verifyValueWrite(String typeMsg) throws IOException
{
+ final int status = _writeContext.writeValue();
if (_cfgPrettyPrinter != null) {
// Otherwise, pretty printer knows what to do...
- _verifyPrettyValueWrite(typeMsg);
+ _verifyPrettyValueWrite(typeMsg, status);
return;
}
char c;
- final int status = _writeContext.writeValue();
- if (status == JsonWriteContext.STATUS_EXPECT_NAME) {
- _reportError("Can not "+typeMsg+", expecting field name");
- }
switch (status) {
+ case JsonWriteContext.STATUS_OK_AS_IS:
+ default:
+ return;
case JsonWriteContext.STATUS_OK_AFTER_COMMA:
c = ',';
break;
@@ -775,47 +811,14 @@
writeRaw(_rootValueSeparator.getValue());
}
return;
- case JsonWriteContext.STATUS_OK_AS_IS:
- default:
+ case JsonWriteContext.STATUS_EXPECT_NAME:
+ _reportCantWriteValueExpectName(typeMsg);
return;
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
- _outputBuffer[_outputTail] = c;
- ++_outputTail;
- }
-
- protected void _verifyPrettyValueWrite(String typeMsg) throws IOException
- {
- final int status = _writeContext.writeValue();
- if (status == JsonWriteContext.STATUS_EXPECT_NAME) {
- _reportError("Can not "+typeMsg+", expecting field name");
- }
-
- // If we have a pretty printer, it knows what to do:
- switch (status) {
- case JsonWriteContext.STATUS_OK_AFTER_COMMA: // array
- _cfgPrettyPrinter.writeArrayValueSeparator(this);
- break;
- case JsonWriteContext.STATUS_OK_AFTER_COLON:
- _cfgPrettyPrinter.writeObjectFieldValueSeparator(this);
- break;
- case JsonWriteContext.STATUS_OK_AFTER_SPACE:
- _cfgPrettyPrinter.writeRootValueSeparator(this);
- break;
- case JsonWriteContext.STATUS_OK_AS_IS:
- // First entry, but of which context?
- if (_writeContext.inArray()) {
- _cfgPrettyPrinter.beforeArrayValues(this);
- } else if (_writeContext.inObject()) {
- _cfgPrettyPrinter.beforeObjectEntries(this);
- }
- break;
- default:
- _throwInternal();
- break;
- }
+ _outputBuffer[_outputTail++] = c;
}
/*
diff --git a/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer.java b/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer.java
index 3532b7e..b3d3ffb 100644
--- a/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer.java
+++ b/src/main/java/com/fasterxml/jackson/core/sym/ByteQuadsCanonicalizer.java
@@ -36,7 +36,7 @@
/**
* No point in trying to construct tiny tables, just need to resize soon.
*/
- final static int MIN_HASH_SIZE = 16;
+ private final static int MIN_HASH_SIZE = 16;
/**
* Let's only share reasonably sized symbol tables. Max size set to 3/4 of 8k;
@@ -56,7 +56,7 @@
* Reference to the root symbol table, for child tables, so
* that they can merge table information back as necessary.
*/
- final protected ByteQuadsCanonicalizer _parent;
+ final private ByteQuadsCanonicalizer _parent;
/**
* Member that is only used by the root table instance: root
@@ -64,7 +64,7 @@
* may return new state if they add entries to the table.
* Child tables do NOT use the reference.
*/
- final protected AtomicReference<TableInfo> _tableInfo;
+ final private AtomicReference<TableInfo> _tableInfo;
/**
* Seed value we use as the base to make hash codes non-static between
@@ -88,7 +88,7 @@
* NOTE: non-final to allow disabling intern()ing in case of excessive
* collisions.
*/
- protected boolean _intern;
+ private boolean _intern;
/**
* Flag that indicates whether we should throw an exception if enough
@@ -96,7 +96,7 @@
*
* @since 2.4
*/
- protected final boolean _failOnDoS;
+ private final boolean _failOnDoS;
/*
/**********************************************************
@@ -110,7 +110,7 @@
* structure (details of which may be tweaked depending on expected rates
* of collisions).
*/
- protected int[] _hashArea;
+ private int[] _hashArea;
/**
* Number of slots for primary entries within {@link #_hashArea}; which is
@@ -118,17 +118,17 @@
* primary covers only half of the area; plus, additional area for longer
* symbols after hash area).
*/
- protected int _hashSize;
+ private int _hashSize;
/**
* Offset within {@link #_hashArea} where secondary entries start
*/
- protected int _secondaryStart;
+ private int _secondaryStart;
/**
* Offset within {@link #_hashArea} where tertiary entries start
*/
- protected int _tertiaryStart;
+ private int _tertiaryStart;
/**
* Constant that determines size of buckets for tertiary entries:
@@ -139,12 +139,12 @@
* Default value is 2, for buckets of 4 slots; grows bigger with
* bigger table sizes.
*/
- protected int _tertiaryShift;
+ private int _tertiaryShift;
/**
* Total number of Strings in the symbol table; only used for child tables.
*/
- protected int _count;
+ private int _count;
/**
* Array that contains <code>String</code> instances matching
@@ -152,7 +152,7 @@
* Contains nulls for unused entries. Note that this size is twice
* that of {@link #_hashArea}
*/
- protected String[] _names;
+ private String[] _names;
/*
/**********************************************************
@@ -165,7 +165,7 @@
* for more spilled over entries (if any).
* Spill over area is within fixed-size portion of {@link #_hashArea}.
*/
- protected int _spilloverEnd;
+ private int _spilloverEnd;
/**
* Offset within {@link #_hashArea} that follows main slots and contains
@@ -175,7 +175,7 @@
* Note that long name area follows immediately after the fixed-size
* main hash area ({@link #_hashArea}).
*/
- protected int _longNameOffset;
+ private int _longNameOffset;
/**
* This flag is set if, after adding a new entry, it is deemed
diff --git a/src/main/java/com/fasterxml/jackson/core/sym/CharsToNameCanonicalizer.java b/src/main/java/com/fasterxml/jackson/core/sym/CharsToNameCanonicalizer.java
index a668f2e..1d50226 100644
--- a/src/main/java/com/fasterxml/jackson/core/sym/CharsToNameCanonicalizer.java
+++ b/src/main/java/com/fasterxml/jackson/core/sym/CharsToNameCanonicalizer.java
@@ -58,21 +58,21 @@
* reuse factories it doesn't matter either way; but when
* recreating factories often, initial overhead may dominate.
*/
- protected static final int DEFAULT_T_SIZE = 64;
+ private static final int DEFAULT_T_SIZE = 64;
/**
* Let's not expand symbol tables past some maximum size;
* this should protected against OOMEs caused by large documents
* with unique (~= random) names.
*/
- protected static final int MAX_T_SIZE = 0x10000; // 64k entries == 256k mem
+ private static final int MAX_T_SIZE = 0x10000; // 64k entries == 256k mem
/**
* Let's only share reasonably sized symbol tables. Max size set to 3/4 of 16k;
* this corresponds to 64k main hash index. This should allow for enough distinct
* names for almost any case.
*/
- final static int MAX_ENTRIES_FOR_REUSE = 12000;
+ static final int MAX_ENTRIES_FOR_REUSE = 12000;
/**
* Also: to thwart attacks based on hash collisions (which may or may not
@@ -88,8 +88,8 @@
*
* @since 2.1
*/
- final static int MAX_COLL_CHAIN_LENGTH = 100;
-
+ static final int MAX_COLL_CHAIN_LENGTH = 100;
+
final static CharsToNameCanonicalizer sBootstrapSymbolTable = new CharsToNameCanonicalizer();
/*
@@ -104,7 +104,7 @@
* defined, and child instance is released (call to <code>release</code>),
* parent's shared tables may be updated from the child instance.
*/
- protected CharsToNameCanonicalizer _parent;
+ private CharsToNameCanonicalizer _parent;
/**
* Seed value we use as the base to make hash codes non-static between
@@ -117,13 +117,13 @@
*/
final private int _hashSeed;
- final protected int _flags;
+ final private int _flags;
/**
* Whether any canonicalization should be attempted (whether using
* intern or not)
*/
- protected boolean _canonicalize;
+ private boolean _canonicalize;
/*
/**********************************************************
@@ -135,7 +135,7 @@
* Primary matching symbols; it's expected most match occur from
* here.
*/
- protected String[] _symbols;
+ private String[] _symbols;
/**
* Overflow buckets; if primary doesn't match, lookup is done
@@ -144,27 +144,27 @@
* Note: Number of buckets is half of number of symbol entries, on
* assumption there's less need for buckets.
*/
- protected Bucket[] _buckets;
+ private Bucket[] _buckets;
/**
* Current size (number of entries); needed to know if and when
* rehash.
*/
- protected int _size;
+ private int _size;
/**
* Limit that indicates maximum size this instance can hold before
* it needs to be expanded and rehashed. Calculated using fill
* factor passed in to constructor.
*/
- protected int _sizeThreshold;
+ private int _sizeThreshold;
/**
* Mask used to get index from hash values; equal to
* <code>_buckets.length - 1</code>, when _buckets.length is
* a power of two.
*/
- protected int _indexMask;
+ private int _indexMask;
/**
* We need to keep track of the longest collision list; this is needed
@@ -173,8 +173,8 @@
*
* @since 2.1
*/
- protected int _longestCollisionList;
-
+ private int _longestCollisionList;
+
/*
/**********************************************************
/* State regarding shared arrays
@@ -187,7 +187,7 @@
* (first) change is made, and potentially if updated bucket list
* is to be resync'ed back to master instance.
*/
- protected boolean _dirty;
+ private boolean _dirty;
/*
/**********************************************************
@@ -203,8 +203,8 @@
*
* @since 2.4
*/
- protected BitSet _overflows;
-
+ private BitSet _overflows;
+
/*
/**********************************************************
/* Life-cycle
diff --git a/src/main/java/com/fasterxml/jackson/core/util/ByteArrayBuilder.java b/src/main/java/com/fasterxml/jackson/core/util/ByteArrayBuilder.java
index 30a2622..cfbe77a 100644
--- a/src/main/java/com/fasterxml/jackson/core/util/ByteArrayBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/core/util/ByteArrayBuilder.java
@@ -42,7 +42,7 @@
private int _pastLen;
private byte[] _currBlock;
private int _currBlockPtr;
-
+
public ByteArrayBuilder() { this(null); }
public ByteArrayBuilder(BufferRecycler br) { this(br, INITIAL_BLOCK_SIZE); }
public ByteArrayBuilder(int firstBlockSize) { this(null, firstBlockSize); }
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 f8c31ca..6641151 100644
--- a/src/main/java/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java
+++ b/src/main/java/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java
@@ -200,16 +200,43 @@
@Override
public void writeStartObject() throws IOException { delegate.writeStartObject(); }
-
+
+ @Override
+ public void writeStartObject(Object forValue) throws IOException { delegate.writeStartObject(forValue); }
+
@Override
public void writeEndObject() throws IOException { delegate.writeEndObject(); }
@Override
- public void writeFieldName(String name) throws IOException { delegate.writeFieldName(name); }
+ public void writeFieldName(String name) throws IOException {
+ delegate.writeFieldName(name);
+ }
@Override
- public void writeFieldName(SerializableString name) throws IOException { delegate.writeFieldName(name); }
-
+ public void writeFieldName(SerializableString name) throws IOException {
+ delegate.writeFieldName(name);
+ }
+
+ @Override
+ public void writeFieldId(long id) throws IOException {
+ delegate.writeFieldId(id);
+ }
+
+ @Override
+ public void writeArray(int[] array, int offset, int length) throws IOException {
+ delegate.writeArray(array, offset, length);
+ }
+
+ @Override
+ public void writeArray(long[] array, int offset, int length) throws IOException {
+ delegate.writeArray(array, offset, length);
+ }
+
+ @Override
+ public void writeArray(double[] array, int offset, int length) throws IOException {
+ delegate.writeArray(array, offset, length);
+ }
+
/*
/**********************************************************
/* Public API, write methods, text/String values
@@ -326,6 +353,9 @@
@Override
public void writeTypeId(Object id) throws IOException { delegate.writeTypeId(id); }
+
+ @Override
+ public void writeEmbeddedObject(Object object) throws IOException { delegate.writeEmbeddedObject(object); }
/*
/**********************************************************
diff --git a/src/main/java/com/fasterxml/jackson/core/util/JsonParserDelegate.java b/src/main/java/com/fasterxml/jackson/core/util/JsonParserDelegate.java
index c96d239..9ec93fb 100644
--- a/src/main/java/com/fasterxml/jackson/core/util/JsonParserDelegate.java
+++ b/src/main/java/com/fasterxml/jackson/core/util/JsonParserDelegate.java
@@ -2,6 +2,7 @@
import java.io.IOException;
import java.io.OutputStream;
+import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -106,7 +107,10 @@
/* Public API, token accessors
/**********************************************************
*/
-
+
+ @Override public JsonToken currentToken() { return delegate.currentToken(); }
+ @Override public int currentTokenId() { return delegate.currentTokenId(); }
+
@Override public JsonToken getCurrentToken() { return delegate.getCurrentToken(); }
@Override public int getCurrentTokenId() { return delegate.getCurrentTokenId(); }
@Override public boolean hasCurrentToken() { return delegate.hasCurrentToken(); }
@@ -140,6 +144,7 @@
@Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
@Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
@Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
+ @Override public int getText(Writer writer) throws IOException, UnsupportedOperationException { return delegate.getText(writer); }
/*
/**********************************************************
@@ -209,6 +214,8 @@
@Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
@Override public JsonToken nextToken() throws IOException { return delegate.nextToken(); }
@Override public JsonToken nextValue() throws IOException { return delegate.nextValue(); }
+
+ @Override public void finishToken() throws IOException { delegate.finishToken(); }
@Override
public JsonParser skipChildren() throws IOException {
diff --git a/src/main/java/com/fasterxml/jackson/core/util/JsonParserSequence.java b/src/main/java/com/fasterxml/jackson/core/util/JsonParserSequence.java
index 1100d61..5b75047 100644
--- a/src/main/java/com/fasterxml/jackson/core/util/JsonParserSequence.java
+++ b/src/main/java/com/fasterxml/jackson/core/util/JsonParserSequence.java
@@ -20,23 +20,56 @@
* as delegate)
*/
protected final JsonParser[] _parsers;
-
+
+ /**
+ * Configuration that determines whether state of parsers is first verified
+ * to see if parser already points to a token (that is,
+ * {@link JsonParser#hasCurrentToken()} returns <code>true</code>), and if so
+ * that token is first return before {@link JsonParser#nextToken} is called.
+ * If enabled, this check is made; if disabled, no check is made and
+ * {@link JsonParser#nextToken} is always called for all parsers.
+ *<p>
+ * Default setting is <code>false</code> (for backwards-compatibility)
+ * so that possible existing token is not considered for parsers.
+ *
+ * @since 2.8
+ */
+ protected final boolean _checkForExistingToken;
+
/**
* Index of the next parser in {@link #_parsers}.
*/
- protected int _nextParser;
-
+ protected int _nextParserIndex;
+
+ /**
+ * Flag used to indicate that `JsonParser.nextToken()` should not be called,
+ * due to parser already pointing to a token.
+ *
+ * @since 2.8
+ */
+ protected boolean _hasToken;
+
/*
*******************************************************
* Construction
*******************************************************
*/
- protected JsonParserSequence(JsonParser[] parsers)
+ @Deprecated // since 2.8
+ protected JsonParserSequence(JsonParser[] parsers) {
+ this(false, parsers);
+ }
+
+ /**
+ * @since 2.8
+ */
+ protected JsonParserSequence(boolean checkForExistingToken, JsonParser[] parsers)
{
super(parsers[0]);
+ _checkForExistingToken = checkForExistingToken;
+ _hasToken = checkForExistingToken && delegate.hasCurrentToken();
_parsers = parsers;
- _nextParser = 1;
+ _nextParserIndex = 1;
}
/**
@@ -48,11 +81,12 @@
* within sequences. This is done to minimize delegation depth,
* ideally only having just a single level of delegation.
*/
- public static JsonParserSequence createFlattened(JsonParser first, JsonParser second)
+ public static JsonParserSequence createFlattened(boolean checkForExistingToken,
+ JsonParser first, JsonParser second)
{
if (!(first instanceof JsonParserSequence || second instanceof JsonParserSequence)) {
- // simple:
- return new JsonParserSequence(new JsonParser[] { first, second });
+ return new JsonParserSequence(checkForExistingToken,
+ new JsonParser[] { first, second });
}
ArrayList<JsonParser> p = new ArrayList<JsonParser>();
if (first instanceof JsonParserSequence) {
@@ -65,44 +99,59 @@
} else {
p.add(second);
}
- return new JsonParserSequence(p.toArray(new JsonParser[p.size()]));
+ return new JsonParserSequence(checkForExistingToken,
+ p.toArray(new JsonParser[p.size()]));
}
+ /**
+ * @deprecated Since 2.8 use {@link #createFlattened(boolean, JsonParser, JsonParser)}
+ * instead
+ */
+ @Deprecated // since 2.8
+ public static JsonParserSequence createFlattened(JsonParser first, JsonParser second) {
+ return createFlattened(false, first, second);
+ }
+
@SuppressWarnings("resource")
- protected void addFlattenedActiveParsers(List<JsonParser> result)
+ protected void addFlattenedActiveParsers(List<JsonParser> listToAddIn)
{
- for (int i = _nextParser-1, len = _parsers.length; i < len; ++i) {
+ for (int i = _nextParserIndex-1, len = _parsers.length; i < len; ++i) {
JsonParser p = _parsers[i];
if (p instanceof JsonParserSequence) {
- ((JsonParserSequence) p).addFlattenedActiveParsers(result);
+ ((JsonParserSequence) p).addFlattenedActiveParsers(listToAddIn);
} else {
- result.add(p);
+ listToAddIn.add(p);
}
}
}
-
+
/*
- *******************************************************
- * Overridden methods, needed: cases where default
- * delegation does not work
- *******************************************************
+ /*******************************************************
+ /* Overridden methods, needed: cases where default
+ /* delegation does not work
+ /*******************************************************
*/
-
+
@Override
public void close() throws IOException {
do { delegate.close(); } while (switchToNext());
}
@Override
- public JsonToken nextToken() throws IOException, JsonParseException
+ public JsonToken nextToken() throws IOException
{
- JsonToken t = delegate.nextToken();
- if (t != null) return t;
- while (switchToNext()) {
- t = delegate.nextToken();
- if (t != null) return t;
+ if (delegate == null) {
+ return null;
}
- return null;
+ if (_hasToken) {
+ _hasToken = false;
+ return delegate.currentToken();
+ }
+ JsonToken t = delegate.nextToken();
+ if (t == null) {
+ return switchAndReturnNext();
+ }
+ return t;
}
/*
@@ -119,7 +168,7 @@
public int containedParsersCount() {
return _parsers.length;
}
-
+
/*
/*******************************************************
/* Helper methods
@@ -127,19 +176,35 @@
*/
/**
- * Method that will switch active parser from the current one
- * to next parser in sequence, if there is another parser left,
- * making this the new delegate. Old delegate is returned if
- * switch succeeds.
+ * Method that will switch active delegate parser from the current one
+ * to the next parser in sequence, if there is another parser left:
+ * if so, the next parser will become the active delegate parser.
*
* @return True if switch succeeded; false otherwise
+ *
+ * @since 2.8
*/
protected boolean switchToNext()
{
- if (_nextParser >= _parsers.length) {
- return false;
+ if (_nextParserIndex < _parsers.length) {
+ delegate = _parsers[_nextParserIndex++];
+ return true;
}
- delegate = _parsers[_nextParser++];
- return true;
+ return false;
+ }
+
+ protected JsonToken switchAndReturnNext() throws IOException
+ {
+ while (_nextParserIndex < _parsers.length) {
+ delegate = _parsers[_nextParserIndex++];
+ if (_checkForExistingToken && delegate.hasCurrentToken()) {
+ return delegate.getCurrentToken();
+ }
+ JsonToken t = delegate.nextToken();
+ if (t != null) {
+ return t;
+ }
+ }
+ return null;
}
}
diff --git a/src/main/java/com/fasterxml/jackson/core/util/RequestPayload.java b/src/main/java/com/fasterxml/jackson/core/util/RequestPayload.java
new file mode 100644
index 0000000..a7e7e85
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/util/RequestPayload.java
@@ -0,0 +1,67 @@
+package com.fasterxml.jackson.core.util;
+
+import java.io.IOException;
+
+/**
+ * Container object used to contain optional information on content
+ * being parsed, passed to {@link com.fasterxml.jackson.core.JsonParseException} in case of
+ * exception being thrown; this may be useful for caller to display
+ * information on failure.
+ *
+ * @since 2.8
+ */
+public class RequestPayload
+ implements java.io.Serializable // just in case, even though likely included as transient
+{
+ private static final long serialVersionUID = 1L;
+
+ // request payload as byte[]
+ protected byte[] _payloadAsBytes;
+
+ // request payload as String
+ protected CharSequence _payloadAsText;
+
+ // Charset if the request payload is set in bytes
+ protected String _charset;
+
+ public RequestPayload(byte[] bytes, String charset) {
+ if (bytes == null) {
+ throw new IllegalArgumentException();
+ }
+ _payloadAsBytes = bytes;
+ _charset = (charset == null || charset.isEmpty()) ? "UTF-8" : charset;
+ }
+
+ public RequestPayload(CharSequence str) {
+ if (str == null) {
+ throw new IllegalArgumentException();
+ }
+ _payloadAsText = str;
+ }
+
+ /**
+ * Returns the raw request payload object i.e, either byte[] or String
+ *
+ * @return Object which is a raw request payload i.e, either byte[] or
+ * String
+ */
+ public Object getRawPayload() {
+ if (_payloadAsBytes != null) {
+ return _payloadAsBytes;
+ }
+
+ return _payloadAsText;
+ }
+
+ @Override
+ public String toString() {
+ if (_payloadAsBytes != null) {
+ try {
+ return new String(_payloadAsBytes, _charset);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return _payloadAsText.toString();
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java b/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java
index 85250b1..c32ebf3 100644
--- a/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java
+++ b/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java
@@ -1,8 +1,8 @@
package com.fasterxml.jackson.core.util;
+import java.io.*;
import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.*;
import com.fasterxml.jackson.core.io.NumberInput;
@@ -399,6 +399,45 @@
return NumberInput.parseDouble(contentsAsString());
}
+ /**
+ * @since 2.8
+ */
+ public int contentsToWriter(Writer w) throws IOException
+ {
+ if (_resultArray != null) {
+ w.write(_resultArray);
+ return _resultArray.length;
+ }
+ if (_resultString != null) { // Can take a shortcut...
+ w.write(_resultString);
+ return _resultString.length();
+ }
+ // Do we use shared array?
+ if (_inputStart >= 0) {
+ final int len = _inputLen;
+ if (len > 0) {
+ w.write(_inputBuffer, _inputStart, len);
+ }
+ return len;
+ }
+ // nope, not shared
+ int total = 0;
+ if (_segments != null) {
+ for (int i = 0, end = _segments.size(); i < end; ++i) {
+ char[] curr = _segments.get(i);
+ int currLen = curr.length;
+ w.write(curr, 0, currLen);
+ total += currLen;
+ }
+ }
+ int len = _currentSize;
+ if (len > 0) {
+ w.write(_currentSegment, 0, len);
+ total += len;
+ }
+ return total;
+ }
+
/*
/**********************************************************
/* Public mutators:
diff --git a/src/test/java/com/fasterxml/jackson/core/BaseTest.java b/src/test/java/com/fasterxml/jackson/core/BaseTest.java
index 5184162..ca8ce4f 100644
--- a/src/test/java/com/fasterxml/jackson/core/BaseTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/BaseTest.java
@@ -3,12 +3,45 @@
import java.io.*;
import java.util.Arrays;
+import com.fasterxml.jackson.core.testsupport.MockDataInput;
+import com.fasterxml.jackson.core.testsupport.ThrottledInputStream;
+
import junit.framework.TestCase;
+@SuppressWarnings("resource")
public abstract class BaseTest
extends TestCase
{
protected final static String FIELD_BASENAME = "f";
+
+ protected final static int MODE_INPUT_STREAM = 0;
+ protected final static int MODE_INPUT_STREAM_THROTTLED = 1;
+ protected final static int MODE_READER = 2;
+ protected final static int MODE_DATA_INPUT = 3;
+
+ protected final static int[] ALL_MODES = new int[] {
+ MODE_INPUT_STREAM,
+ MODE_INPUT_STREAM_THROTTLED,
+ MODE_READER,
+ MODE_DATA_INPUT
+ };
+
+ protected final static int[] ALL_BINARY_MODES = new int[] {
+ MODE_INPUT_STREAM,
+ MODE_INPUT_STREAM_THROTTLED,
+ MODE_DATA_INPUT
+ };
+
+ protected final static int[] ALL_TEXT_MODES = new int[] {
+ MODE_READER
+ };
+
+ // DataInput not streaming
+ protected final static int[] ALL_STREAMING_MODES = new int[] {
+ MODE_INPUT_STREAM,
+ MODE_INPUT_STREAM_THROTTLED,
+ MODE_READER
+ };
/*
/**********************************************************
@@ -125,116 +158,118 @@
return true;
}
}
-
+
+ protected final JsonFactory JSON_FACTORY = new JsonFactory();
+
/*
/**********************************************************
/* High-level helpers
/**********************************************************
*/
- protected void verifyJsonSpecSampleDoc(JsonParser jp, boolean verifyContents)
+ protected void verifyJsonSpecSampleDoc(JsonParser p, boolean verifyContents)
throws IOException
{
- verifyJsonSpecSampleDoc(jp, verifyContents, true);
+ verifyJsonSpecSampleDoc(p, verifyContents, true);
}
- protected void verifyJsonSpecSampleDoc(JsonParser jp, boolean verifyContents,
+ protected void verifyJsonSpecSampleDoc(JsonParser p, boolean verifyContents,
boolean requireNumbers)
throws IOException
{
- if (!jp.hasCurrentToken()) {
- jp.nextToken();
+ if (!p.hasCurrentToken()) {
+ p.nextToken();
}
- assertToken(JsonToken.START_OBJECT, jp.getCurrentToken()); // main object
+ assertToken(JsonToken.START_OBJECT, p.currentToken()); // main object
- assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Image'
+ assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Image'
if (verifyContents) {
- verifyFieldName(jp, "Image");
+ verifyFieldName(p, "Image");
}
- assertToken(JsonToken.START_OBJECT, jp.nextToken()); // 'image' object
+ assertToken(JsonToken.START_OBJECT, p.nextToken()); // 'image' object
- assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Width'
+ assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Width'
if (verifyContents) {
- verifyFieldName(jp, "Width");
+ verifyFieldName(p, "Width");
}
- verifyIntToken(jp.nextToken(), requireNumbers);
+ verifyIntToken(p.nextToken(), requireNumbers);
if (verifyContents) {
- verifyIntValue(jp, SAMPLE_SPEC_VALUE_WIDTH);
+ verifyIntValue(p, SAMPLE_SPEC_VALUE_WIDTH);
}
- assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Height'
+ assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Height'
if (verifyContents) {
- verifyFieldName(jp, "Height");
+ verifyFieldName(p, "Height");
}
- verifyIntToken(jp.nextToken(), requireNumbers);
+ verifyIntToken(p.nextToken(), requireNumbers);
if (verifyContents) {
- verifyIntValue(jp, SAMPLE_SPEC_VALUE_HEIGHT);
+ verifyIntValue(p, SAMPLE_SPEC_VALUE_HEIGHT);
}
- assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Title'
+ assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Title'
if (verifyContents) {
- verifyFieldName(jp, "Title");
+ verifyFieldName(p, "Title");
}
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals(SAMPLE_SPEC_VALUE_TITLE, getAndVerifyText(jp));
- assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Thumbnail'
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals(SAMPLE_SPEC_VALUE_TITLE, getAndVerifyText(p));
+ assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Thumbnail'
if (verifyContents) {
- verifyFieldName(jp, "Thumbnail");
+ verifyFieldName(p, "Thumbnail");
}
- assertToken(JsonToken.START_OBJECT, jp.nextToken()); // 'thumbnail' object
- assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Url'
+ assertToken(JsonToken.START_OBJECT, p.nextToken()); // 'thumbnail' object
+ assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Url'
if (verifyContents) {
- verifyFieldName(jp, "Url");
+ verifyFieldName(p, "Url");
}
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
if (verifyContents) {
- assertEquals(SAMPLE_SPEC_VALUE_TN_URL, getAndVerifyText(jp));
+ assertEquals(SAMPLE_SPEC_VALUE_TN_URL, getAndVerifyText(p));
}
- assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Height'
+ assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Height'
if (verifyContents) {
- verifyFieldName(jp, "Height");
+ verifyFieldName(p, "Height");
}
- verifyIntToken(jp.nextToken(), requireNumbers);
+ verifyIntToken(p.nextToken(), requireNumbers);
if (verifyContents) {
- verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_HEIGHT);
+ verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_HEIGHT);
}
- assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Width'
+ assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Width'
if (verifyContents) {
- verifyFieldName(jp, "Width");
+ verifyFieldName(p, "Width");
}
// Width value is actually a String in the example
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
if (verifyContents) {
- assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, getAndVerifyText(jp));
+ assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, getAndVerifyText(p));
}
- assertToken(JsonToken.END_OBJECT, jp.nextToken()); // 'thumbnail' object
- assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'IDs'
- assertToken(JsonToken.START_ARRAY, jp.nextToken()); // 'ids' array
- verifyIntToken(jp.nextToken(), requireNumbers); // ids[0]
+ assertToken(JsonToken.END_OBJECT, p.nextToken()); // 'thumbnail' object
+ assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'IDs'
+ assertToken(JsonToken.START_ARRAY, p.nextToken()); // 'ids' array
+ verifyIntToken(p.nextToken(), requireNumbers); // ids[0]
if (verifyContents) {
- verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID1);
+ verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_ID1);
}
- verifyIntToken(jp.nextToken(), requireNumbers); // ids[1]
+ verifyIntToken(p.nextToken(), requireNumbers); // ids[1]
if (verifyContents) {
- verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID2);
+ verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_ID2);
}
- verifyIntToken(jp.nextToken(), requireNumbers); // ids[2]
+ verifyIntToken(p.nextToken(), requireNumbers); // ids[2]
if (verifyContents) {
- verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID3);
+ verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_ID3);
}
- verifyIntToken(jp.nextToken(), requireNumbers); // ids[3]
+ verifyIntToken(p.nextToken(), requireNumbers); // ids[3]
if (verifyContents) {
- verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID4);
+ verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_ID4);
}
- assertToken(JsonToken.END_ARRAY, jp.nextToken()); // 'ids' array
+ assertToken(JsonToken.END_ARRAY, p.nextToken()); // 'ids' array
- assertToken(JsonToken.END_OBJECT, jp.nextToken()); // 'image' object
+ assertToken(JsonToken.END_OBJECT, p.nextToken()); // 'image' object
- assertToken(JsonToken.END_OBJECT, jp.nextToken()); // main object
+ assertToken(JsonToken.END_OBJECT, p.nextToken()); // main object
}
private void verifyIntToken(JsonToken t, boolean requireNumbers)
@@ -251,18 +286,18 @@
}
}
- protected void verifyFieldName(JsonParser jp, String expName)
+ protected void verifyFieldName(JsonParser p, String expName)
throws IOException
{
- assertEquals(expName, jp.getText());
- assertEquals(expName, jp.getCurrentName());
+ assertEquals(expName, p.getText());
+ assertEquals(expName, p.getCurrentName());
}
- protected void verifyIntValue(JsonParser jp, long expValue)
+ protected void verifyIntValue(JsonParser p, long expValue)
throws IOException
{
// First, via textual
- assertEquals(String.valueOf(expValue), jp.getText());
+ assertEquals(String.valueOf(expValue), p.getText());
}
/*
@@ -271,27 +306,72 @@
/**********************************************************
*/
- protected JsonParser createParserUsingReader(String input)
- throws IOException, JsonParseException
+ protected JsonParser createParser(int mode, String doc) throws IOException {
+ return createParser(JSON_FACTORY, mode, doc);
+ }
+
+ protected JsonParser createParser(int mode, byte[] doc) throws IOException {
+ return createParser(JSON_FACTORY, mode, doc);
+ }
+
+ protected JsonParser createParser(JsonFactory f, int mode, String doc) throws IOException
+ {
+ switch (mode) {
+ case MODE_INPUT_STREAM:
+ return createParserUsingStream(f, doc, "UTF-8");
+ case MODE_INPUT_STREAM_THROTTLED:
+ {
+ InputStream in = new ThrottledInputStream(doc.getBytes("UTF-8"), 1);
+ return f.createParser(in);
+ }
+ case MODE_READER:
+ return createParserUsingReader(f, doc);
+ case MODE_DATA_INPUT:
+ return createParserForDataInput(f, new MockDataInput(doc));
+ default:
+ }
+ throw new RuntimeException("internal error");
+ }
+
+ protected JsonParser createParser(JsonFactory f, int mode, byte[] doc) throws IOException
+ {
+ switch (mode) {
+ case MODE_INPUT_STREAM:
+ return f.createParser(new ByteArrayInputStream(doc));
+ case MODE_INPUT_STREAM_THROTTLED:
+ {
+ InputStream in = new ThrottledInputStream(doc, 1);
+ return f.createParser(in);
+ }
+ case MODE_READER:
+ return f.createParser(new StringReader(new String(doc, "UTF-8")));
+ case MODE_DATA_INPUT:
+ return createParserForDataInput(f, new MockDataInput(doc));
+ default:
+ }
+ throw new RuntimeException("internal error");
+ }
+
+ protected JsonParser createParserUsingReader(String input) throws IOException
{
return createParserUsingReader(new JsonFactory(), input);
}
protected JsonParser createParserUsingReader(JsonFactory f, String input)
- throws IOException, JsonParseException
+ throws IOException
{
return f.createParser(new StringReader(input));
}
protected JsonParser createParserUsingStream(String input, String encoding)
- throws IOException, JsonParseException
+ throws IOException
{
return createParserUsingStream(new JsonFactory(), input, encoding);
}
protected JsonParser createParserUsingStream(JsonFactory f,
- String input, String encoding)
- throws IOException, JsonParseException
+ String input, String encoding)
+ throws IOException
{
/* 23-Apr-2008, tatus: UTF-32 is not supported by JDK, have to
@@ -309,6 +389,13 @@
return f.createParser(is);
}
+ protected JsonParser createParserForDataInput(JsonFactory f,
+ DataInput input)
+ throws IOException
+ {
+ return f.createParser(input);
+ }
+
/*
/**********************************************************
/* Helper read/write methods
@@ -361,9 +448,9 @@
}
}
- protected void assertToken(JsonToken expToken, JsonParser jp)
+ protected void assertToken(JsonToken expToken, JsonParser p)
{
- assertToken(expToken, jp.getCurrentToken());
+ assertToken(expToken, p.currentToken());
}
protected void assertType(Object ob, Class<?> expType)
@@ -395,16 +482,16 @@
* available methods, and ensures results are consistent, before
* returning them
*/
- protected String getAndVerifyText(JsonParser jp) throws IOException
+ protected String getAndVerifyText(JsonParser p) throws IOException
{
// Ok, let's verify other accessors
- int actLen = jp.getTextLength();
- char[] ch = jp.getTextCharacters();
- String str2 = new String(ch, jp.getTextOffset(), actLen);
- String str = jp.getText();
+ int actLen = p.getTextLength();
+ char[] ch = p.getTextCharacters();
+ String str2 = new String(ch, p.getTextOffset(), actLen);
+ String str = p.getText();
if (str.length() != actLen) {
- fail("Internal problem (jp.token == "+jp.getCurrentToken()+"): jp.getText().length() ['"+str+"'] == "+str.length()+"; jp.getTextLength() == "+actLen);
+ fail("Internal problem (p.token == "+p.currentToken()+"): p.getText().length() ['"+str+"'] == "+str.length()+"; p.getTextLength() == "+actLen);
}
assertEquals("String access via getText(), getTextXxx() must be the same", str, str2);
diff --git a/src/test/java/com/fasterxml/jackson/core/TestExceptions.java b/src/test/java/com/fasterxml/jackson/core/TestExceptions.java
index d5a2ae0..0301b19 100644
--- a/src/test/java/com/fasterxml/jackson/core/TestExceptions.java
+++ b/src/test/java/com/fasterxml/jackson/core/TestExceptions.java
@@ -2,6 +2,8 @@
import java.io.StringWriter;
+import com.fasterxml.jackson.core.io.JsonEOFException;
+
public class TestExceptions extends BaseTest
{
private final JsonFactory JSON_F = new JsonFactory();
@@ -42,4 +44,65 @@
assertEquals("Test!", e.getOriginalMessage());
g.close();
}
+
+ // [core#281]: new eof exception
+ public void testEofExceptionsBytes() throws Exception {
+ _testEofExceptions(MODE_INPUT_STREAM);
+ }
+
+ // [core#281]: new eof exception
+ public void testEofExceptionsChars() throws Exception {
+ _testEofExceptions(MODE_READER);
+ }
+
+ private void _testEofExceptions(int mode) throws Exception
+ {
+ JsonParser p = createParser(mode, "[ ");
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ try {
+ p.nextToken();
+ fail("Should get exception");
+ } catch (JsonEOFException e) {
+ verifyException(e, "close marker for Array");
+ }
+ p.close();
+
+ p = createParser(mode, "{ \"foo\" : [ ] ");
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ try {
+ p.nextToken();
+ fail("Should get exception");
+ } catch (JsonEOFException e) {
+ verifyException(e, "close marker for Object");
+ }
+ p.close();
+
+ p = createParser(mode, "{ \"fo");
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ try {
+ p.nextToken();
+ fail("Should get exception");
+ } catch (JsonEOFException e) {
+
+ verifyException(e, "in field name");
+ assertEquals(JsonToken.FIELD_NAME, e.getTokenBeingDecoded());
+ }
+ p.close();
+
+ p = createParser(mode, "{ \"field\" : ");
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ try {
+ p.nextToken();
+ fail("Should get exception");
+ } catch (JsonEOFException e) {
+ verifyException(e, "unexpected end-of-input");
+ verifyException(e, "Object entries");
+ }
+ p.close();
+
+ // any other cases we'd like to test?
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java b/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java
index ced0384..c03458f 100644
--- a/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java
+++ b/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java
@@ -11,7 +11,7 @@
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
/**
- * Unit tests for [Issue#31] (https://github.com/FasterXML/jackson-core/issues/31)
+ * Unit tests for [core#31] (https://github.com/FasterXML/jackson-core/issues/31)
*/
public class TestJDKSerializability extends BaseTest
{
@@ -128,7 +128,7 @@
objIn.close();
}
}
-
+
@SuppressWarnings("resource")
protected String _copyJson(JsonFactory f, String json, boolean useBytes) throws IOException
{
@@ -144,13 +144,13 @@
return sw.toString();
}
- protected void _copyJson(JsonFactory f, String json, JsonGenerator jg) throws IOException
+ protected void _copyJson(JsonFactory f, String json, JsonGenerator g) throws IOException
{
- JsonParser jp = f.createParser(json);
- while (jp.nextToken() != null) {
- jg.copyCurrentEvent(jp);
+ JsonParser p = f.createParser(json);
+ while (p.nextToken() != null) {
+ g.copyCurrentEvent(p);
}
- jp.close();
- jg.close();
+ p.close();
+ g.close();
}
}
diff --git a/src/test/java/com/fasterxml/jackson/core/base64/TestBase64Generation.java b/src/test/java/com/fasterxml/jackson/core/base64/TestBase64Generation.java
index e947b71..ef3c673 100644
--- a/src/test/java/com/fasterxml/jackson/core/base64/TestBase64Generation.java
+++ b/src/test/java/com/fasterxml/jackson/core/base64/TestBase64Generation.java
@@ -3,37 +3,13 @@
import java.io.*;
import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.testsupport.ThrottledInputStream;
public class TestBase64Generation
extends com.fasterxml.jackson.core.BaseTest
{
- static class ThrottledInputStream extends FilterInputStream
- {
- protected final int _maxBytes;
-
- public ThrottledInputStream(byte[] data, int maxBytes)
- {
- this(new ByteArrayInputStream(data), maxBytes);
- }
-
- public ThrottledInputStream(InputStream in, int maxBytes)
- {
- super(in);
- _maxBytes = maxBytes;
- }
-
- @Override
- public int read(byte[] buf) throws IOException {
- return read(buf, 0, buf.length);
- }
-
- @Override
- public int read(byte[] buf, int offset, int len) throws IOException {
- return in.read(buf, offset, Math.min(_maxBytes, len));
- }
-
- }
+
/*
/**********************************************************
/* Test methods
diff --git a/src/test/java/com/fasterxml/jackson/core/base64/TestJsonParserBinary.java b/src/test/java/com/fasterxml/jackson/core/base64/TestJsonParserBinary.java
deleted file mode 100644
index 23dacc1..0000000
--- a/src/test/java/com/fasterxml/jackson/core/base64/TestJsonParserBinary.java
+++ /dev/null
@@ -1,155 +0,0 @@
-package com.fasterxml.jackson.core.base64;
-
-import java.io.*;
-
-import com.fasterxml.jackson.core.*;
-
-import static org.junit.Assert.*;
-
-/**
- * Tests for verifying that accessing base64 encoded content works ok.
- */
-public class TestJsonParserBinary
- extends com.fasterxml.jackson.core.BaseTest
-{
- /*
- /**********************************************************************
- /* Unit tests
- /**********************************************************************
- */
-
- public void testSimple()
- throws IOException
- {
- // let's test reader (char) based first, then stream (byte)
- _testSimple(false);
- _testSimple(true);
- }
-
- public void testInArray()
- throws IOException
- {
- // let's test reader (char) based first, then stream (byte)
- _testInArray(false);
- _testInArray(true);
- }
-
- public void testWithEscaped() throws IOException
- {
- // let's test reader (char) based first, then stream (byte)
- _testEscaped(false);
- _testEscaped(true);
- }
-
- /*
- /**********************************************************************
- /* Actual test methods
- /**********************************************************************
- */
-
- private void _testSimple(boolean useStream)
- throws IOException
- {
- /* The usual sample input string, from Thomas Hobbes's "Leviathan"
- * (via Wikipedia)
- */
- final String RESULT = "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.";
- final byte[] RESULT_BYTES = RESULT.getBytes("US-ASCII");
-
- // And here's what should produce it...
- final String INPUT_STR =
- "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
-+"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
-+"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu"
-+"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
-+"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="
- ;
-
- final String DOC = "\""+INPUT_STR+"\"";
- JsonParser jp = _getParser(DOC, useStream);
-
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- byte[] data = jp.getBinaryValue();
- assertNotNull(data);
- assertArrayEquals(RESULT_BYTES, data);
- jp.close();
- }
-
- private void _testInArray(boolean useStream)
- throws IOException
- {
- JsonFactory jf = new JsonFactory();
-
- final int entryCount = 7;
-
- StringWriter sw = new StringWriter();
- JsonGenerator jg = jf.createGenerator(sw);
- jg.writeStartArray();
-
- byte[][] entries = new byte[entryCount][];
- for (int i = 0; i < entryCount; ++i) {
- byte[] b = new byte[200 + i * 100];
- for (int x = 0; x < b.length; ++x) {
- b[x] = (byte) (i + x);
- }
- entries[i] = b;
- jg.writeBinary(b);
- }
-
- jg.writeEndArray();
- jg.close();
-
- JsonParser jp = _getParser(sw.toString(), useStream);
-
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
-
- for (int i = 0; i < entryCount; ++i) {
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- byte[] b = jp.getBinaryValue();
- assertArrayEquals(entries[i], b);
- }
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- jp.close();
- }
-
- private void _testEscaped(boolean useStream) throws IOException
- {
- // Input: "Test!" -> "VGVzdCE="
-
- // First, try with embedded linefeed half-way through:
-
- String DOC = quote("VGVz\\ndCE="); // note: must double-quote to get linefeed
- JsonParser jp = _getParser(DOC, useStream);
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- byte[] b = jp.getBinaryValue();
- assertEquals("Test!", new String(b, "US-ASCII"));
- assertNull(jp.nextToken());
- jp.close();
-
- // and then with escaped chars
-// DOC = quote("V\\u0047V\\u007AdCE="); // note: must escape backslash...
- DOC = quote("V\\u0047V\\u007AdCE="); // note: must escape backslash...
- jp = _getParser(DOC, useStream);
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- b = jp.getBinaryValue();
- assertEquals("Test!", new String(b, "US-ASCII"));
- assertNull(jp.nextToken());
- jp.close();
- }
-
- /*
- /**********************************************************************
- /* Other helper methods
- /**********************************************************************
- */
-
- private JsonParser _getParser(String doc, boolean useStream)
- throws IOException
- {
- JsonFactory jf = new JsonFactory();
- if (useStream) {
- return jf.createParser(doc.getBytes("UTF-8"));
- }
- return jf.createParser(new StringReader(doc));
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/io/TestIOContext.java b/src/test/java/com/fasterxml/jackson/core/io/TestIOContext.java
index 3bad591..475abb5 100644
--- a/src/test/java/com/fasterxml/jackson/core/io/TestIOContext.java
+++ b/src/test/java/com/fasterxml/jackson/core/io/TestIOContext.java
@@ -24,7 +24,7 @@
try {
ctxt.releaseReadIOBuffer(new byte[1]);
} catch (IllegalArgumentException e) {
- verifyException(e, "not owned");
+ verifyException(e, "smaller than original");
}
// but call with null is a NOP for convenience
ctxt.releaseReadIOBuffer(null);
@@ -40,7 +40,7 @@
try {
ctxt.releaseWriteEncodingBuffer(new byte[1]);
} catch (IllegalArgumentException e) {
- verifyException(e, "not owned");
+ verifyException(e, "smaller than original");
}
ctxt.releaseWriteEncodingBuffer(null);
@@ -55,7 +55,7 @@
try {
ctxt.releaseTokenBuffer(new char[1]);
} catch (IllegalArgumentException e) {
- verifyException(e, "not owned");
+ verifyException(e, "smaller than original");
}
ctxt.releaseTokenBuffer(null);
@@ -70,7 +70,7 @@
try {
ctxt.releaseConcatBuffer(new char[1]);
} catch (IllegalArgumentException e) {
- verifyException(e, "not owned");
+ verifyException(e, "smaller than original");
}
ctxt.releaseConcatBuffer(null);
@@ -85,7 +85,7 @@
try {
ctxt.releaseNameCopyBuffer(new char[1]);
} catch (IllegalArgumentException e) {
- verifyException(e, "not owned");
+ verifyException(e, "smaller than original");
}
ctxt.releaseNameCopyBuffer(null);
}
diff --git a/src/test/java/com/fasterxml/jackson/core/io/TestJDKSerializable.java b/src/test/java/com/fasterxml/jackson/core/io/TestJDKSerializable.java
deleted file mode 100644
index 214344c..0000000
--- a/src/test/java/com/fasterxml/jackson/core/io/TestJDKSerializable.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.fasterxml.jackson.core.io;
-
-import java.io.*;
-
-import com.fasterxml.jackson.core.*;
-
-public class TestJDKSerializable
- extends com.fasterxml.jackson.core.BaseTest
-{
- public void testLocationSerializability() throws Exception
- {
- JsonFactory jf = new JsonFactory();
- JsonParser jp = jf.createParser(" { }");
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- JsonLocation loc = jp.getCurrentLocation();
-
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- ObjectOutputStream out = new ObjectOutputStream(bytes);
- out.writeObject(loc);
- out.close();
- byte[] stuff = bytes.toByteArray();
-
- ObjectInputStream obIn = new ObjectInputStream(new ByteArrayInputStream(stuff));
- JsonLocation loc2 = (JsonLocation) obIn.readObject();
- assertNotNull(loc2);
-
- assertEquals(loc.getLineNr(), loc2.getLineNr());
- assertEquals(loc.getColumnNr(), loc2.getColumnNr());
- jp.close();
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/io/TestJsonStringEncoder.java b/src/test/java/com/fasterxml/jackson/core/io/TestJsonStringEncoder.java
index 9c982a4..ec03582 100644
--- a/src/test/java/com/fasterxml/jackson/core/io/TestJsonStringEncoder.java
+++ b/src/test/java/com/fasterxml/jackson/core/io/TestJsonStringEncoder.java
@@ -6,7 +6,6 @@
import static org.junit.Assert.*;
import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.core.io.JsonStringEncoder;
public class TestJsonStringEncoder
extends com.fasterxml.jackson.core.BaseTest
@@ -20,6 +19,21 @@
assertArrayEquals("\\\"x\\\"".toCharArray(), result);
}
+ public void testQuoteCharSequenceAsString() throws Exception
+ {
+ JsonStringEncoder encoder = new JsonStringEncoder();
+ StringBuilder output = new StringBuilder();
+ StringBuilder builder = new StringBuilder();
+ builder.append("foobar");
+ encoder.quoteAsString(builder, output);
+ assertEquals("foobar", output.toString());
+ builder.setLength(0);
+ output.setLength(0);
+ builder.append("\"x\"");
+ encoder.quoteAsString(builder, output);
+ assertEquals("\\\"x\\\"", output.toString());
+ }
+
// For [JACKSON-853]
public void testQuoteLongAsString() throws Exception
{
@@ -37,7 +51,24 @@
assertEquals(exp, new String(result));
}
-
+
+ public void testQuoteLongCharSequenceAsString() throws Exception
+ {
+ JsonStringEncoder encoder = new JsonStringEncoder();
+ StringBuilder output = new StringBuilder();
+ StringBuilder input = new StringBuilder();
+ StringBuilder sb2 = new StringBuilder();
+ for (int i = 0; i < 1111; ++i) {
+ input.append('"');
+ sb2.append("\\\"");
+ }
+ String exp = sb2.toString();
+ encoder.quoteAsString(input, output);
+ assertEquals(2*input.length(), output.length());
+ assertEquals(exp, output.toString());
+
+ }
+
public void testQuoteAsUTF8() throws Exception
{
// In this case, let's actually use existing JsonGenerator to produce expected values
@@ -82,7 +113,18 @@
char[] quoted = JsonStringEncoder.getInstance().quoteAsString(new String(input));
assertEquals("\\u0000\\u0001\\u0002\\u0003\\u0004", new String(quoted));
}
-
+
+ // [JACKSON-884]
+ public void testCharSequenceWithCtrlChars() throws Exception
+ {
+ char[] input = new char[] { 0, 1, 2, 3, 4 };
+ StringBuilder builder = new StringBuilder();
+ builder.append(input);
+ StringBuilder output = new StringBuilder();
+ JsonStringEncoder.getInstance().quoteAsString(builder, output);
+ assertEquals("\\u0000\\u0001\\u0002\\u0003\\u0004", output.toString());
+ }
+
/*
/**********************************************************
/* Helper methods
diff --git a/src/test/java/com/fasterxml/jackson/core/json/ArrayGenerationTest.java b/src/test/java/com/fasterxml/jackson/core/json/ArrayGenerationTest.java
index 80483bf..dd490b3 100644
--- a/src/test/java/com/fasterxml/jackson/core/json/ArrayGenerationTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/json/ArrayGenerationTest.java
@@ -6,7 +6,7 @@
import com.fasterxml.jackson.core.*;
/**
- * Basic testing for scalar-array write methods added in 2.8
+ * Basic testing for scalar-array write methods added in 2.8.
*/
public class ArrayGenerationTest extends BaseTest
{
diff --git a/src/test/java/com/fasterxml/jackson/core/json/GeneratorBasicTest.java b/src/test/java/com/fasterxml/jackson/core/json/GeneratorBasicTest.java
index 7b159a9..a490d5f 100644
--- a/src/test/java/com/fasterxml/jackson/core/json/GeneratorBasicTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/json/GeneratorBasicTest.java
@@ -55,14 +55,22 @@
public void testIntValueWrite() throws Exception
{
- doTestIntValueWrite(false);
- doTestIntValueWrite(true);
+ // char[]
+ doTestIntValueWrite(false, false);
+ doTestIntValueWrite(true, false);
+ // byte[]
+ doTestIntValueWrite(false, true);
+ doTestIntValueWrite(true, true);
}
public void testLongValueWrite() throws Exception
{
- doTestLongValueWrite(false);
- doTestLongValueWrite(true);
+ // char[]
+ doTestLongValueWrite(false, false);
+ doTestLongValueWrite(true, false);
+ // byte[]
+ doTestLongValueWrite(false, true);
+ doTestLongValueWrite(true, true);
}
public void testBooleanWrite() throws Exception
@@ -129,14 +137,18 @@
String docStr = sw.toString();
- JsonParser jp = createParserUsingReader(docStr);
- assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(1, jp.getIntValue());
- assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(2, jp.getIntValue());
- assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(-13, jp.getIntValue());
- jp.close();
+ try {
+ JsonParser jp = createParserUsingReader(docStr);
+ assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(1, jp.getIntValue());
+ assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(2, jp.getIntValue());
+ assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(-13, jp.getIntValue());
+ jp.close();
+ } catch (IOException e) {
+ fail("Problem with document ["+docStr+"]: "+e.getMessage());
+ }
}
// Convenience methods
@@ -273,63 +285,114 @@
/**********************************************************
*/
- private void doTestIntValueWrite(boolean pad) throws Exception
+ private void doTestIntValueWrite(boolean pad, boolean useBytes) throws Exception
{
int[] VALUES = new int[] {
0, 1, -9, 32, -32, 57, 189, 2017, -9999, 13240, 123456,
- 1111111, 22222222, 123456789, Integer.MAX_VALUE, Integer.MAX_VALUE
+ 1111111, 22222222, 123456789,
+ 7300999, -7300999,
+ 99300999, -99300999,
+ 999300999, -999300999,
+ 1000300999, 2000500126, -1000300999, -2000500126,
+ Integer.MIN_VALUE, Integer.MAX_VALUE
};
for (int i = 0; i < VALUES.length; ++i) {
int VALUE = VALUES[i];
- StringWriter sw = new StringWriter();
- JsonGenerator gen = JSON_F.createGenerator(sw);
- gen.writeNumber(VALUE);
- if (pad) {
- gen.writeRaw(" ");
+ String docStr;
+ JsonParser p;
+
+ if (useBytes) {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ JsonGenerator gen = JSON_F.createGenerator(bytes);
+ gen.writeNumber(VALUE);
+ if (pad) {
+ gen.writeRaw(" ");
+ }
+ gen.close();
+ docStr = bytes.toString("UTF-8");
+ p = JSON_F.createParser(bytes.toByteArray());
+ } else {
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = JSON_F.createGenerator(sw);
+ gen.writeNumber(VALUE);
+ if (pad) {
+ gen.writeRaw(" ");
+ }
+ gen.close();
+ docStr = sw.toString();
+ p = JSON_F.createParser(docStr);
}
- gen.close();
- String docStr = sw.toString();
- JsonParser jp = createParserUsingReader(docStr);
- JsonToken t = jp.nextToken();
+ JsonToken t = null;
+ try {
+ t = p.nextToken();
+ } catch (IOException e) {
+ fail("Problem with value "+VALUE+", document ["+docStr+"]: "+e.getMessage());
+ }
assertNotNull("Document \""+docStr+"\" yielded no tokens", t);
// Number are always available as lexical representation too
String exp = ""+VALUE;
- if (!exp.equals(jp.getText())) {
- fail("Expected '"+exp+"', got '"+jp.getText());
+ if (!exp.equals(p.getText())) {
+ fail("Expected '"+exp+"', got '"+p.getText());
}
assertEquals(JsonToken.VALUE_NUMBER_INT, t);
- assertEquals(VALUE, jp.getIntValue());
- assertEquals(null, jp.nextToken());
- jp.close();
+ assertEquals(VALUE, p.getIntValue());
+ assertEquals(null, p.nextToken());
+ p.close();
}
}
- private void doTestLongValueWrite(boolean pad) throws Exception
+ private void doTestLongValueWrite(boolean pad, boolean useBytes) throws Exception
{
long[] VALUES = new long[] {
- 0L, 1L, -1L, -12005002294L, Long.MIN_VALUE, Long.MAX_VALUE
+ 0L, 1L, -1L, 2000100345, -12005002294L,
+ 5111222333L, -5111222333L,
+ 65111222333L, -65111222333L,
+ 123456789012L, -123456789012L,
+ 123456789012345L, -123456789012345L,
+ 123456789012345789L, -123456789012345789L,
+ Long.MIN_VALUE, Long.MAX_VALUE
};
for (int i = 0; i < VALUES.length; ++i) {
long VALUE = VALUES[i];
- StringWriter sw = new StringWriter();
- JsonGenerator gen = JSON_F.createGenerator(sw);
- gen.writeNumber(VALUE);
- if (pad) {
- gen.writeRaw(" ");
+ String docStr;
+ JsonParser p;
+
+ if (useBytes) {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ JsonGenerator gen = JSON_F.createGenerator(bytes);
+ gen.writeNumber(VALUE);
+ if (pad) {
+ gen.writeRaw(" ");
+ }
+ gen.close();
+ docStr = bytes.toString("UTF-8");
+ p = JSON_F.createParser(bytes.toByteArray());
+ } else {
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = JSON_F.createGenerator(sw);
+ gen.writeNumber(VALUE);
+ if (pad) {
+ gen.writeRaw(" ");
+ }
+ gen.close();
+ docStr = sw.toString();
+ p = JSON_F.createParser(docStr);
}
- gen.close();
- String docStr = sw.toString();
- JsonParser jp = createParserUsingReader(docStr);
- JsonToken t = jp.nextToken();
+ JsonToken t = null;
+ try {
+ t = p.nextToken();
+ } catch (IOException e) {
+ fail("Problem with number "+VALUE+", document ["+docStr+"]: "+e.getMessage());
+ }
assertNotNull("Document \""+docStr+"\" yielded no tokens", t);
String exp = ""+VALUE;
- if (!exp.equals(jp.getText())) {
- fail("Expected '"+exp+"', got '"+jp.getText());
+ if (!exp.equals(p.getText())) {
+ fail("Expected '"+exp+"', got '"+p.getText());
}
assertEquals(JsonToken.VALUE_NUMBER_INT, t);
- assertEquals(VALUE, jp.getLongValue());
- assertEquals(null, jp.nextToken());
- jp.close();
+ assertEquals(VALUE, p.getLongValue());
+ assertEquals(null, p.nextToken());
+ p.close();
}
}
}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/GeneratorFailTest.java b/src/test/java/com/fasterxml/jackson/core/json/GeneratorFailTest.java
index 7b4368b..67ebb25 100644
--- a/src/test/java/com/fasterxml/jackson/core/json/GeneratorFailTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/json/GeneratorFailTest.java
@@ -32,6 +32,12 @@
_testFailOnWritingStringNotFieldName(F, true);
}
+ // for [core#282]
+ public void testFailOnWritingFieldNameInRoot() throws Exception {
+ _testFailOnWritingFieldNameInRoot(F, false);
+ _testFailOnWritingFieldNameInRoot(F, true);
+ }
+
/*
/**********************************************************
/* Internal methods
@@ -83,5 +89,24 @@
gen.close();
}
-
+ // for [core#282]
+ private void _testFailOnWritingFieldNameInRoot(JsonFactory f, boolean useReader) throws Exception
+ {
+ JsonGenerator gen;
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ if (useReader) {
+ gen = f.createGenerator(new OutputStreamWriter(bout, "UTF-8"));
+ } else {
+ gen = f.createGenerator(bout, JsonEncoding.UTF8);
+ }
+ try {
+ gen.writeFieldName("a");
+ gen.flush();
+ String json = bout.toString("UTF-8");
+ fail("Should not have let "+gen.getClass().getName()+".writeFieldName() be used in root context: output = "+json);
+ } catch (JsonProcessingException e) {
+ verifyException(e, "can not write a field name");
+ }
+ gen.close();
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/GeneratorFeaturesTest.java b/src/test/java/com/fasterxml/jackson/core/json/GeneratorFeaturesTest.java
new file mode 100644
index 0000000..20bdff4
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/json/GeneratorFeaturesTest.java
@@ -0,0 +1,323 @@
+package com.fasterxml.jackson.core.json;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+
+/**
+ * Set of basic unit tests for verifying that the basic generator
+ * functionality works as expected.
+ */
+public class GeneratorFeaturesTest
+ extends com.fasterxml.jackson.core.BaseTest
+{
+ private final JsonFactory JSON_F = new JsonFactory();
+
+ public void testConfigDefaults() throws IOException
+ {
+ JsonGenerator g = JSON_F.createGenerator(new StringWriter());
+ assertFalse(g.isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS));
+ assertFalse(g.isEnabled(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN));
+
+ assertTrue(g.canOmitFields());
+ assertFalse(g.canWriteBinaryNatively());
+ assertTrue(g.canWriteFormattedNumbers());
+ assertFalse(g.canWriteObjectId());
+ assertFalse(g.canWriteTypeId());
+
+ g.close();
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testConfigOverrides() throws IOException
+ {
+ // but also allow overide
+ JsonGenerator g = JSON_F.createGenerator(new StringWriter());
+ int mask = JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS.getMask()
+ | JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN.getMask();
+ g.overrideStdFeatures(mask, mask);
+ assertTrue(g.isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS));
+ assertTrue(g.isEnabled(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN));
+
+ // and for now, also test straight override
+ g.setFeatureMask(0);
+ assertFalse(g.isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS));
+ assertFalse(g.isEnabled(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN));
+ g.close();
+ }
+
+ public void testFieldNameQuoting() throws IOException
+ {
+ JsonFactory f = new JsonFactory();
+ // by default, quoting should be enabled
+ _testFieldNameQuoting(f, true);
+ // can disable it
+ f.disable(JsonGenerator.Feature.QUOTE_FIELD_NAMES);
+ _testFieldNameQuoting(f, false);
+ // and (re)enable:
+ f.enable(JsonGenerator.Feature.QUOTE_FIELD_NAMES);
+ _testFieldNameQuoting(f, true);
+ }
+
+ public void testNonNumericQuoting() throws IOException
+ {
+ JsonFactory f = new JsonFactory();
+ // by default, quoting should be enabled
+ _testNonNumericQuoting(f, true);
+ // can disable it
+ f.disable(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS);
+ _testNonNumericQuoting(f, false);
+ // and (re)enable:
+ f.enable(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS);
+ _testNonNumericQuoting(f, true);
+ }
+
+ /**
+ * Testing for [JACKSON-176], ability to force serializing numbers
+ * as JSON Strings.
+ */
+ public void testNumbersAsJSONStrings() throws IOException
+ {
+ JsonFactory f = new JsonFactory();
+ // by default should output numbers as-is:
+ assertEquals("[1,2,1.25,2.25,3001,0.5,-1]", _writeNumbers(f));
+
+ // but if overridden, quotes as Strings
+ f.configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, true);
+ assertEquals("[\"1\",\"2\",\"1.25\",\"2.25\",\"3001\",\"0.5\",\"-1\"]",
+ _writeNumbers(f));
+
+
+ }
+
+ public void testBigDecimalAsPlain() throws IOException
+ {
+ JsonFactory f = new JsonFactory();
+ BigDecimal ENG = new BigDecimal("1E+2");
+
+ StringWriter sw = new StringWriter();
+ JsonGenerator g = f.createGenerator(sw);
+ g.writeNumber(ENG);
+ g.close();
+ assertEquals("1E+2", sw.toString());
+
+ f.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true);
+ sw = new StringWriter();
+ g = f.createGenerator(sw);
+ g.writeNumber(ENG);
+ g.close();
+ assertEquals("100", sw.toString());
+ }
+
+ public void testBigDecimalAsPlainString() throws Exception
+ {
+ JsonFactory f = new JsonFactory();
+ BigDecimal ENG = new BigDecimal("1E+2");
+ f.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
+ f.enable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);
+
+ StringWriter sw = new StringWriter();
+ JsonGenerator g = f.createGenerator(sw);
+ g.writeNumber(ENG);
+ g.close();
+ assertEquals(quote("100"), sw.toString());
+
+ // also, as bytes
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ g = f.createGenerator(bos);
+ g.writeNumber(ENG);
+ g.close();
+ assertEquals(quote("100"), bos.toString("UTF-8"));
+ }
+
+ // [core#315]
+ public void testTooBigBigDecimal() throws Exception
+ {
+ JsonFactory f = new JsonFactory();
+ f.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
+
+ // 24-Aug-2016, tatu: Initial check limits scale to [-9999,+9999]
+ BigDecimal BIG = new BigDecimal("1E+9999");
+ BigDecimal TOO_BIG = new BigDecimal("1E+10000");
+ BigDecimal SMALL = new BigDecimal("1E-9999");
+ BigDecimal TOO_SMALL = new BigDecimal("1E-10000");
+
+ for (boolean useBytes : new boolean[] { false, true } ) {
+ for (boolean asString : new boolean[] { false, true } ) {
+ JsonGenerator g;
+
+ if (useBytes) {
+ g = f.createGenerator(new ByteArrayOutputStream());
+ } else {
+ g = f.createGenerator(new StringWriter());
+ }
+ if (asString) {
+ g.enable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);
+ }
+
+ // first, ok cases:
+ g.writeStartArray();
+ g.writeNumber(BIG);
+ g.writeNumber(SMALL);
+ g.writeEndArray();
+ g.close();
+
+ // then invalid
+ for (BigDecimal input : new BigDecimal[] { TOO_BIG, TOO_SMALL }) {
+ if (useBytes) {
+ g = f.createGenerator(new ByteArrayOutputStream());
+ } else {
+ g = f.createGenerator(new StringWriter());
+ }
+ if (asString) {
+ g.enable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);
+ }
+ try {
+ g.writeNumber(input);
+ fail("Should not have written without exception: "+input);
+ } catch (JsonGenerationException e) {
+ verifyException(e, "Attempt to write plain `java.math.BigDecimal`");
+ verifyException(e, "illegal scale");
+ }
+ g.close();
+ }
+ }
+ }
+ }
+
+ private String _writeNumbers(JsonFactory f) throws IOException
+ {
+ StringWriter sw = new StringWriter();
+ JsonGenerator g = f.createGenerator(sw);
+
+ g.writeStartArray();
+ g.writeNumber(1);
+ g.writeNumber(2L);
+ g.writeNumber(1.25);
+ g.writeNumber(2.25f);
+ g.writeNumber(BigInteger.valueOf(3001));
+ g.writeNumber(BigDecimal.valueOf(0.5));
+ g.writeNumber("-1");
+ g.writeEndArray();
+ g.close();
+
+ return sw.toString();
+ }
+
+ // for [core#246]
+ public void testFieldNameQuotingEnabled() throws IOException
+ {
+ // // First, test with default factory, with quoting enabled by default
+
+ // First, default, with quotes
+ _testFieldNameQuotingEnabled(JSON_F, true, true, "{\"foo\":1}");
+ _testFieldNameQuotingEnabled(JSON_F, false, true, "{\"foo\":1}");
+
+ // then without quotes
+ _testFieldNameQuotingEnabled(JSON_F, true, false, "{foo:1}");
+ _testFieldNameQuotingEnabled(JSON_F, false, false, "{foo:1}");
+
+ // // Then with alternatively configured factory
+
+ JsonFactory f2 = new JsonFactory();
+ f2.disable(JsonGenerator.Feature.QUOTE_FIELD_NAMES);
+
+ _testFieldNameQuotingEnabled(f2, true, true, "{\"foo\":1}");
+ _testFieldNameQuotingEnabled(f2, false, true, "{\"foo\":1}");
+
+ // then without quotes
+ _testFieldNameQuotingEnabled(f2, true, false, "{foo:1}");
+ _testFieldNameQuotingEnabled(f2, false, false, "{foo:1}");
+ }
+
+ private void _testFieldNameQuotingEnabled(JsonFactory f, boolean useBytes,
+ boolean useQuotes, String exp) throws IOException
+ {
+ ByteArrayOutputStream bytes = useBytes ? new ByteArrayOutputStream() : null;
+ StringWriter sw = useBytes ? null : new StringWriter();
+ JsonGenerator gen = useBytes ? f.createGenerator(bytes) : f.createGenerator(sw);
+ if (useQuotes) {
+ gen.enable(JsonGenerator.Feature.QUOTE_FIELD_NAMES);
+ } else {
+ gen.disable(JsonGenerator.Feature.QUOTE_FIELD_NAMES);
+ }
+
+ gen.writeStartObject();
+ gen.writeFieldName("foo");
+ gen.writeNumber(1);
+ gen.writeEndObject();
+ gen.close();
+
+ String json = useBytes ? bytes.toString("UTF-8") : sw.toString();
+ assertEquals(exp, json);
+ }
+
+ public void testChangeOnGenerator() throws IOException
+ {
+ StringWriter w = new StringWriter();
+
+ JsonGenerator g = JSON_F.createGenerator(w);
+ g.enable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);
+ g.writeNumber(123);
+ g.close();
+ assertEquals(quote("123"), w.toString());
+
+ // but also the opposite
+ w = new StringWriter();
+ g = JSON_F.createGenerator(w);
+ g.enable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);
+ g.disable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);
+ g.writeNumber(123);
+ g.close();
+ assertEquals("123", w.toString());
+ }
+
+ /*
+ /**********************************************************
+ /* Helper methods
+ /**********************************************************
+ */
+
+ private void _testFieldNameQuoting(JsonFactory f, boolean quoted)
+ throws IOException
+ {
+ StringWriter sw = new StringWriter();
+ JsonGenerator g = f.createGenerator(sw);
+ g.writeStartObject();
+ g.writeFieldName("foo");
+ g.writeNumber(1);
+ g.writeEndObject();
+ g.close();
+
+ String result = sw.toString();
+ if (quoted) {
+ assertEquals("{\"foo\":1}", result);
+ } else {
+ assertEquals("{foo:1}", result);
+ }
+ }
+ private void _testNonNumericQuoting(JsonFactory f, boolean quoted)
+ throws IOException
+ {
+ StringWriter sw = new StringWriter();
+ JsonGenerator g = f.createGenerator(sw);
+ g.writeStartObject();
+ g.writeFieldName("double");
+ g.writeNumber(Double.NaN);
+ g.writeEndObject();
+ g.writeStartObject();
+ g.writeFieldName("float");
+ g.writeNumber(Float.NaN);
+ g.writeEndObject();
+ g.close();
+
+ String result = sw.toString();
+ if (quoted) {
+ assertEquals("{\"double\":\"NaN\"} {\"float\":\"NaN\"}", result);
+ } else {
+ assertEquals("{\"double\":NaN} {\"float\":NaN}", result);
+ }
+ }
+}
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 6b3f4c9..85363f4 100644
--- a/src/test/java/com/fasterxml/jackson/core/json/ParserSequenceTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/json/ParserSequenceTest.java
@@ -3,16 +3,15 @@
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.util.JsonParserSequence;
+@SuppressWarnings("resource")
public class ParserSequenceTest
extends com.fasterxml.jackson.core.BaseTest
{
- private final JsonFactory JSON_FACTORY = new JsonFactory();
-
public void testSimple() throws Exception
{
JsonParser p1 = JSON_FACTORY.createParser("[ 1 ]");
JsonParser p2 = JSON_FACTORY.createParser("[ 2 ]");
- JsonParserSequence seq = JsonParserSequence.createFlattened(p1, p2);
+ JsonParserSequence seq = JsonParserSequence.createFlattened(false, p1, p2);
assertEquals(2, seq.containedParsersCount());
assertFalse(p1.isClosed());
@@ -45,8 +44,52 @@
assertTrue(seq.isClosed());
seq.close();
- // redundant, but call to remove IDE warnings
- p1.close();
- p2.close();
+ }
+
+ // for [jackson-core#296]
+ public void testInitializationDisabled() throws Exception
+ {
+ // // First, with old legacy settings
+
+ JsonParser p1 = JSON_FACTORY.createParser("1 2");
+ JsonParser p2 = JSON_FACTORY.createParser("3 true");
+ assertToken(JsonToken.VALUE_NUMBER_INT, p1.nextToken());
+ assertEquals(1, p1.getIntValue());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p2.nextToken());
+ assertEquals(3, p2.getIntValue());
+
+ // with legacy settings, will see neither '1' nor '3'
+
+ JsonParserSequence seq = JsonParserSequence.createFlattened(false, p1, p2);
+ assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken());
+ assertEquals(2, seq.getIntValue());
+ assertToken(JsonToken.VALUE_TRUE, seq.nextToken());
+ assertNull(seq.nextToken());
+ seq.close();
+ }
+
+ // for [jackson-core#296]
+ public void testInitializationEnabled() throws Exception
+ {
+ // // and then with new "check for current":
+ JsonParser p1 = JSON_FACTORY.createParser("1 2");
+ JsonParser p2 = JSON_FACTORY.createParser("3 true");
+ assertToken(JsonToken.VALUE_NUMBER_INT, p1.nextToken());
+ assertEquals(1, p1.getIntValue());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p2.nextToken());
+ assertEquals(3, p2.getIntValue());
+
+ // with new settings, both '1' and '3' will be visible
+
+ JsonParserSequence seq = JsonParserSequence.createFlattened(true, p1, p2);
+ assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken());
+ assertEquals(1, seq.getIntValue());
+ assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken());
+ assertEquals(2, seq.getIntValue());
+ assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken());
+ assertEquals(3, seq.getIntValue());
+ assertToken(JsonToken.VALUE_TRUE, seq.nextToken());
+ assertNull(seq.nextToken());
+ seq.close();
}
}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/RequestPayloadOnExceptionTest.java b/src/test/java/com/fasterxml/jackson/core/json/RequestPayloadOnExceptionTest.java
new file mode 100644
index 0000000..7fbc717
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/json/RequestPayloadOnExceptionTest.java
@@ -0,0 +1,144 @@
+package com.fasterxml.jackson.core.json;
+
+import com.fasterxml.jackson.core.*;
+
+public class RequestPayloadOnExceptionTest extends BaseTest
+{
+ /**
+ * Tests for Request payload data (bytes) on parsing error
+ */
+ public void testRequestPayloadAsBytesOnParseException() throws Exception {
+ testRequestPayloadAsBytesOnParseExceptionInternal(true, "nul");
+ testRequestPayloadAsBytesOnParseExceptionInternal(false, "nul");
+ }
+
+ /**
+ * Tests for Request payload data (String) on parsing error
+ */
+ public void testRequestPayloadAsStringOnParseException() throws Exception {
+ testRequestPayloadAsStringOnParseExceptionInternal(true, "nul");
+ testRequestPayloadAsStringOnParseExceptionInternal(false, "nul");
+ }
+
+ /**
+ * Tests for Raw Request payload data on parsing error
+ */
+ public void testRawRequestPayloadOnParseException() throws Exception {
+ testRawRequestPayloadOnParseExceptionInternal(true, "nul");
+ testRawRequestPayloadOnParseExceptionInternal(false, "nul");
+ }
+
+ /**
+ * Tests for no Request payload data on parsing error
+ */
+ public void testNoRequestPayloadOnParseException() throws Exception {
+ testNoRequestPayloadOnParseExceptionInternal(true, "nul");
+ testNoRequestPayloadOnParseExceptionInternal(false, "nul");
+ }
+
+ /**
+ * Tests for Request payload data which is null
+ */
+ public void testNullRequestPayloadOnParseException() throws Exception {
+ testNullRequestPayloadOnParseExceptionInternal(true, "nul");
+ testNullRequestPayloadOnParseExceptionInternal(false, "nul");
+ }
+
+ /**
+ * Tests for null Charset in Request payload data
+ */
+ public void testNullCharsetOnParseException() throws Exception {
+ testNullCharsetOnParseExceptionInternal(true, "nul");
+ testNullCharsetOnParseExceptionInternal(false, "nul");
+ }
+
+ /*
+ * *******************Private Methods*************************
+ */
+ private void testRequestPayloadAsBytesOnParseExceptionInternal(boolean isStream, String value) throws Exception {
+ final String doc = "{ \"key1\" : " + value + " }";
+ JsonParser jp = isStream ? createParserUsingStream(doc, "UTF-8") : createParserUsingReader(doc);
+ jp.setRequestPayloadOnError(doc.getBytes(), "UTF-8");
+ assertToken(JsonToken.START_OBJECT, jp.nextToken());
+ try {
+ jp.nextToken();
+ fail("Expecting parsing exception");
+ } catch (JsonParseException ex) {
+ assertEquals("Request payload data should match", doc, ex.getRequestPayloadAsString());
+ assertTrue("Message contains request body", ex.getMessage().contains("Request payload : " + doc));
+ }
+ jp.close();
+ }
+
+ private void testRequestPayloadAsStringOnParseExceptionInternal(boolean isStream, String value) throws Exception {
+ final String doc = "{ \"key1\" : " + value + " }";
+ JsonParser jp = isStream ? createParserUsingStream(doc, "UTF-8") : createParserUsingReader(doc);
+ jp.setRequestPayloadOnError(doc);
+ assertToken(JsonToken.START_OBJECT, jp.nextToken());
+ try {
+ jp.nextToken();
+ fail("Expecting parsing exception");
+ } catch (JsonParseException ex) {
+ assertEquals("Request payload data should match", doc, ex.getRequestPayloadAsString());
+ assertTrue("Message contains request body", ex.getMessage().contains("Request payload : " + doc));
+ }
+ jp.close();
+ }
+
+ private void testRawRequestPayloadOnParseExceptionInternal(boolean isStream, String value) throws Exception {
+ final String doc = "{ \"key1\" : " + value + " }";
+ JsonParser jp = isStream ? createParserUsingStream(doc, "UTF-8") : createParserUsingReader(doc);
+ jp.setRequestPayloadOnError(doc.getBytes(), "UTF-8");
+ assertToken(JsonToken.START_OBJECT, jp.nextToken());
+ try {
+ jp.nextToken();
+ fail("Expecting parsing exception");
+ } catch (JsonParseException ex) {
+ assertTrue(((byte[]) ex.getRequestPayload().getRawPayload()).length > 0);
+ assertTrue("Message contains request body", ex.getMessage().contains("Request payload : " + doc));
+ }
+ jp.close();
+ }
+
+ private void testNoRequestPayloadOnParseExceptionInternal(boolean isStream, String value) throws Exception {
+ final String doc = "{ \"key1\" : " + value + " }";
+ JsonParser jp = isStream ? createParserUsingStream(doc, "UTF-8") : createParserUsingReader(doc);
+ assertToken(JsonToken.START_OBJECT, jp.nextToken());
+ try {
+ jp.nextToken();
+ fail("Expecting parsing exception");
+ } catch (JsonParseException ex) {
+ assertEquals("Request payload data should be null", null, ex.getRequestPayload());
+ }
+ jp.close();
+ }
+
+ private void testNullRequestPayloadOnParseExceptionInternal(boolean isStream, String value) throws Exception {
+ final String doc = "{ \"key1\" : " + value + " }";
+ JsonParser jp = isStream ? createParserUsingStream(doc, "UTF-8") : createParserUsingReader(doc);
+ jp.setRequestPayloadOnError(null, "UTF-8");
+ assertToken(JsonToken.START_OBJECT, jp.nextToken());
+ try {
+ jp.nextToken();
+ fail("Expecting parsing exception");
+ } catch (JsonParseException ex) {
+ assertEquals("Request payload data should be null", null, ex.getRequestPayload());
+ }
+ jp.close();
+ }
+
+ private void testNullCharsetOnParseExceptionInternal(boolean isStream, String value) throws Exception {
+ final String doc = "{ \"key1\" : " + value + " }";
+ JsonParser jp = isStream ? createParserUsingStream(doc, "UTF-8") : createParserUsingReader(doc);
+ jp.setRequestPayloadOnError(doc.getBytes(), "UTF-8");
+ assertToken(JsonToken.START_OBJECT, jp.nextToken());
+ try {
+ jp.nextToken();
+ fail("Expecting parsing exception");
+ } catch (JsonParseException ex) {
+ assertEquals("Request payload data should match", doc, ex.getRequestPayloadAsString());
+ assertTrue("Message contains request body", ex.getMessage().contains("Request payload : " + doc));
+ }
+ jp.close();
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/StringGenerationTest.java b/src/test/java/com/fasterxml/jackson/core/json/StringGenerationTest.java
index b36cbe6..4fd67c1 100644
--- a/src/test/java/com/fasterxml/jackson/core/json/StringGenerationTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/json/StringGenerationTest.java
@@ -20,7 +20,7 @@
"\"\"\"", "\\r)'\"",
"Longer text & other stuff:\twith some\r\n\r\n random linefeeds etc added in to cause some \"special\" handling \\\\ to occur...\n"
};
-
+
private final JsonFactory FACTORY = new JsonFactory();
public void testBasicEscaping() throws Exception
@@ -32,21 +32,21 @@
// for [core#194]
public void testMediumStringsBytes() throws Exception
{
- _testMediumStrings(true, 1100);
- _testMediumStrings(true, 2300);
- _testMediumStrings(true, 3800);
- _testMediumStrings(true, 7500);
- _testMediumStrings(true, 19000);
+ for (int mode : ALL_BINARY_MODES) {
+ for (int size : new int[] { 1100, 2300, 3800, 7500, 19000 }) {
+ _testMediumStrings(mode, size);
+ }
+ }
}
// for [core#194]
public void testMediumStringsChars() throws Exception
{
- _testMediumStrings(false, 1100);
- _testMediumStrings(false, 2300);
- _testMediumStrings(false, 3800);
- _testMediumStrings(false, 7500);
- _testMediumStrings(false, 19000);
+ for (int mode : ALL_TEXT_MODES) {
+ for (int size : new int[] { 1100, 2300, 3800, 7500, 19000 }) {
+ _testMediumStrings(mode, size);
+ }
+ }
}
public void testLongerRandomSingleChunk() throws Exception
@@ -54,10 +54,12 @@
/* Let's first generate 100k of pseudo-random characters, favoring
* 7-bit ascii range
*/
- for (int round = 0; round < 80; ++round) {
- String content = generateRandom(75000+round);
- doTestLongerRandom(content, false);
- doTestLongerRandom(content, true);
+ for (int mode : ALL_TEXT_MODES) {
+ for (int round = 0; round < 80; ++round) {
+ String content = generateRandom(75000+round);
+ _testLongerRandom(mode, content, false);
+ _testLongerRandom(mode, content, true);
+ }
}
}
@@ -66,10 +68,12 @@
/* Let's first generate 100k of pseudo-random characters, favoring
* 7-bit ascii range
*/
- for (int round = 0; round < 70; ++round) {
- String content = generateRandom(73000+round);
- doTestLongerRandomMulti(content, false, round);
- doTestLongerRandomMulti(content, true, round);
+ for (int mode : ALL_TEXT_MODES) {
+ for (int round = 0; round < 70; ++round) {
+ String content = generateRandom(73000+round);
+ _testLongerRandomMulti(mode, content, false, round);
+ _testLongerRandomMulti(mode, content, true, round);
+ }
}
}
@@ -126,27 +130,25 @@
return sb.toString();
}
- private void _testMediumStrings(boolean useBinary, int length) throws Exception
+ private void _testMediumStrings(int readMode, int length) throws Exception
{
String text = _generareMediumText(length);
StringWriter sw = new StringWriter();
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- JsonGenerator gen = useBinary ? FACTORY.createGenerator(bytes)
+ JsonGenerator gen = (readMode != MODE_READER) ? FACTORY.createGenerator(bytes)
: FACTORY.createGenerator(sw);
gen.writeStartArray();
gen.writeString(text);
gen.writeEndArray();
gen.close();
- String json;
- if (useBinary) {
- json = bytes.toString("UTF-8");
+ JsonParser p;
+ if (readMode == MODE_READER) {
+ p = FACTORY.createParser(sw.toString());
} else {
- json = sw.toString();
+ p = createParser(FACTORY, readMode, bytes.toByteArray());
}
-
- JsonParser p = FACTORY.createParser(json);
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_STRING, p.nextToken());
assertEquals(text, p.getText());
@@ -154,8 +156,7 @@
p.close();
}
- private void doTestBasicEscaping(boolean charArray)
- throws Exception
+ private void doTestBasicEscaping(boolean charArray) throws Exception
{
for (int i = 0; i < SAMPLES.length; ++i) {
String VALUE = SAMPLES[i];
@@ -172,18 +173,18 @@
gen.writeEndArray();
gen.close();
String docStr = sw.toString();
- JsonParser jp = createParserUsingReader(docStr);
- assertEquals(JsonToken.START_ARRAY, jp.nextToken());
- JsonToken t = jp.nextToken();
+ JsonParser p = createParserUsingReader(docStr);
+ assertEquals(JsonToken.START_ARRAY, p.nextToken());
+ JsonToken t = p.nextToken();
assertEquals(JsonToken.VALUE_STRING, t);
- assertEquals(VALUE, jp.getText());
- assertEquals(JsonToken.END_ARRAY, jp.nextToken());
- assertEquals(null, jp.nextToken());
- jp.close();
+ assertEquals(VALUE, p.getText());
+ assertEquals(JsonToken.END_ARRAY, p.nextToken());
+ assertEquals(null, p.nextToken());
+ p.close();
}
}
- private void doTestLongerRandom(String text, boolean charArray)
+ private void _testLongerRandom(int readMode, String text, boolean charArray)
throws Exception
{
ByteArrayOutputStream bow = new ByteArrayOutputStream(text.length());
@@ -200,11 +201,11 @@
gen.writeEndArray();
gen.close();
byte[] docData = bow.toByteArray();
- JsonParser jp = FACTORY.createParser(new ByteArrayInputStream(docData));
- assertEquals(JsonToken.START_ARRAY, jp.nextToken());
- JsonToken t = jp.nextToken();
+ JsonParser p = createParser(FACTORY, readMode, docData);
+ assertEquals(JsonToken.START_ARRAY, p.nextToken());
+ JsonToken t = p.nextToken();
assertEquals(JsonToken.VALUE_STRING, t);
- String act = jp.getText();
+ String act = p.getText();
if (!text.equals(act)) {
if (text.length() != act.length()) {
fail("Expected string length "+text.length()+", actual "+act.length());
@@ -217,12 +218,11 @@
}
fail("Strings differ at position #"+i+" (len "+text.length()+"): expected char 0x"+Integer.toHexString(text.charAt(i))+", actual 0x"+Integer.toHexString(act.charAt(i)));
}
- assertEquals(JsonToken.END_ARRAY, jp.nextToken());
- assertEquals(null, jp.nextToken());
- jp.close();
+ assertEquals(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
}
- private void doTestLongerRandomMulti(String text, boolean charArray, int round)
+ private void _testLongerRandomMulti(int readMode, String text, boolean charArray, int round)
throws Exception
{
ByteArrayOutputStream bow = new ByteArrayOutputStream(text.length());
@@ -265,13 +265,13 @@
gen.writeEndArray();
gen.close();
byte[] docData = bow.toByteArray();
- JsonParser jp = FACTORY.createParser(new ByteArrayInputStream(docData));
- assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+ JsonParser p = createParser(FACTORY, readMode, docData);
+ assertEquals(JsonToken.START_ARRAY, p.nextToken());
offset = 0;
- while (jp.nextToken() == JsonToken.VALUE_STRING) {
+ while (p.nextToken() == JsonToken.VALUE_STRING) {
// Let's verify, piece by piece
- String act = jp.getText();
+ String act = p.getText();
String exp = text.substring(offset, offset+act.length());
if (act.length() != exp.length()) {
fail("String segment ["+offset+" - "+(offset+act.length())+"[ differs; exp length "+exp+", actual "+act);
@@ -287,7 +287,7 @@
}
offset += act.length();
}
- assertEquals(JsonToken.END_ARRAY, jp.getCurrentToken());
- jp.close();
+ assertEquals(JsonToken.END_ARRAY, p.currentToken());
+ p.close();
}
}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestComments.java b/src/test/java/com/fasterxml/jackson/core/json/TestComments.java
deleted file mode 100644
index 9840909..0000000
--- a/src/test/java/com/fasterxml/jackson/core/json/TestComments.java
+++ /dev/null
@@ -1,274 +0,0 @@
-package com.fasterxml.jackson.core.json;
-
-import java.io.*;
-
-import com.fasterxml.jackson.core.*;
-
-/**
- * Unit tests for verifying that support for (non-standard) comments
- * works as expected.
- */
-public class TestComments
- extends com.fasterxml.jackson.core.BaseTest
-{
- final static String DOC_WITH_SLASHSTAR_COMMENT =
- "[ /* comment:\n ends here */ 1 /* one more ok to have \"unquoted\" */ ]"
- ;
-
- final static String DOC_WITH_SLASHSLASH_COMMENT =
- "[ // comment...\n 1 \r // one more, not array: [] \n ]"
- ;
-
- /*
- /**********************************************************
- /* Test method wrappers
- /**********************************************************
- */
-
- /**
- * Unit test for verifying that by default comments are not
- * recognized.
- */
- public void testDefaultSettings()
- throws Exception
- {
- JsonFactory jf = new JsonFactory();
- assertFalse(jf.isEnabled(JsonParser.Feature.ALLOW_COMMENTS));
- JsonParser jp = jf.createParser(new StringReader("[ 1 ]"));
- assertFalse(jp.isEnabled(JsonParser.Feature.ALLOW_COMMENTS));
- jp.close();
- }
-
- public void testCommentsDisabled()
- throws Exception
- {
- _testDisabled(DOC_WITH_SLASHSTAR_COMMENT, false);
- _testDisabled(DOC_WITH_SLASHSLASH_COMMENT, false);
- _testDisabled(DOC_WITH_SLASHSTAR_COMMENT, true);
- _testDisabled(DOC_WITH_SLASHSLASH_COMMENT, true);
- }
-
- public void testCommentsEnabled()
- throws Exception
- {
- _testEnabled(DOC_WITH_SLASHSTAR_COMMENT, false);
- _testEnabled(DOC_WITH_SLASHSLASH_COMMENT, false);
- _testEnabled(DOC_WITH_SLASHSTAR_COMMENT, true);
- _testEnabled(DOC_WITH_SLASHSLASH_COMMENT, true);
- }
-
- // for [JACKSON-779]
- public void testCommentsWithUTF8() throws Exception
- {
- final String JSON = "/* \u00a9 2099 Yoyodyne Inc. */\n [ \"bar? \u00a9\" ]\n";
- _testWithUTF8Chars(JSON, false);
- _testWithUTF8Chars(JSON, true);
- }
-
- public void testYAMLCommentsBytes() throws Exception {
- JsonFactory f = new JsonFactory();
- f.configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true);
- _testYAMLComments(f, true);
- _testCommentsBeforePropValue(f, true, "# foo\n");
- }
-
- public void testYAMLCommentsChars() throws Exception {
- JsonFactory f = new JsonFactory();
- f.configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true);
- _testYAMLComments(f, false);
- final String COMMENT = "# foo\n";
- _testCommentsBeforePropValue(f, false, COMMENT);
- _testCommentsBetweenArrayValues(f, false, COMMENT);
- }
-
- public void testCCommentsBytes() throws Exception {
- JsonFactory f = new JsonFactory();
- f.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
- final String COMMENT = "/* foo */\n";
- _testCommentsBeforePropValue(f, true, COMMENT);
- }
-
- public void testCCommentsChars() throws Exception {
- JsonFactory f = new JsonFactory();
- f.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
- final String COMMENT = "/* foo */\n";
- _testCommentsBeforePropValue(f, false, COMMENT);
- }
-
- public void testCppCommentsBytes() throws Exception {
- JsonFactory f = new JsonFactory();
- f.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
- final String COMMENT = "// foo\n";
- _testCommentsBeforePropValue(f, true, COMMENT);
- }
-
- public void testCppCommentsChars() throws Exception {
- JsonFactory f = new JsonFactory();
- f.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
- final String COMMENT = "// foo \n";
- _testCommentsBeforePropValue(f, false, COMMENT);
- }
-
- @SuppressWarnings("resource")
- private void _testCommentsBeforePropValue(JsonFactory f, boolean useStream, String comment) throws Exception
- {
- for (String arg : new String[] {
- ":%s123",
- " :%s123",
- "\t:%s123",
- ": %s123",
- ":\t%s123",
- }) {
- String commented = String.format(arg, comment);
-
- final String DOC = "{\"abc\"" + commented + "}";
- JsonParser jp = useStream ?
- f.createParser(DOC.getBytes("UTF-8"))
- : f.createParser(DOC);
- assertEquals(JsonToken.START_OBJECT, jp.nextToken());
- JsonToken t = null;
- try {
- t = jp.nextToken();
- } catch (Exception e) {
- throw new RuntimeException("Failed on '"+DOC+"' due to "+e, e);
- }
- assertEquals(JsonToken.FIELD_NAME, t);
-
- try {
- t = jp.nextToken();
- } catch (Exception e) {
- throw new RuntimeException("Failed on '"+DOC+"' due to "+e, e);
- }
- assertEquals(JsonToken.VALUE_NUMBER_INT, t);
- assertEquals(123, jp.getIntValue());
- assertEquals(JsonToken.END_OBJECT, jp.nextToken());
- jp.close();
- }
-
- }
-
- @SuppressWarnings("resource")
- private void _testCommentsBetweenArrayValues(JsonFactory f, boolean useStream, String comment) throws Exception
- {
- for (String tmpl : new String[] {
- "%s,",
- " %s,",
- "\t%s,",
- "%s ,",
- "%s\t,",
- " %s ,",
- "\t%s\t,",
- "\n%s,",
- "%s\n,",
- }) {
- String commented = String.format(tmpl, comment);
-
- final String DOC = "[1"+commented+"2]";
- JsonParser jp = useStream ?
- f.createParser(DOC.getBytes("UTF-8"))
- : f.createParser(DOC);
- assertEquals(JsonToken.START_ARRAY, jp.nextToken());
- JsonToken t = null;
- try {
- t = jp.nextToken();
- } catch (Exception e) {
- throw new RuntimeException("Failed on '"+DOC+"' due to "+e, e);
- }
- assertEquals(JsonToken.VALUE_NUMBER_INT, t);
- assertEquals(1, jp.getIntValue());
-
- try {
- t = jp.nextToken();
- } catch (Exception e) {
- throw new RuntimeException("Failed on '"+DOC+"' due to "+e, e);
- }
- assertEquals(JsonToken.VALUE_NUMBER_INT, t);
- assertEquals(2, jp.getIntValue());
- assertEquals(JsonToken.END_ARRAY, jp.nextToken());
- jp.close();
- }
-
- }
-
- private void _testYAMLComments(JsonFactory f, boolean useStream) throws Exception
- {
- final String DOC = "# foo\n"
- +" {\"a\" # xyz\n"
- +" : # foo\n"
- +" 1, # more\n"
- +"\"b\": [ \n"
- +" #all!\n"
- +" 3 #yay!\n"
- +"] # foobar\n"
- +"} # x"
- ;
- JsonParser jp = useStream ?
- f.createParser(DOC.getBytes("UTF-8"))
- : f.createParser(DOC);
- assertEquals(JsonToken.START_OBJECT, jp.nextToken());
- assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("a", jp.getCurrentName());
- assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(1, jp.getIntValue());
- assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("b", jp.getCurrentName());
- assertEquals(JsonToken.START_ARRAY, jp.nextToken());
- assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(3, jp.getIntValue());
- assertEquals(JsonToken.END_ARRAY, jp.nextToken());
- assertEquals(JsonToken.END_OBJECT, jp.nextToken());
- assertNull(jp.nextToken());
- jp.close();
- }
-
- /*
- /**********************************************************
- /* Helper methods
- /**********************************************************
- */
-
- private void _testWithUTF8Chars(String doc, boolean useStream) throws IOException
- {
- // should basically just stream through
- JsonParser jp = _createParser(doc, useStream, true);
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- assertNull(jp.nextToken());
- jp.close();
- }
-
- private void _testDisabled(String doc, boolean useStream) throws IOException
- {
- JsonParser jp = _createParser(doc, useStream, false);
- try {
- jp.nextToken();
- fail("Expected exception for unrecognized comment");
- } catch (JsonParseException je) {
- // Should have something denoting that user may want to enable 'ALLOW_COMMENTS'
- verifyException(je, "ALLOW_COMMENTS");
- }
- jp.close();
- }
-
- private void _testEnabled(String doc, boolean useStream)
- throws IOException
- {
- JsonParser jp = _createParser(doc, useStream, true);
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(1, jp.getIntValue());
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- jp.close();
- }
-
- private JsonParser _createParser(String doc, boolean useStream, boolean enabled)
- throws IOException
- {
- JsonFactory jf = new JsonFactory();
- jf.configure(JsonParser.Feature.ALLOW_COMMENTS, enabled);
- JsonParser jp = useStream ?
- jf.createParser(doc.getBytes("UTF-8"))
- : jf.createParser(doc);
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- return jp;
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestCustomEscaping.java b/src/test/java/com/fasterxml/jackson/core/json/TestCustomEscaping.java
index ae7a841..7188be7 100644
--- a/src/test/java/com/fasterxml/jackson/core/json/TestCustomEscaping.java
+++ b/src/test/java/com/fasterxml/jackson/core/json/TestCustomEscaping.java
@@ -78,7 +78,7 @@
}
// // // Tests for [JACKSON-106]
-
+
public void testEscapeCustomWithReader() throws Exception
{
_testEscapeCustom(false); // reader
@@ -88,7 +88,40 @@
{
_testEscapeCustom(true); // stream (utf-8)
}
-
+
+ public void testJsonpEscapes() throws Exception {
+ _testJsonpEscapes(false);
+ _testJsonpEscapes(true);
+ }
+
+ @SuppressWarnings("resource")
+ private void _testJsonpEscapes(boolean useStream) throws Exception
+ {
+ JsonFactory f = new JsonFactory();
+ f.setCharacterEscapes(JsonpCharacterEscapes.instance());
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ JsonGenerator g;
+
+ // First: output normally; should not add escaping
+ if (useStream) {
+ g = f.createGenerator(bytes, JsonEncoding.UTF8);
+ } else {
+ g = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8"));
+ }
+ final String VALUE_TEMPLATE = "String with JS 'linefeeds': %s and %s...";
+ final String INPUT_VALUE = String.format(VALUE_TEMPLATE, "\u2028", "\u2029");
+
+ g.writeStartArray();
+ g.writeString(INPUT_VALUE);
+ g.writeEndArray();
+ g.close();
+
+ String json = bytes.toString("UTF-8");
+ assertEquals(String.format("[%s]",
+ quote(String.format(VALUE_TEMPLATE, "\\u2028", "\\u2029"))),
+ json);
+ }
+
/*
/********************************************************
/* Secondary test methods
@@ -102,18 +135,18 @@
final String VALUE = "chars: [\u00A0]/[\u1234]";
final String KEY = "fun:\u0088:\u3456";
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- JsonGenerator jgen;
+ JsonGenerator g;
// First: output normally; should not add escaping
if (useStream) {
- jgen = f.createGenerator(bytes, JsonEncoding.UTF8);
+ g = f.createGenerator(bytes, JsonEncoding.UTF8);
} else {
- jgen = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8"));
+ g = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8"));
}
- jgen.writeStartArray();
- jgen.writeString(VALUE);
- jgen.writeEndArray();
- jgen.close();
+ g.writeStartArray();
+ g.writeString(VALUE);
+ g.writeEndArray();
+ g.close();
String json = bytes.toString("UTF-8");
assertEquals("["+quote(VALUE)+"]", json);
@@ -122,31 +155,31 @@
bytes = new ByteArrayOutputStream();
if (useStream) {
- jgen = f.createGenerator(bytes, JsonEncoding.UTF8);
+ g = f.createGenerator(bytes, JsonEncoding.UTF8);
} else {
- jgen = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8"));
+ g = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8"));
}
- jgen.enable(JsonGenerator.Feature.ESCAPE_NON_ASCII);
- jgen.writeStartArray();
- jgen.writeString(VALUE);
- jgen.writeEndArray();
- jgen.close();
+ g.enable(JsonGenerator.Feature.ESCAPE_NON_ASCII);
+ g.writeStartArray();
+ g.writeString(VALUE);
+ g.writeEndArray();
+ g.close();
json = bytes.toString("UTF-8");
assertEquals("["+quote("chars: [\\u00A0]/[\\u1234]")+"]", json);
// and then keys
bytes = new ByteArrayOutputStream();
if (useStream) {
- jgen = f.createGenerator(bytes, JsonEncoding.UTF8);
+ g = f.createGenerator(bytes, JsonEncoding.UTF8);
} else {
- jgen = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8"));
+ g = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8"));
}
- jgen.enable(JsonGenerator.Feature.ESCAPE_NON_ASCII);
- jgen.writeStartObject();
- jgen.writeFieldName(KEY);
- jgen.writeBoolean(true);
- jgen.writeEndObject();
- jgen.close();
+ g.enable(JsonGenerator.Feature.ESCAPE_NON_ASCII);
+ g.writeStartObject();
+ g.writeFieldName(KEY);
+ g.writeBoolean(true);
+ g.writeEndObject();
+ g.close();
json = bytes.toString("UTF-8");
assertEquals("{"+quote("fun:\\u0088:\\u3456")+":true}", json);
}
@@ -158,18 +191,18 @@
final String STR_IN = "[abcd/"+((char) TWO_BYTE_ESCAPED)+"/"+((char) THREE_BYTE_ESCAPED)+"]";
final String STR_OUT = "[\\A\\u0062c[D]/"+TWO_BYTE_ESCAPED_STRING+"/"+THREE_BYTE_ESCAPED_STRING+"]";
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- JsonGenerator jgen;
+ JsonGenerator g;
// First: output normally; should not add escaping
if (useStream) {
- jgen = f.createGenerator(bytes, JsonEncoding.UTF8);
+ g = f.createGenerator(bytes, JsonEncoding.UTF8);
} else {
- jgen = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8"));
+ g = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8"));
}
- jgen.writeStartObject();
- jgen.writeStringField(STR_IN, STR_IN);
- jgen.writeEndObject();
- jgen.close();
+ g.writeStartObject();
+ g.writeStringField(STR_IN, STR_IN);
+ g.writeEndObject();
+ g.close();
String json = bytes.toString("UTF-8");
assertEquals("{"+quote(STR_OUT)+":"+quote(STR_OUT)+"}", json);
}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestJsonGeneratorFeatures.java b/src/test/java/com/fasterxml/jackson/core/json/TestJsonGeneratorFeatures.java
deleted file mode 100644
index d2f6c28..0000000
--- a/src/test/java/com/fasterxml/jackson/core/json/TestJsonGeneratorFeatures.java
+++ /dev/null
@@ -1,278 +0,0 @@
-package com.fasterxml.jackson.core.json;
-
-import java.io.*;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-
-import com.fasterxml.jackson.core.*;
-
-/**
- * Set of basic unit tests for verifying that the basic generator
- * functionality works as expected.
- */
-public class TestJsonGeneratorFeatures
- extends com.fasterxml.jackson.core.BaseTest
-{
- private final JsonFactory JSON_F = new JsonFactory();
-
- public void testConfigDefaults() throws IOException
- {
- JsonGenerator g = JSON_F.createGenerator(new StringWriter());
- assertFalse(g.isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS));
- assertFalse(g.isEnabled(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN));
- g.close();
- }
-
- public void testFieldNameQuoting() throws IOException
- {
- JsonFactory jf = new JsonFactory();
- // by default, quoting should be enabled
- _testFieldNameQuoting(jf, true);
- // can disable it
- jf.disable(JsonGenerator.Feature.QUOTE_FIELD_NAMES);
- _testFieldNameQuoting(jf, false);
- // and (re)enable:
- jf.enable(JsonGenerator.Feature.QUOTE_FIELD_NAMES);
- _testFieldNameQuoting(jf, true);
- }
-
- public void testNonNumericQuoting() throws IOException
- {
- JsonFactory jf = new JsonFactory();
- // by default, quoting should be enabled
- _testNonNumericQuoting(jf, true);
- // can disable it
- jf.disable(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS);
- _testNonNumericQuoting(jf, false);
- // and (re)enable:
- jf.enable(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS);
- _testNonNumericQuoting(jf, true);
- }
-
- /**
- * Testing for [JACKSON-176], ability to force serializing numbers
- * as JSON Strings.
- */
- public void testNumbersAsJSONStrings() throws IOException
- {
- JsonFactory jf = new JsonFactory();
- // by default should output numbers as-is:
- assertEquals("[1,2,1.25,2.25,3001,0.5,-1]", _writeNumbers(jf));
-
- // but if overridden, quotes as Strings
- jf.configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, true);
- assertEquals("[\"1\",\"2\",\"1.25\",\"2.25\",\"3001\",\"0.5\",\"-1\"]",
- _writeNumbers(jf));
- }
-
- // [core#85]
- public void testBigDecimalAsPlain() throws IOException
- {
- JsonFactory jf = new JsonFactory();
- BigDecimal ENG = new BigDecimal("1E+2");
-
- StringWriter sw = new StringWriter();
- JsonGenerator jg = jf.createGenerator(sw);
- jg.writeNumber(ENG);
- jg.close();
- assertEquals("1E+2", sw.toString());
-
- jf.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true);
- sw = new StringWriter();
- jg = jf.createGenerator(sw);
- jg.writeNumber(ENG);
- jg.close();
- assertEquals("100", sw.toString());
- }
-
- // [core#184]
- public void testBigDecimalAsPlainString() throws Exception
- {
- JsonFactory jf = new JsonFactory();
- BigDecimal ENG = new BigDecimal("1E+2");
- jf.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
- jf.enable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);
-
- StringWriter sw = new StringWriter();
- JsonGenerator jg = jf.createGenerator(sw);
- jg.writeNumber(ENG);
- jg.close();
- assertEquals(quote("100"), sw.toString());
-
- // also, as bytes
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- jg = jf.createGenerator(bos);
- jg.writeNumber(ENG);
- jg.close();
- assertEquals(quote("100"), bos.toString("UTF-8"));
- }
-
- // [core#315]
- public void testTooBigBigDecimal() throws Exception
- {
- JsonFactory f = new JsonFactory();
- f.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
-
- // 24-Aug-2016, tatu: Initial check limits scale to [-9999,+9999]
- BigDecimal BIG = new BigDecimal("1E+9999");
- BigDecimal TOO_BIG = new BigDecimal("1E+10000");
- BigDecimal SMALL = new BigDecimal("1E-9999");
- BigDecimal TOO_SMALL = new BigDecimal("1E-10000");
-
- for (boolean useBytes : new boolean[] { false, true } ) {
- for (boolean asString : new boolean[] { false, true } ) {
- JsonGenerator g;
-
- if (useBytes) {
- g = f.createGenerator(new ByteArrayOutputStream());
- } else {
- g = f.createGenerator(new StringWriter());
- }
- if (asString) {
- g.enable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);
- }
-
- // first, ok cases:
- g.writeStartArray();
- g.writeNumber(BIG);
- g.writeNumber(SMALL);
- g.writeEndArray();
- g.close();
-
- // then invalid
- for (BigDecimal input : new BigDecimal[] { TOO_BIG, TOO_SMALL }) {
- if (useBytes) {
- g = f.createGenerator(new ByteArrayOutputStream());
- } else {
- g = f.createGenerator(new StringWriter());
- }
- if (asString) {
- g.enable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);
- }
- try {
- g.writeNumber(input);
- fail("Should not have written without exception: "+input);
- } catch (JsonGenerationException e) {
- verifyException(e, "Attempt to write plain `java.math.BigDecimal`");
- verifyException(e, "illegal scale");
- }
- g.close();
- }
- }
- }
- }
-
- private String _writeNumbers(JsonFactory jf) throws IOException
- {
- StringWriter sw = new StringWriter();
- JsonGenerator jg = jf.createGenerator(sw);
-
- jg.writeStartArray();
- jg.writeNumber(1);
- jg.writeNumber(2L);
- jg.writeNumber(1.25);
- jg.writeNumber(2.25f);
- jg.writeNumber(BigInteger.valueOf(3001));
- jg.writeNumber(BigDecimal.valueOf(0.5));
- jg.writeNumber("-1");
- jg.writeEndArray();
- jg.close();
-
- return sw.toString();
- }
-
- // for [core#246]
- public void testFieldNameQuotingEnabled() throws IOException
- {
- // // First, test with default factory, with quoting enabled by default
-
- // First, default, with quotes
- _testFieldNameQuotingEnabled(JSON_F, true, true, "{\"foo\":1}");
- _testFieldNameQuotingEnabled(JSON_F, false, true, "{\"foo\":1}");
-
- // then without quotes
- _testFieldNameQuotingEnabled(JSON_F, true, false, "{foo:1}");
- _testFieldNameQuotingEnabled(JSON_F, false, false, "{foo:1}");
-
- // // Then with alternatively configured factory
-
- JsonFactory JF2 = new JsonFactory();
- JF2.disable(JsonGenerator.Feature.QUOTE_FIELD_NAMES);
-
- _testFieldNameQuotingEnabled(JF2, true, true, "{\"foo\":1}");
- _testFieldNameQuotingEnabled(JF2, false, true, "{\"foo\":1}");
-
- // then without quotes
- _testFieldNameQuotingEnabled(JF2, true, false, "{foo:1}");
- _testFieldNameQuotingEnabled(JF2, false, false, "{foo:1}");
- }
-
- private void _testFieldNameQuotingEnabled(JsonFactory jf, boolean useBytes,
- boolean useQuotes, String exp) throws IOException
- {
- ByteArrayOutputStream bytes = useBytes ? new ByteArrayOutputStream() : null;
- StringWriter sw = useBytes ? null : new StringWriter();
- JsonGenerator gen = useBytes ? jf.createGenerator(bytes) : jf.createGenerator(sw);
- if (useQuotes) {
- gen.enable(JsonGenerator.Feature.QUOTE_FIELD_NAMES);
- } else {
- gen.disable(JsonGenerator.Feature.QUOTE_FIELD_NAMES);
- }
-
- gen.writeStartObject();
- gen.writeFieldName("foo");
- gen.writeNumber(1);
- gen.writeEndObject();
- gen.close();
-
- String json = useBytes ? bytes.toString("UTF-8") : sw.toString();
- assertEquals(exp, json);
- }
-
- /*
- /**********************************************************
- /* Helper methods
- /**********************************************************
- */
-
- private void _testFieldNameQuoting(JsonFactory jf, boolean quoted)
- throws IOException
- {
- StringWriter sw = new StringWriter();
- JsonGenerator jg = jf.createGenerator(sw);
- jg.writeStartObject();
- jg.writeFieldName("foo");
- jg.writeNumber(1);
- jg.writeEndObject();
- jg.close();
-
- String result = sw.toString();
- if (quoted) {
- assertEquals("{\"foo\":1}", result);
- } else {
- assertEquals("{foo:1}", result);
- }
- }
- private void _testNonNumericQuoting(JsonFactory jf, boolean quoted)
- throws IOException
- {
- StringWriter sw = new StringWriter();
- JsonGenerator jg = jf.createGenerator(sw);
- jg.writeStartObject();
- jg.writeFieldName("double");
- jg.writeNumber(Double.NaN);
- jg.writeEndObject();
- jg.writeStartObject();
- jg.writeFieldName("float");
- jg.writeNumber(Float.NaN);
- jg.writeEndObject();
- jg.close();
-
- String result = sw.toString();
- if (quoted) {
- assertEquals("{\"double\":\"NaN\"} {\"float\":\"NaN\"}", result);
- } else {
- assertEquals("{\"double\":NaN} {\"float\":NaN}", result);
- }
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestJsonParser.java b/src/test/java/com/fasterxml/jackson/core/json/TestJsonParser.java
deleted file mode 100644
index d4dead5..0000000
--- a/src/test/java/com/fasterxml/jackson/core/json/TestJsonParser.java
+++ /dev/null
@@ -1,609 +0,0 @@
-package com.fasterxml.jackson.core.json;
-
-import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.core.util.JsonParserDelegate;
-
-import java.io.*;
-import java.net.URL;
-import java.util.*;
-
-/**
- * Set of basic unit tests for verifying that the basic parser
- * functionality works as expected.
- */
-public class TestJsonParser
- extends com.fasterxml.jackson.core.BaseTest
-{
- private final JsonFactory JSON_FACTORY = new JsonFactory();
-
- public void testConfig() throws Exception
- {
- JsonParser jp = createParserUsingReader("[ ]");
- jp.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
- assertTrue(jp.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE));
- jp.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
- assertFalse(jp.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE));
-
- jp.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true);
- assertTrue(jp.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE));
- jp.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false);
- assertFalse(jp.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE));
- jp.close();
- }
-
- public void testInterningWithStreams() throws Exception
- {
- _testIntern(true, true, "a");
- _testIntern(true, false, "b");
- }
-
- public void testInterningWithReaders() throws Exception
- {
- _testIntern(false, true, "c");
- _testIntern(false, false, "d");
- }
-
- private void _testIntern(boolean useStream, boolean enableIntern, String expName) throws IOException
- {
- JsonFactory f = new JsonFactory();
- f.configure(JsonFactory.Feature.INTERN_FIELD_NAMES, enableIntern);
- assertEquals(enableIntern, f.isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES));
- final String JSON = "{ \""+expName+"\" : 1}";
- JsonParser jp = useStream ?
- createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON);
-
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- // needs to be same of cours
- String actName = jp.getCurrentName();
- assertEquals(expName, actName);
- if (enableIntern) {
- assertSame(expName, actName);
- } else {
- assertNotSame(expName, actName);
- }
- jp.close();
- }
-
- /**
- * This basic unit test verifies that example given in the Json
- * specification (RFC-4627 or later) is properly parsed at
- * high-level, without verifying values.
- */
- public void testSpecExampleSkipping()
- throws Exception
- {
- doTestSpec(false);
- }
-
- /**
- * Unit test that verifies that the spec example JSON is completely
- * parsed, and proper values are given for contents of all
- * events/tokens.
- */
- public void testSpecExampleFully()
- throws Exception
- {
- doTestSpec(true);
- }
-
- /**
- * Unit test that verifies that 3 basic keywords (null, true, false)
- * are properly parsed in various contexts.
- */
- public void testKeywords()
- throws Exception
- {
- final String DOC = "{\n"
- +"\"key1\" : null,\n"
- +"\"key2\" : true,\n"
- +"\"key3\" : false,\n"
- +"\"key4\" : [ false, null, true ]\n"
- +"}"
- ;
-
- JsonParser jp = createParserUsingStream(DOC, "UTF-8");
-
- JsonStreamContext ctxt = jp.getParsingContext();
- assertTrue(ctxt.inRoot());
- assertFalse(ctxt.inArray());
- assertFalse(ctxt.inObject());
- assertEquals(0, ctxt.getEntryCount());
- assertEquals(0, ctxt.getCurrentIndex());
-
- /* Before advancing to content, we should have following
- * default state...
- */
- assertFalse(jp.hasCurrentToken());
- assertNull(jp.getText());
- assertNull(jp.getTextCharacters());
- assertEquals(0, jp.getTextLength());
- // not sure if this is defined but:
- assertEquals(0, jp.getTextOffset());
-
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
-
- assertTrue(jp.hasCurrentToken());
- JsonLocation loc = jp.getTokenLocation();
- assertNotNull(loc);
- assertEquals(1, loc.getLineNr());
- assertEquals(1, loc.getColumnNr());
-
- ctxt = jp.getParsingContext();
- assertFalse(ctxt.inRoot());
- assertFalse(ctxt.inArray());
- assertTrue(ctxt.inObject());
- assertEquals(0, ctxt.getEntryCount());
- assertEquals(0, ctxt.getCurrentIndex());
-
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- verifyFieldName(jp, "key1");
- assertEquals(2, jp.getTokenLocation().getLineNr());
-
- ctxt = jp.getParsingContext();
- assertFalse(ctxt.inRoot());
- assertFalse(ctxt.inArray());
- assertTrue(ctxt.inObject());
- assertEquals(1, ctxt.getEntryCount());
- assertEquals(0, ctxt.getCurrentIndex());
- assertEquals("key1", ctxt.getCurrentName());
-
- assertToken(JsonToken.VALUE_NULL, jp.nextToken());
- assertEquals("key1", ctxt.getCurrentName());
-
- ctxt = jp.getParsingContext();
- assertEquals(1, ctxt.getEntryCount());
- assertEquals(0, ctxt.getCurrentIndex());
-
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- verifyFieldName(jp, "key2");
- ctxt = jp.getParsingContext();
- assertEquals(2, ctxt.getEntryCount());
- assertEquals(1, ctxt.getCurrentIndex());
- assertEquals("key2", ctxt.getCurrentName());
-
- assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
- assertEquals("key2", ctxt.getCurrentName());
-
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- verifyFieldName(jp, "key3");
- assertToken(JsonToken.VALUE_FALSE, jp.nextToken());
-
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- verifyFieldName(jp, "key4");
-
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- ctxt = jp.getParsingContext();
- assertTrue(ctxt.inArray());
- assertNull(ctxt.getCurrentName());
- assertEquals("key4", ctxt.getParent().getCurrentName());
-
- assertToken(JsonToken.VALUE_FALSE, jp.nextToken());
- assertToken(JsonToken.VALUE_NULL, jp.nextToken());
- assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
-
- ctxt = jp.getParsingContext();
- assertTrue(ctxt.inObject());
-
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- ctxt = jp.getParsingContext();
- assertTrue(ctxt.inRoot());
- assertNull(ctxt.getCurrentName());
-
- jp.close();
- }
-
- public void testSkipping() throws Exception
- {
- String DOC =
- "[ 1, 3, [ true, null ], 3, { \"a\":\"b\" }, [ [ ] ], { } ]";
- ;
- JsonParser jp = createParserUsingStream(DOC, "UTF-8");
-
- // First, skipping of the whole thing
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- jp.skipChildren();
- assertEquals(JsonToken.END_ARRAY, jp.getCurrentToken());
- JsonToken t = jp.nextToken();
- if (t != null) {
- fail("Expected null at end of doc, got "+t);
- }
- jp.close();
-
- // Then individual ones
- jp = createParserUsingStream(DOC, "UTF-8");
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
-
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- jp.skipChildren();
- // shouldn't move
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.getCurrentToken());
- assertEquals(1, jp.getIntValue());
-
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- // then skip array
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- jp.skipChildren();
- assertToken(JsonToken.END_ARRAY, jp.getCurrentToken());
-
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- jp.skipChildren();
- assertToken(JsonToken.END_OBJECT, jp.getCurrentToken());
-
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- jp.skipChildren();
- assertToken(JsonToken.END_ARRAY, jp.getCurrentToken());
-
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- jp.skipChildren();
- assertToken(JsonToken.END_OBJECT, jp.getCurrentToken());
-
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
-
- jp.close();
- }
-
- public void testNameEscaping() throws IOException
- {
- _testNameEscaping(false);
- _testNameEscaping(true);
- }
-
- private void _testNameEscaping(boolean useStream) throws IOException
- {
- final Map<String,String> NAME_MAP = new LinkedHashMap<String,String>();
- NAME_MAP.put("", "");
- NAME_MAP.put("\\\"funny\\\"", "\"funny\"");
- NAME_MAP.put("\\\\", "\\");
- NAME_MAP.put("\\r", "\r");
- NAME_MAP.put("\\n", "\n");
- NAME_MAP.put("\\t", "\t");
- NAME_MAP.put("\\r\\n", "\r\n");
- NAME_MAP.put("\\\"\\\"", "\"\"");
- NAME_MAP.put("Line\\nfeed", "Line\nfeed");
- NAME_MAP.put("Yet even longer \\\"name\\\"!", "Yet even longer \"name\"!");
-
- int entry = 0;
- for (Map.Entry<String,String> en : NAME_MAP.entrySet()) {
- ++entry;
- String input = en.getKey();
- String expResult = en.getValue();
- final String DOC = "{ \""+input+"\":null}";
- JsonParser jp = useStream ?
- JSON_FACTORY.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")))
- : JSON_FACTORY.createParser(new StringReader(DOC));
-
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- // first, sanity check (field name == getText()
- String act = jp.getCurrentName();
- assertEquals(act, getAndVerifyText(jp));
- if (!expResult.equals(act)) {
- String msg = "Failed for name #"+entry+"/"+NAME_MAP.size();
- if (expResult.length() != act.length()) {
- fail(msg+": exp length "+expResult.length()+", actual "+act.length());
- }
- assertEquals(msg, expResult, act);
- }
- assertToken(JsonToken.VALUE_NULL, jp.nextToken());
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- jp.close();
- }
- }
-
- /**
- * Unit test that verifies that long text segments are handled
- * correctly; mostly to stress-test underlying segment-based
- * text buffer(s).
- */
- public void testLongText() throws Exception
- {
- // lengths chosen to tease out problems with buffer allocation...
- _testLongText(7700);
- _testLongText(49000);
- _testLongText(96000);
- }
-
- @SuppressWarnings("resource")
- private void _testLongText(int LEN) throws Exception
- {
- StringBuilder sb = new StringBuilder(LEN + 100);
- Random r = new Random(99);
- while (sb.length() < LEN) {
- sb.append(r.nextInt());
- sb.append(" xyz foo");
- if (r.nextBoolean()) {
- sb.append(" and \"bar\"");
- } else if (r.nextBoolean()) {
- sb.append(" [whatever].... ");
- } else {
- // Let's try some more 'exotic' chars
- sb.append(" UTF-8-fu: try this {\u00E2/\u0BF8/\uA123!} (look funny?)");
- }
- if (r.nextBoolean()) {
- if (r.nextBoolean()) {
- sb.append('\n');
- } else if (r.nextBoolean()) {
- sb.append('\r');
- } else {
- sb.append("\r\n");
- }
- }
- }
- final String VALUE = sb.toString();
-
- // Let's use real generator to get JSON done right
- StringWriter sw = new StringWriter(LEN + (LEN >> 2));
- JsonGenerator jg = JSON_FACTORY.createGenerator(sw);
- jg.writeStartObject();
- jg.writeFieldName("doc");
- jg.writeString(VALUE);
- jg.writeEndObject();
- jg.close();
-
- final String DOC = sw.toString();
-
- for (int type = 0; type < 3; ++type) {
- JsonParser jp;
-
- switch (type) {
- default:
- jp = JSON_FACTORY.createParser(DOC.getBytes("UTF-8"));
- break;
- case 1:
- jp = JSON_FACTORY.createParser(DOC);
- break;
- case 2: // NEW: let's also exercise UTF-32...
- jp = JSON_FACTORY.createParser(encodeInUTF32BE(DOC));
- break;
- }
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("doc", jp.getCurrentName());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
-
- String act = getAndVerifyText(jp);
- if (act.length() != VALUE.length()) {
- fail("Expected length "+VALUE.length()+", got "+act.length());
- }
- if (!act.equals(VALUE)) {
- fail("Long text differs");
- }
-
- // should still know the field name
- assertEquals("doc", jp.getCurrentName());
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- assertNull(jp.nextToken());
- jp.close();
- }
- }
-
- /**
- * Simple unit test that verifies that passing in a byte array
- * as source works as expected.
- */
- public void testBytesAsSource() throws Exception
- {
- String JSON = "[ 1, 2, 3, 4 ]";
- byte[] b = JSON.getBytes("UTF-8");
- int offset = 50;
- int len = b.length;
- byte[] src = new byte[offset + len + offset];
-
- System.arraycopy(b, 0, src, offset, len);
-
- JsonParser jp = JSON_FACTORY.createParser(src, offset, len);
-
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(1, jp.getIntValue());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(2, jp.getIntValue());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(3, jp.getIntValue());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(4, jp.getIntValue());
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- assertNull(jp.nextToken());
-
- jp.close();
- }
-
- // [JACKSON-632]
- public void testUtf8BOMHandling() throws Exception
- {
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- // first, write BOM:
- bytes.write(0xEF);
- bytes.write(0xBB);
- bytes.write(0xBF);
- bytes.write("[ 1 ]".getBytes("UTF-8"));
- JsonParser jp = JSON_FACTORY.createParser(bytes.toByteArray());
- assertEquals(JsonToken.START_ARRAY, jp.nextToken());
- // should also have skipped first 3 bytes of BOM; but do we have offset available?
- /* 08-Oct-2013, tatu: Alas, due to [Issue#111], we have to omit BOM in calculations
- * as we do not know what the offset is due to -- may need to revisit, if this
- * discrepancy becomes an issue. For now it just means that BOM is considered
- * "out of stream" (not part of input).
- */
- JsonLocation loc = jp.getTokenLocation();
- // so if BOM was consider in-stream (part of input), this should expect 3:
- assertEquals(0, loc.getByteOffset());
- assertEquals(-1, loc.getCharOffset());
- jp.close();
- }
-
- // [Issue#48]
- public void testSpacesInURL() throws Exception
- {
- File f = File.createTempFile("pre fix&stuff", ".txt");
- BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f), "UTF-8"));
- w.write("{ }");
- w.close();
- URL url = f.toURI().toURL();
-
- JsonParser jp = JSON_FACTORY.createParser(url);
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- jp.close();
- }
-
- // [#142]
- public void testHandlingOfInvalidSpaceBytes() throws Exception {
- _testHandlingOfInvalidSpace(true);
- _testHandlingOfInvalidSpaceFromResource(true);
- }
-
- // [#142]
- public void testHandlingOfInvalidSpaceChars() throws Exception {
- _testHandlingOfInvalidSpace(false);
- _testHandlingOfInvalidSpaceFromResource(false);
- }
-
- private void _testHandlingOfInvalidSpace(boolean useStream) throws Exception
- {
- final String JSON = "{ \u00A0 \"a\":1}";
- JsonParser jp = useStream
- ? createParserUsingStream(JSON_FACTORY, JSON, "UTF-8")
- : createParserUsingReader(JSON_FACTORY, JSON);
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- try {
- jp.nextToken();
- fail("Should have failed");
- } catch (JsonParseException e) {
- verifyException(e, "unexpected character");
- // and correct error code
- verifyException(e, "code 160");
- }
- jp.close();
- }
-
- private void _testHandlingOfInvalidSpaceFromResource(boolean useStream) throws Exception
- {
- InputStream in = getClass().getResourceAsStream("/test_0xA0.json");
- @SuppressWarnings("resource")
- JsonParser jp = useStream
- ? JSON_FACTORY.createParser(in)
- : JSON_FACTORY.createParser(new InputStreamReader(in, "UTF-8"));
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- try {
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("request", jp.getCurrentName());
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("mac", jp.getCurrentName());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertNotNull(jp.getText());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("data", jp.getCurrentName());
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
-
- // ... and from there on, just loop
-
- while (jp.nextToken() != null) { }
- fail("Should have failed");
- } catch (JsonParseException e) {
- verifyException(e, "unexpected character");
- // and correct error code
- verifyException(e, "code 160");
- }
- jp.close();
- }
-
- public void testGetValueAsTextBytes() throws Exception
- {
- JsonFactory f = new JsonFactory();
- _testGetValueAsText(f, true, false);
- _testGetValueAsText(f, true, true);
- }
-
- public void testGetValueAsTextChars() throws Exception
- {
- JsonFactory f = new JsonFactory();
- _testGetValueAsText(f, false, false);
- _testGetValueAsText(f, false, true);
- }
-
- @SuppressWarnings("resource")
- private void _testGetValueAsText(JsonFactory f,
- boolean useBytes, boolean delegate) throws Exception
- {
- String JSON = "{\"a\":1,\"b\":true,\"c\":null,\"d\":\"foo\"}";
- JsonParser p = useBytes ? f.createParser(JSON.getBytes("UTF-8"))
- : f.createParser(JSON);
-
- if (delegate) {
- p = new JsonParserDelegate(p);
- }
-
- assertToken(JsonToken.START_OBJECT, p.nextToken());
- assertNull(p.getValueAsString());
-
- assertToken(JsonToken.FIELD_NAME, p.nextToken());
- assertEquals("a", p.getText());
- assertEquals("a", p.getValueAsString());
- assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
- assertEquals("1", p.getValueAsString());
-
- assertToken(JsonToken.FIELD_NAME, p.nextToken());
- assertEquals("b", p.getValueAsString());
- assertToken(JsonToken.VALUE_TRUE, p.nextToken());
- assertEquals("true", p.getValueAsString());
-
- assertToken(JsonToken.FIELD_NAME, p.nextToken());
- assertEquals("c", p.getValueAsString());
- assertToken(JsonToken.VALUE_NULL, p.nextToken());
- // null token returned as Java null, as per javadoc
- assertNull(p.getValueAsString());
-
- assertToken(JsonToken.FIELD_NAME, p.nextToken());
- assertEquals("d", p.getValueAsString());
- assertToken(JsonToken.VALUE_STRING, p.nextToken());
- assertEquals("foo", p.getValueAsString());
-
- assertToken(JsonToken.END_OBJECT, p.nextToken());
- assertNull(p.getValueAsString());
-
- assertNull(p.nextToken());
- p.close();
- }
-
- /*
- /**********************************************************
- /* Helper methods
- /**********************************************************
- */
-
- private void doTestSpec(boolean verify) throws IOException
- {
- // First, using a StringReader:
- doTestSpecIndividual(null, verify);
-
- // Then with streams using supported encodings:
- doTestSpecIndividual("UTF-8", verify);
- doTestSpecIndividual("UTF-16BE", verify);
- doTestSpecIndividual("UTF-16LE", verify);
-
- /* Hmmh. UTF-32 is harder only because JDK doesn't come with
- * a codec for it. Can't test it yet using this method
- */
- doTestSpecIndividual("UTF-32", verify);
- }
-
- private void doTestSpecIndividual(String enc, boolean verify) throws IOException
- {
- String doc = SAMPLE_DOC_JSON_SPEC;
- JsonParser jp;
-
- if (enc == null) {
- jp = createParserUsingReader(doc);
- } else {
- jp = createParserUsingStream(doc, enc);
- }
- verifyJsonSpecSampleDoc(jp, verify);
- jp.close();
- }
-}
-
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestNextXxx.java b/src/test/java/com/fasterxml/jackson/core/json/TestNextXxx.java
deleted file mode 100644
index 2aaeeb1..0000000
--- a/src/test/java/com/fasterxml/jackson/core/json/TestNextXxx.java
+++ /dev/null
@@ -1,526 +0,0 @@
-package com.fasterxml.jackson.core.json;
-
-import java.io.ByteArrayInputStream;
-import java.io.StringReader;
-import java.util.Random;
-
-import com.fasterxml.jackson.core.JsonFactory;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonToken;
-import com.fasterxml.jackson.core.SerializableString;
-import com.fasterxml.jackson.core.io.SerializedString;
-
-public class TestNextXxx
- extends com.fasterxml.jackson.core.BaseTest
-{
- /*
- /********************************************************
- /* Wrappers to test InputStream vs Reader
- /********************************************************
- */
-
- private final JsonFactory JSON_F = new JsonFactory();
-
- public void testIsNextTokenName() throws Exception
- {
- _testIsNextTokenName1(false);
- _testIsNextTokenName1(true);
- }
-
- public void testIsNextTokenName2() throws Exception {
- _testIsNextTokenName2(false);
- _testIsNextTokenName2(true);
- }
-
- public void testIsNextTokenName3() throws Exception {
- _testIsNextTokenName3(false);
- _testIsNextTokenName3(true);
- }
-
- public void testIsNextTokenName4() throws Exception {
- _testIsNextTokenName4(false);
- _testIsNextTokenName4(true);
- }
-
- // [jackson-core#34]
- public void testIssue34() throws Exception
- {
- _testIssue34(false);
- _testIssue34(true);
- }
-
- // [jackson-core#38] with nextFieldName
- public void testIssue38() throws Exception
- {
- _testIssue38(false);
- _testIssue38(true);
- }
-
- public void testNextNameWithLongContent() throws Exception
- {
- _testNextNameWithLong(false);
- _testNextNameWithLong(true);
- }
-
- // for [core#220]: problem with `nextFieldName(str)`, indented content
- public void testNextNameWithIndentation() throws Exception
- {
- _testNextFieldNameIndent(false);
- _testNextFieldNameIndent(true);
- }
-
- public void testNextTextValue() throws Exception
- {
- _textNextText(false);
- _textNextText(true);
- }
-
- public void testNextIntValue() throws Exception
- {
- _textNextInt(false);
- _textNextInt(true);
- }
-
- public void testNextLongValue() throws Exception
- {
- _textNextLong(false);
- _textNextLong(true);
- }
-
- public void testNextBooleanValue() throws Exception
- {
- _textNextBoolean(false);
- _textNextBoolean(true);
- }
-
- /*
- /********************************************************
- /* Actual test code
- /********************************************************
- */
-
- private void _testIsNextTokenName1(boolean useStream) throws Exception
- {
- final String DOC = "{\"name\":123,\"name2\":14,\"x\":\"name\"}";
- JsonFactory jf = new JsonFactory();
- JsonParser jp = useStream ?
- jf.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")))
- : jf.createParser(new StringReader(DOC));
- final SerializedString NAME = new SerializedString("name");
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.START_OBJECT, jp.getCurrentToken());
- assertTrue(jp.nextFieldName(NAME));
- assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken());
- assertEquals(NAME.getValue(), jp.getCurrentName());
- assertEquals(NAME.getValue(), jp.getText());
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.getCurrentToken());
- assertEquals(123, jp.getIntValue());
-
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken());
- assertEquals("name2", jp.getCurrentName());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- // do NOT check number value, to enforce skipping
-
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken());
- assertEquals("x", jp.getCurrentName());
-
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.VALUE_STRING, jp.getCurrentToken());
-
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.END_OBJECT, jp.getCurrentToken());
-
- assertFalse(jp.nextFieldName(NAME));
- assertNull(jp.getCurrentToken());
-
- jp.close();
-
- // Actually, try again with slightly different sequence...
- jp = useStream ? jf.createParser(DOC.getBytes("UTF-8"))
- : jf.createParser(DOC);
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- assertFalse(jp.nextFieldName(new SerializedString("Nam")));
- assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken());
- assertEquals(NAME.getValue(), jp.getCurrentName());
- assertEquals(NAME.getValue(), jp.getText());
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.getCurrentToken());
- assertEquals(123, jp.getIntValue());
-
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken());
- assertEquals("name2", jp.getCurrentName());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
-
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken());
- assertEquals("x", jp.getCurrentName());
-
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.VALUE_STRING, jp.getCurrentToken());
-
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.END_OBJECT, jp.getCurrentToken());
-
- assertFalse(jp.nextFieldName(NAME));
- assertNull(jp.getCurrentToken());
-
- jp.close();
- }
-
- private void _testIsNextTokenName2(boolean useStream) throws Exception
- {
- final String DOC = "{\"name\":123,\"name2\":14,\"x\":\"name\"}";
- JsonParser jp = useStream ?
- JSON_F.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")))
- : JSON_F.createParser(new StringReader(DOC));
- SerializableString NAME = new SerializedString("name");
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.START_OBJECT, jp.getCurrentToken());
- assertTrue(jp.nextFieldName(NAME));
- assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken());
- assertEquals(NAME.getValue(), jp.getCurrentName());
- assertEquals(NAME.getValue(), jp.getText());
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.getCurrentToken());
- assertEquals(123, jp.getIntValue());
-
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken());
- assertEquals("name2", jp.getCurrentName());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
-
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken());
- assertEquals("x", jp.getCurrentName());
-
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.VALUE_STRING, jp.getCurrentToken());
-
- assertFalse(jp.nextFieldName(NAME));
- assertToken(JsonToken.END_OBJECT, jp.getCurrentToken());
-
- assertFalse(jp.nextFieldName(NAME));
- assertNull(jp.getCurrentToken());
-
- jp.close();
- }
-
- private void _testIsNextTokenName3(boolean useStream) throws Exception
- {
- final String DOC = "{\"name\":123,\"name2\":14,\"x\":\"name\"}";
- JsonParser p = useStream ?
- JSON_F.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")))
- : JSON_F.createParser(new StringReader(DOC));
- assertNull(p.nextFieldName());
- assertToken(JsonToken.START_OBJECT, p.getCurrentToken());
- assertEquals("name", p.nextFieldName());
- assertToken(JsonToken.FIELD_NAME, p.getCurrentToken());
- assertEquals("name", p.getCurrentName());
- assertEquals("name", p.getText());
- assertNull(p.nextFieldName());
- assertToken(JsonToken.VALUE_NUMBER_INT, p.getCurrentToken());
- assertEquals(123, p.getIntValue());
-
- assertEquals("name2", p.nextFieldName());
- assertToken(JsonToken.FIELD_NAME, p.getCurrentToken());
- assertEquals("name2", p.getCurrentName());
- assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
-
- assertEquals("x", p.nextFieldName());
- assertToken(JsonToken.FIELD_NAME, p.getCurrentToken());
- assertEquals("x", p.getCurrentName());
-
- assertNull(p.nextFieldName());
- assertToken(JsonToken.VALUE_STRING, p.getCurrentToken());
-
- assertNull(p.nextFieldName());
- assertToken(JsonToken.END_OBJECT, p.getCurrentToken());
-
- assertNull(p.nextFieldName());
- assertNull(p.getCurrentToken());
-
- p.close();
- }
-
- private void _testIsNextTokenName4(boolean useStream) throws Exception
- {
- final String DOC = "{\"name\":-123,\"name2\":99}";
- JsonParser jp = useStream ?
- JSON_F.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")))
- : JSON_F.createParser(new StringReader(DOC));
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
-
- assertTrue(jp.nextFieldName(new SerializedString("name")));
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(-123, jp.getIntValue());
-
- assertTrue(jp.nextFieldName(new SerializedString("name2")));
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(99, jp.getIntValue());
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- assertNull(jp.nextToken());
-
- jp.close();
- }
-
- private void _testNextFieldNameIndent(boolean useStream) throws Exception
- {
- final String DOC = "{\n \"name\" : \n [\n ]\n }";
- JsonParser p = useStream ?
- JSON_F.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")))
- : JSON_F.createParser(new StringReader(DOC));
- assertToken(JsonToken.START_OBJECT, p.nextToken());
- assertTrue(p.nextFieldName(new SerializedString("name")));
-
- assertToken(JsonToken.START_ARRAY, p.nextToken());
- assertToken(JsonToken.END_ARRAY, p.nextToken());
- assertToken(JsonToken.END_OBJECT, p.nextToken());
-
- assertNull(p.nextToken());
-
- p.close();
- }
-
- private void _textNextText(boolean useStream) throws Exception
- {
- final String DOC = aposToQuotes("{'a':'123','b':5,'c':[false,'foo']}");
- JsonParser p = useStream ?
- JSON_F.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")))
- : JSON_F.createParser(new StringReader(DOC));
- assertNull(p.nextTextValue());
- assertToken(JsonToken.START_OBJECT, p.getCurrentToken());
- assertNull(p.nextTextValue());
- assertToken(JsonToken.FIELD_NAME, p.getCurrentToken());
- assertEquals("a", p.getCurrentName());
-
- assertEquals("123", p.nextTextValue());
- assertToken(JsonToken.FIELD_NAME, p.nextToken());
- assertEquals("b", p.getCurrentName());
- assertNull(p.nextFieldName());
- assertToken(JsonToken.VALUE_NUMBER_INT, p.getCurrentToken());
-
- assertEquals("c", p.nextFieldName());
-
- assertNull(p.nextTextValue());
- assertToken(JsonToken.START_ARRAY, p.getCurrentToken());
- assertNull(p.nextTextValue());
- assertToken(JsonToken.VALUE_FALSE, p.getCurrentToken());
- assertEquals("foo", p.nextTextValue());
-
- assertNull(p.nextTextValue());
- assertToken(JsonToken.END_ARRAY, p.getCurrentToken());
- assertNull(p.nextTextValue());
- assertToken(JsonToken.END_OBJECT, p.getCurrentToken());
- assertNull(p.nextTextValue());
- assertNull(p.getCurrentToken());
-
- p.close();
- }
-
- private void _textNextInt(boolean useStream) throws Exception
- {
- final String DOC = aposToQuotes("{'a':'123','b':5,'c':[false,456]}");
- JsonParser p = useStream ?
- JSON_F.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")))
- : JSON_F.createParser(new StringReader(DOC));
- assertEquals(0, p.nextIntValue(0));
- assertToken(JsonToken.START_OBJECT, p.getCurrentToken());
- assertEquals(0, p.nextIntValue(0));
- assertToken(JsonToken.FIELD_NAME, p.getCurrentToken());
- assertEquals("a", p.getCurrentName());
-
- assertEquals(0, p.nextIntValue(0));
- assertToken(JsonToken.VALUE_STRING, p.getCurrentToken());
- assertEquals("123", p.getText());
- assertToken(JsonToken.FIELD_NAME, p.nextToken());
- assertEquals("b", p.getCurrentName());
- assertEquals(5, p.nextIntValue(0));
-
- assertEquals("c", p.nextFieldName());
-
- assertEquals(0, p.nextIntValue(0));
- assertToken(JsonToken.START_ARRAY, p.getCurrentToken());
- assertEquals(0, p.nextIntValue(0));
- assertToken(JsonToken.VALUE_FALSE, p.getCurrentToken());
- assertEquals(456, p.nextIntValue(0));
-
- assertEquals(0, p.nextIntValue(0));
- assertToken(JsonToken.END_ARRAY, p.getCurrentToken());
- assertEquals(0, p.nextIntValue(0));
- assertToken(JsonToken.END_OBJECT, p.getCurrentToken());
- assertEquals(0, p.nextIntValue(0));
- assertNull(p.getCurrentToken());
-
- p.close();
- }
-
- private void _textNextLong(boolean useStream) throws Exception
- {
- final String DOC = aposToQuotes("{'a':'xyz','b':-59,'c':[false,-1]}");
- JsonParser p = useStream ?
- JSON_F.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")))
- : JSON_F.createParser(new StringReader(DOC));
- assertEquals(0L, p.nextLongValue(0L));
- assertToken(JsonToken.START_OBJECT, p.getCurrentToken());
- assertEquals(0L, p.nextLongValue(0L));
- assertToken(JsonToken.FIELD_NAME, p.getCurrentToken());
- assertEquals("a", p.getCurrentName());
-
- assertEquals(0L, p.nextLongValue(0L));
- assertToken(JsonToken.VALUE_STRING, p.getCurrentToken());
- assertEquals("xyz", p.getText());
- assertToken(JsonToken.FIELD_NAME, p.nextToken());
- assertEquals("b", p.getCurrentName());
- assertEquals(-59L, p.nextLongValue(0L));
-
- assertEquals("c", p.nextFieldName());
-
- assertEquals(0L, p.nextLongValue(0L));
- assertToken(JsonToken.START_ARRAY, p.getCurrentToken());
- assertEquals(0L, p.nextLongValue(0L));
- assertToken(JsonToken.VALUE_FALSE, p.getCurrentToken());
- assertEquals(-1L, p.nextLongValue(0L));
-
- assertEquals(0L, p.nextLongValue(0L));
- assertToken(JsonToken.END_ARRAY, p.getCurrentToken());
- assertEquals(0L, p.nextLongValue(0L));
- assertToken(JsonToken.END_OBJECT, p.getCurrentToken());
- assertEquals(0L, p.nextLongValue(0L));
- assertNull(p.getCurrentToken());
-
- p.close();
- }
-
- private void _textNextBoolean(boolean useStream) throws Exception
- {
- final String DOC = aposToQuotes("{'a':'xyz','b':true,'c':[false,0]}");
- JsonParser p = useStream ?
- JSON_F.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")))
- : JSON_F.createParser(new StringReader(DOC));
- assertNull(p.nextBooleanValue());
- assertToken(JsonToken.START_OBJECT, p.getCurrentToken());
- assertNull(p.nextBooleanValue());
- assertToken(JsonToken.FIELD_NAME, p.getCurrentToken());
- assertEquals("a", p.getCurrentName());
-
- assertNull(p.nextBooleanValue());
- assertToken(JsonToken.VALUE_STRING, p.getCurrentToken());
- assertEquals("xyz", p.getText());
- assertToken(JsonToken.FIELD_NAME, p.nextToken());
- assertEquals("b", p.getCurrentName());
- assertEquals(Boolean.TRUE, p.nextBooleanValue());
-
- assertEquals("c", p.nextFieldName());
-
- assertNull(p.nextBooleanValue());
- assertToken(JsonToken.START_ARRAY, p.getCurrentToken());
- assertEquals(Boolean.FALSE, p.nextBooleanValue());
- assertNull(p.nextBooleanValue());
- assertToken(JsonToken.VALUE_NUMBER_INT, p.getCurrentToken());
- assertEquals(0, p.getIntValue());
-
- assertNull(p.nextBooleanValue());
- assertToken(JsonToken.END_ARRAY, p.getCurrentToken());
- assertNull(p.nextBooleanValue());
- assertToken(JsonToken.END_OBJECT, p.getCurrentToken());
- assertNull(p.nextBooleanValue());
- assertNull(p.getCurrentToken());
-
- p.close();
- }
-
- private void _testIssue34(boolean useStream) throws Exception
- {
- final int TESTROUNDS = 223;
- final String DOC_PART = "{ \"fieldName\": 1 }";
-
- // build the big document to trigger issue
- StringBuilder sb = new StringBuilder(2000);
- for (int i = 0; i < TESTROUNDS; ++i) {
- sb.append(DOC_PART);
- }
- final String DOC = sb.toString();
-
- SerializableString fieldName = new SerializedString("fieldName");
- JsonFactory jf = new JsonFactory();
- JsonParser parser = useStream ?
- jf.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")))
- : jf.createParser(new StringReader(DOC));
-
- for (int i = 0; i < TESTROUNDS - 1; i++) {
- assertEquals(JsonToken.START_OBJECT, parser.nextToken());
-
- // These will succeed
- assertTrue(parser.nextFieldName(fieldName));
-
- parser.nextLongValue(-1);
- assertEquals(JsonToken.END_OBJECT, parser.nextToken());
- }
-
- assertEquals(JsonToken.START_OBJECT, parser.nextToken());
-
- // This will fail
- assertTrue(parser.nextFieldName(fieldName));
- parser.close();
- }
-
- private void _testIssue38(boolean useStream) throws Exception
- {
- final String DOC = "{\"field\" :\"value\"}";
- SerializableString fieldName = new SerializedString("field");
- JsonFactory jf = new JsonFactory();
- JsonParser parser = useStream ?
- jf.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")))
- : jf.createParser(new StringReader(DOC));
- assertEquals(JsonToken.START_OBJECT, parser.nextToken());
- assertTrue(parser.nextFieldName(fieldName));
- assertEquals(JsonToken.VALUE_STRING, parser.nextToken());
- assertEquals("value", parser.getText());
- assertEquals(JsonToken.END_OBJECT, parser.nextToken());
- assertNull(parser.nextToken());
- parser.close();
- }
-
- private void _testNextNameWithLong(boolean useStream) throws Exception
- {
- // do 5 meg thingy
- final int SIZE = 5 * 1024 * 1024;
- StringBuilder sb = new StringBuilder(SIZE + 20);
-
- sb.append("{");
- Random rnd = new Random(1);
- int count = 0;
- while (sb.length() < SIZE) {
- ++count;
- if (sb.length() > 1) {
- sb.append(", ");
- }
- int val = rnd.nextInt();
- sb.append('"');
- sb.append("f"+val);
- sb.append("\":");
- sb.append(String.valueOf(val % 1000));
- }
- sb.append("}");
- final String DOC = sb.toString();
-
- JsonParser parser = useStream ?
- JSON_F.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")))
- : JSON_F.createParser(new StringReader(DOC));
- assertToken(JsonToken.START_OBJECT, parser.nextToken());
- rnd = new Random(1);
- for (int i = 0; i < count; ++i) {
- int exp = rnd.nextInt();
- SerializableString expName = new SerializedString("f"+exp);
- assertTrue(parser.nextFieldName(expName));
- assertToken(JsonToken.VALUE_NUMBER_INT, parser.nextToken());
- assertEquals(exp % 1000, parser.getIntValue());
- }
- assertToken(JsonToken.END_OBJECT, parser.nextToken());
- parser.close();
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestNumericValues.java b/src/test/java/com/fasterxml/jackson/core/json/TestNumericValues.java
deleted file mode 100644
index fb99205..0000000
--- a/src/test/java/com/fasterxml/jackson/core/json/TestNumericValues.java
+++ /dev/null
@@ -1,566 +0,0 @@
-package com.fasterxml.jackson.core.json;
-
-import java.math.BigDecimal;
-import java.math.BigInteger;
-
-import com.fasterxml.jackson.core.*;
-
-/**
- * Set of basic unit tests for verifying that the basic parser
- * functionality works as expected.
- */
-@SuppressWarnings("resource")
-public class TestNumericValues
- extends com.fasterxml.jackson.core.BaseTest
-{
- private final JsonFactory FACTORY = new JsonFactory();
-
- public void testSimpleBoolean() throws Exception
- {
- JsonParser jp = FACTORY.createParser("[ true ]");
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
- assertEquals(true, jp.getBooleanValue());
- jp.close();
- }
-
- public void testSimpleInt() throws Exception
- {
- for (int EXP_I : new int[] { 1234, -999, 0, 1, -2 }) {
- _testSimpleInt(EXP_I, false);
- _testSimpleInt(EXP_I, true);
- }
- }
-
- private void _testSimpleInt(int EXP_I, boolean useStream) throws Exception
- {
- String DOC = "[ "+EXP_I+" ]";
- JsonParser jp = useStream
- ? FACTORY.createParser(DOC)
- : FACTORY.createParser(DOC.getBytes("UTF-8"));
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(JsonParser.NumberType.INT, jp.getNumberType());
- assertEquals(""+EXP_I, jp.getText());
-
- assertEquals(EXP_I, jp.getIntValue());
- assertEquals((long) EXP_I, jp.getLongValue());
- assertEquals((double) EXP_I, jp.getDoubleValue());
- assertEquals(BigDecimal.valueOf((long) EXP_I), jp.getDecimalValue());
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- assertNull(jp.nextToken());
- jp.close();
-
- DOC = String.valueOf(EXP_I);
- jp = useStream
- ? FACTORY.createParser(DOC)
- : FACTORY.createParser(DOC.getBytes("UTF-8"));
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(DOC, jp.getText());
-
- int i = 0;
-
- try {
- i = jp.getIntValue();
- } catch (Exception e) {
- throw new Exception("Failed to parse input '"+DOC+"' (parser of type "+jp.getClass().getSimpleName()+")", e);
- }
-
- assertEquals(EXP_I, i);
-
- assertEquals((long) EXP_I, jp.getLongValue());
- assertEquals((double) EXP_I, jp.getDoubleValue());
- assertEquals(BigDecimal.valueOf((long) EXP_I), jp.getDecimalValue());
- assertNull(jp.nextToken());
- jp.close();
- }
-
- public void testIntRange() throws Exception
- {
- // let's test with readers and streams, separate code paths:
- for (int i = 0; i < 2; ++i) {
- String input = "[ "+Integer.MAX_VALUE+","+Integer.MIN_VALUE+" ]";
- JsonParser jp;
- if (i == 0) {
- jp = FACTORY.createParser(input);
- } else {
- jp = createParserUsingStream(input, "UTF-8");
- }
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(JsonParser.NumberType.INT, jp.getNumberType());
- assertEquals(Integer.MAX_VALUE, jp.getIntValue());
-
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(JsonParser.NumberType.INT, jp.getNumberType());
- assertEquals(Integer.MIN_VALUE, jp.getIntValue());
- jp.close();
- }
- }
-
- public void testSimpleLong() throws Exception
- {
- long EXP_L = 12345678907L;
-
- JsonParser jp = FACTORY.createParser("[ "+EXP_L+" ]");
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- // beyond int, should be long
- assertEquals(JsonParser.NumberType.LONG, jp.getNumberType());
- assertEquals(""+EXP_L, jp.getText());
-
- assertEquals(EXP_L, jp.getLongValue());
- // Should get an exception if trying to convert to int
- try {
- jp.getIntValue();
- } catch (JsonParseException jpe) {
- verifyException(jpe, "out of range");
- }
- assertEquals((double) EXP_L, jp.getDoubleValue());
- assertEquals(BigDecimal.valueOf((long) EXP_L), jp.getDecimalValue());
- jp.close();
- }
-
- public void testLongRange() throws Exception
- {
- for (int i = 0; i < 2; ++i) {
- long belowMinInt = -1L + Integer.MIN_VALUE;
- long aboveMaxInt = 1L + Integer.MAX_VALUE;
- String input = "[ "+Long.MAX_VALUE+","+Long.MIN_VALUE+","+aboveMaxInt+", "+belowMinInt+" ]";
- JsonParser jp;
- if (i == 0) {
- jp = FACTORY.createParser(input);
- } else {
- jp = this.createParserUsingStream(input, "UTF-8");
- }
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(JsonParser.NumberType.LONG, jp.getNumberType());
- assertEquals(Long.MAX_VALUE, jp.getLongValue());
-
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(JsonParser.NumberType.LONG, jp.getNumberType());
- assertEquals(Long.MIN_VALUE, jp.getLongValue());
-
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(JsonParser.NumberType.LONG, jp.getNumberType());
- assertEquals(aboveMaxInt, jp.getLongValue());
-
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(JsonParser.NumberType.LONG, jp.getNumberType());
- assertEquals(belowMinInt, jp.getLongValue());
-
-
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- jp.close();
- }
- }
-
- public void testBigDecimalRange()
- throws Exception
- {
- for (int i = 0; i < 2; ++i) {
- // let's test first values outside of Long range
- BigInteger small = new BigDecimal(Long.MIN_VALUE).toBigInteger();
- small = small.subtract(BigInteger.ONE);
- BigInteger big = new BigDecimal(Long.MAX_VALUE).toBigInteger();
- big = big.add(BigInteger.ONE);
- String input = "[ "+small+" , "+big+"]";
- JsonParser jp;
- if (i == 0) {
- jp = FACTORY.createParser(input);
- } else {
- jp = this.createParserUsingStream(input, "UTF-8");
- }
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(JsonParser.NumberType.BIG_INTEGER, jp.getNumberType());
- assertEquals(small, jp.getBigIntegerValue());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(JsonParser.NumberType.BIG_INTEGER, jp.getNumberType());
- assertEquals(big, jp.getBigIntegerValue());
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- jp.close();
- }
- }
-
- // for [Issue#78]
- public void testBigNumbers() throws Exception
- {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < 520; ++i) { // input buffer is 512 bytes by default
- sb.append('1');
- }
- final String NUMBER_STR = sb.toString();
- BigInteger biggie = new BigInteger(NUMBER_STR);
-
- for (int i = 0; i < 2; ++i) {
- JsonParser jp;
- if (i == 0) {
- jp = FACTORY.createParser(NUMBER_STR);
- } else {
- jp = this.createParserUsingStream(NUMBER_STR, "UTF-8");
- }
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(JsonParser.NumberType.BIG_INTEGER, jp.getNumberType());
- assertEquals(NUMBER_STR, jp.getText());
- assertEquals(biggie, jp.getBigIntegerValue());
- jp.close();
- }
- }
-
- public void testSimpleDouble() throws Exception
- {
- final String[] INPUTS = new String[] {
- "1234.00", "2.1101567E-16", "1.0e5", "0.0", "1.0", "-1.0",
- "-0.5", "-12.9", "-999.0",
- "2.5e+5", "9e4", "-12e-3", "0.25",
- };
- for (int input = 0; input < 2; ++input) {
- for (int i = 0; i < INPUTS.length; ++i) {
-
- // First in array
-
- String STR = INPUTS[i];
- double EXP_D = Double.parseDouble(STR);
- String DOC = "["+STR+"]";
-
- JsonParser jp;
-
- if (input == 0) {
- jp = createParserUsingStream(DOC, "UTF-8");
- } else {
- jp = FACTORY.createParser(DOC);
- }
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertEquals(STR, jp.getText());
- assertEquals(EXP_D, jp.getDoubleValue());
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- assertNull(jp.nextToken());
- jp.close();
-
- // then outside
- if (input == 0) {
- jp = createParserUsingStream(STR, "UTF-8");
- } else {
- jp = FACTORY.createParser(STR);
- }
- JsonToken t = null;
-
- try {
- t = jp.nextToken();
- } catch (Exception e) {
- throw new Exception("Failed to parse input '"+STR+"' (parser of type "+jp.getClass().getSimpleName()+")", e);
- }
-
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, t);
- assertEquals(STR, jp.getText());
- assertNull(jp.nextToken());
- jp.close();
- }
- }
- }
-
- public void testNumbers() throws Exception
- {
- final String DOC = "[ -13, 8100200300, 13.5, 0.00010, -2.033 ]";
-
- for (int input = 0; input < 2; ++input) {
- JsonParser jp;
-
- if (input == 0) {
- jp = createParserUsingStream(DOC, "UTF-8");
- } else {
- jp = FACTORY.createParser(DOC);
- }
-
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
-
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(-13, jp.getIntValue());
- assertEquals(-13L, jp.getLongValue());
- assertEquals(-13., jp.getDoubleValue());
- assertEquals("-13", jp.getText());
-
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(8100200300L, jp.getLongValue());
- // Should get exception for overflow:
- try {
- /*int x =*/ jp.getIntValue();
- fail("Expected an exception for overflow");
- } catch (Exception e) {
- verifyException(e, "out of range of int");
- }
- assertEquals(8100200300., jp.getDoubleValue());
- assertEquals("8100200300", jp.getText());
-
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertEquals(13, jp.getIntValue());
- assertEquals(13L, jp.getLongValue());
- assertEquals(13.5, jp.getDoubleValue());
- assertEquals("13.5", jp.getText());
-
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertEquals(0, jp.getIntValue());
- assertEquals(0L, jp.getLongValue());
- assertEquals(0.00010, jp.getDoubleValue());
- assertEquals("0.00010", jp.getText());
-
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertEquals(-2, jp.getIntValue());
- assertEquals(-2L, jp.getLongValue());
- assertEquals(-2.033, jp.getDoubleValue());
- assertEquals("-2.033", jp.getText());
-
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
-
- jp.close();
- }
- }
-
- public void testLongOverflow() throws Exception
- {
- BigInteger below = BigInteger.valueOf(Long.MIN_VALUE);
- below = below.subtract(BigInteger.ONE);
- BigInteger above = BigInteger.valueOf(Long.MAX_VALUE);
- above = above.add(BigInteger.ONE);
-
- String DOC_BELOW = below.toString() + " ";
- String DOC_ABOVE = below.toString() + " ";
- for (int input = 0; input < 2; ++input) {
- JsonParser jp;
-
- if (input == 0) {
- jp = createParserUsingStream(DOC_BELOW, "UTF-8");
- } else {
- jp = FACTORY.createParser(DOC_BELOW);
- }
- jp.nextToken();
- try {
- long x = jp.getLongValue();
- fail("Expected an exception for underflow (input "+jp.getText()+"): instead, got long value: "+x);
- } catch (JsonParseException e) {
- verifyException(e, "out of range of long");
- }
- jp.close();
-
- if (input == 0) {
- jp = createParserUsingStream(DOC_ABOVE, "UTF-8");
- } else {
- jp = createParserUsingReader(DOC_ABOVE);
- }
- jp.nextToken();
- try {
- long x = jp.getLongValue();
- fail("Expected an exception for underflow (input "+jp.getText()+"): instead, got long value: "+x);
- } catch (JsonParseException e) {
- verifyException(e, "out of range of long");
- }
- jp.close();
-
- }
- }
-
- /**
- * Method that tries to test that number parsing works in cases where
- * input is split between buffer boundaries.
- */
- public void testParsingOfLongerSequences()
- throws Exception
- {
- double[] values = new double[] { 0.01, -10.5, 2.1e9, 4.0e-8 };
- StringBuilder sb = new StringBuilder();
-
- for (int i = 0; i < values.length; ++i) {
- if (i > 0) {
- sb.append(',');
- }
- sb.append(values[i]);
- }
- String segment = sb.toString();
-
- int COUNT = 1000;
- sb = new StringBuilder(COUNT * segment.length() + 20);
- sb.append("[");
- for (int i = 0; i < COUNT; ++i) {
- if (i > 0) {
- sb.append(',');
- }
- sb.append(segment);
- sb.append('\n');
- // let's add somewhat arbitrary number of spaces
- int x = (i & 3);
- if (i > 300) {
- x += i % 5;
- }
- while (--x > 0) {
- sb.append(' ');
- }
- }
- sb.append("]");
- String DOC = sb.toString();
-
- for (int input = 0; input < 2; ++input) {
- JsonParser jp;
-
- if (input == 0) {
- jp = createParserUsingStream(DOC, "UTF-8");
- } else {
- jp = FACTORY.createParser(DOC);
- }
-
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- for (int i = 0; i < COUNT; ++i) {
- for (double d : values) {
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertEquals(d, jp.getDoubleValue());
- }
- }
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- jp.close();
- }
- }
-
- // [jackson-core#157]
- public void testLongNumbers() throws Exception
- {
- StringBuilder sb = new StringBuilder(9000);
- for (int i = 0; i < 9000; ++i) {
- sb.append('9');
- }
- String NUM = sb.toString();
- // force use of new factory, just in case (might still recycle same buffers tho?)
- JsonFactory f = new JsonFactory();
- _testLongNumbers(f, NUM, false);
- _testLongNumbers(f, NUM, true);
- }
-
- private void _testLongNumbers(JsonFactory f, String num, boolean useStream) throws Exception
- {
- final String doc = "[ "+num+" ]";
- JsonParser jp = useStream
- ? f.createParser(doc.getBytes("UTF-8"))
- : f.createParser(doc);
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(num, jp.getText());
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- }
-
- // and alternate take on for #157 (with negative num)
- public void testLongNumbers2() throws Exception
- {
- StringBuilder input = new StringBuilder();
- // test this with negative
- input.append('-');
- for (int i = 0; i < 2100; i++) {
- input.append(1);
- }
- final String DOC = input.toString();
- JsonFactory f = new JsonFactory();
- _testIssue160LongNumbers(f, DOC, false);
- _testIssue160LongNumbers(f, DOC, true);
- }
-
- private void _testIssue160LongNumbers(JsonFactory f, String doc, boolean useStream) throws Exception
- {
- JsonParser jp = useStream
- ? FACTORY.createParser(doc.getBytes("UTF-8"))
- : FACTORY.createParser(doc);
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- BigInteger v = jp.getBigIntegerValue();
- assertNull(jp.nextToken());
- assertEquals(doc, v.toString());
- }
-
- // for [jackson-core#181]
- /**
- * Method that tries to test that number parsing works in cases where
- * input is split between buffer boundaries.
- */
- public void testParsingOfLongerSequencesWithNonNumeric() throws Exception
- {
- JsonFactory factory = new JsonFactory();
- factory.enable(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS);
- double[] values = new double[] {
- 0.01, -10.5, 2.1e9, 4.0e-8,
- Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY
- };
- for (int i = 0; i < values.length; ++i) {
- int COUNT = 4096;
- // Don't see the failure with a multiple of 1
- int VCOUNT = 2 * COUNT;
- String arrayJson = toJsonArray(values[i], VCOUNT);
- StringBuilder sb = new StringBuilder(COUNT + arrayJson.length() + 20);
- for (int j = 0; j < COUNT; ++j) {
- sb.append(' ');
- }
- sb.append(arrayJson);
- String DOC = sb.toString();
- for (int input = 0; input < 2; ++input) {
- JsonParser jp;
- if (input == 0) {
- jp = createParserUsingStream(factory, DOC, "UTF-8");
- } else {
- jp = factory.createParser(DOC);
- }
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- for (int j = 0; j < VCOUNT; ++j) {
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertEquals(values[i], jp.getDoubleValue());
- }
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- jp.close();
- }
- }
- }
-
- /*
- /**********************************************************
- /* Tests for invalid access
- /**********************************************************
- */
-
- public void testInvalidBooleanAccess() throws Exception
- {
- JsonParser jp = FACTORY.createParser("[ \"abc\" ]");
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- try {
- jp.getBooleanValue();
- fail("Expected error trying to call getBooleanValue on non-boolean value");
- } catch (JsonParseException e) {
- verifyException(e, "not of boolean type");
- }
- jp.close();
- }
-
- public void testInvalidIntAccess() throws Exception
- {
- JsonParser jp = FACTORY.createParser("[ \"abc\" ]");
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- try {
- jp.getIntValue();
- fail("Expected error trying to call getIntValue on non-numeric value");
- } catch (JsonParseException e) {
- verifyException(e, "can not use numeric value accessors");
- }
- jp.close();
- }
-
- /*
- /**********************************************************
- /* Helper methods
- /**********************************************************
- */
-
- private String toJsonArray(double v, int n) {
- StringBuilder sb = new StringBuilder().append('[').append(v);
- for (int i = 1; i < n; ++i) {
- sb.append(',').append(v);
- }
- return sb.append(']').toString();
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestParserDupHandling.java b/src/test/java/com/fasterxml/jackson/core/json/TestParserDupHandling.java
deleted file mode 100644
index 64183c1..0000000
--- a/src/test/java/com/fasterxml/jackson/core/json/TestParserDupHandling.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.fasterxml.jackson.core.json;
-
-import com.fasterxml.jackson.core.*;
-
-public class TestParserDupHandling
- extends com.fasterxml.jackson.core.BaseTest
-{
- private final String[] DUP_DOCS = new String[] {
- "{ 'a':1, 'a':2 }",
- "[{ 'a':1, 'a':2 }]",
- "{ 'a':1, 'b':2, 'c':3,'a':true,'e':false }",
- "{ 'foo': { 'bar': [ [ { 'x':3, 'a':1 } ]], 'x':0, 'a':'y', 'b':3,'a':13 } }",
- "[{'b':1},{'b\":3},[{'a':3}], {'a':1,'a':2}]",
- "{'b':1,'array':[{'b':3}],'ob':{'b':4,'x':0,'y':3,'a':true,'a':false }}",
- };
- {
- for (int i = 0; i < DUP_DOCS.length; ++i) {
- DUP_DOCS[i] = DUP_DOCS[i].replace("'", "\"");
- }
- }
-
- public void testSimpleDupsDisabled() throws Exception
- {
- // first: verify no problems if detection NOT enabled
- final JsonFactory f = new JsonFactory();
- assertFalse(f.isEnabled(JsonParser.Feature.STRICT_DUPLICATE_DETECTION));
- for (String doc : DUP_DOCS) {
- _testSimpleDupsOk(doc, f, false);
- _testSimpleDupsOk(doc, f, true);
- }
- }
-
- public void testSimpleDupsBytes() throws Exception
- {
- JsonFactory nonDupF = new JsonFactory();
- JsonFactory dupF = new JsonFactory();
- dupF.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION);
- for (String doc : DUP_DOCS) {
- // First, with static setting
- _testSimpleDupsFail(doc, dupF, true, "a", false);
-
- // and then dynamic
- _testSimpleDupsFail(doc, nonDupF, true, "a", true);
- }
- }
-
- public void testSimpleDupsChars() throws Exception
- {
- JsonFactory nonDupF = new JsonFactory();
- JsonFactory dupF = new JsonFactory();
- dupF.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION);
- for (String doc : DUP_DOCS) {
- _testSimpleDupsFail(doc, dupF, false, "a", false);
- _testSimpleDupsFail(doc, nonDupF, false, "a", true);
- }
- }
-
- private void _testSimpleDupsOk(final String doc, JsonFactory f,
- boolean useStream) throws Exception
- {
- JsonParser jp = useStream ?
- createParserUsingStream(f, doc, "UTF-8") : createParserUsingReader(f, doc);
- JsonToken t = jp.nextToken();
- assertNotNull(t);
- assertTrue(t.isStructStart());
- while (jp.nextToken() != null) { }
- jp.close();
- }
-
- private void _testSimpleDupsFail(final String doc, JsonFactory f,
- boolean useStream, String name, boolean lazily) throws Exception
- {
- JsonParser jp = useStream ?
- createParserUsingStream(f, doc, "UTF-8") : createParserUsingReader(f, doc);
- if (lazily) {
- jp.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION);
- }
- JsonToken t = jp.nextToken();
- assertNotNull(t);
- assertTrue(t.isStructStart());
- try {
- while (jp.nextToken() != null) { }
- fail("Should have caught dups in document: "+doc);
- } catch (JsonParseException e) {
- verifyException(e, "duplicate field '"+name+"'");
- }
- jp.close();
- }
-
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestParserErrorHandling.java b/src/test/java/com/fasterxml/jackson/core/json/TestParserErrorHandling.java
deleted file mode 100644
index 15e149f..0000000
--- a/src/test/java/com/fasterxml/jackson/core/json/TestParserErrorHandling.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package com.fasterxml.jackson.core.json;
-
-import java.io.IOException;
-
-import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonToken;
-
-public class TestParserErrorHandling
- extends com.fasterxml.jackson.core.BaseTest
-{
- public void testInvalidKeywordsStream() throws Exception {
- _testInvalidKeywords(true);
- }
-
- public void testInvalidKeywordsReader() throws Exception {
- _testInvalidKeywords(false);
- }
-
- // Tests for #105 ("eager number parsing misses errors")
- public void testMangledNumbersBytes() throws Exception {
- _testMangledNumbers(true);
- }
-
- public void testMangledNumbersChars() throws Exception {
- _testMangledNumbers(false);
- }
-
- /*
- /**********************************************************
- /* Helper methods
- /**********************************************************
- */
-
- private void _testInvalidKeywords(boolean useStream) throws Exception
- {
- doTestInvalidKeyword1(useStream, "nul");
- doTestInvalidKeyword1(useStream, "Null");
- doTestInvalidKeyword1(useStream, "nulla");
- doTestInvalidKeyword1(useStream, "fal");
- doTestInvalidKeyword1(useStream, "False");
- doTestInvalidKeyword1(useStream, "fals0");
- doTestInvalidKeyword1(useStream, "falsett0");
- doTestInvalidKeyword1(useStream, "tr");
- doTestInvalidKeyword1(useStream, "truE");
- doTestInvalidKeyword1(useStream, "treu");
- doTestInvalidKeyword1(useStream, "trueenough");
- doTestInvalidKeyword1(useStream, "C");
- }
-
- private void doTestInvalidKeyword1(boolean useStream, String value)
- throws IOException
- {
- final String doc = "{ \"key1\" : "+value+" }";
- JsonParser jp = useStream ? createParserUsingStream(doc, "UTF-8")
- : createParserUsingReader(doc);
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- /* 24-Nov-2008, tatu: Note that depending on parser impl, we may
- * get the exception early or late...
- */
- try {
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- jp.nextToken();
- fail("Expected an exception for malformed value keyword");
- } catch (JsonParseException jex) {
- verifyException(jex, "Unrecognized token");
- verifyException(jex, value);
- } finally {
- jp.close();
- }
-
- // Try as root-level value as well:
- jp = useStream ? createParserUsingStream(value, "UTF-8")
- : createParserUsingReader(value);
- try {
- jp.nextToken();
- fail("Expected an exception for malformed value keyword");
- } catch (JsonParseException jex) {
- verifyException(jex, "Unrecognized token");
- verifyException(jex, value);
- } finally {
- jp.close();
- }
- }
-
- private void _testMangledNumbers(boolean useStream) throws Exception
- {
- String doc = "123true";
- JsonParser jp = useStream ? createParserUsingStream(doc, "UTF-8")
- : createParserUsingReader(doc);
- try {
- JsonToken t = jp.nextToken();
- fail("Should have gotten an exception; instead got token: "+t);
- } catch (JsonParseException e) {
- verifyException(e, "expected space");
- }
- jp.close();
-
- // Also test with floats
- doc = "1.5false";
- jp = useStream ? createParserUsingStream(doc, "UTF-8")
- : createParserUsingReader(doc);
- try {
- JsonToken t = jp.nextToken();
- fail("Should have gotten an exception; instead got token: "+t);
- } catch (JsonParseException e) {
- verifyException(e, "expected space");
- }
- jp.close();
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestParserNonStandard.java b/src/test/java/com/fasterxml/jackson/core/json/TestParserNonStandard.java
deleted file mode 100644
index 0538b80..0000000
--- a/src/test/java/com/fasterxml/jackson/core/json/TestParserNonStandard.java
+++ /dev/null
@@ -1,505 +0,0 @@
-package com.fasterxml.jackson.core.json;
-
-import com.fasterxml.jackson.core.*;
-
-public class TestParserNonStandard
- extends com.fasterxml.jackson.core.BaseTest
-{
- // // // And then tests to verify [JACKSON-69]:
-
- public void testSimpleUnquotedBytes() throws Exception {
- _testSimpleUnquoted(true);
- }
-
- public void testSimpleUnquotedChars() throws Exception {
- _testSimpleUnquoted(false);
- }
-
- public void testLargeUnquoted() throws Exception
- {
- _testLargeUnquoted(false);
- _testLargeUnquoted(true);
- }
-
- public void testSingleQuotesDefault() throws Exception
- {
- _testSingleQuotesDefault(false);
- _testSingleQuotesDefault(true);
- }
-
- public void testSingleQuotesEnabled() throws Exception
- {
- _testSingleQuotesEnabled(false);
- _testSingleQuotesEnabled(true);
- _testSingleQuotesEscaped(false);
- _testSingleQuotesEscaped(true);
- }
-
- // Test for [JACKSON-267], allowing '@' as name char, for unquoted names
- public void testNonStandardNameChars() throws Exception
- {
- _testNonStandardNameChars(false);
- _testNonStandardNameChars(true);
- }
-
- // Test for [JACKSON-300]
- public void testNonStandardAnyCharQuoting() throws Exception
- {
- _testNonStandarBackslashQuoting(false);
- _testNonStandarBackslashQuoting(true);
- }
-
- // Test for [JACKSON-358]
- public void testLeadingZeroesUTF8() throws Exception {
- _testLeadingZeroes(true, false);
- _testLeadingZeroes(true, true);
- }
-
- public void testLeadingZeroesReader() throws Exception {
- _testLeadingZeroes(false, false);
- _testLeadingZeroes(false, true);
- }
-
- // [JACKSON-142]: allow NaN
- public void testAllowNaN() throws Exception {
- _testAllowNaN(false);
- _testAllowNaN(true);
- }
-
- // [JACKSON-142]: allow +Inf/-Inf
- public void testAllowInfinity() throws Exception {
- _testAllowInf(false);
- _testAllowInf(true);
- }
-
- /*
- /****************************************************************
- /* Secondary test methods
- /****************************************************************
- */
-
- private void _testLargeUnquoted(boolean useStream) throws Exception
- {
- StringBuilder sb = new StringBuilder(5000);
- sb.append("[\n");
- //final int REPS = 2000;
- final int REPS = 1050;
- for (int i = 0; i < REPS; ++i) {
- if (i > 0) {
- sb.append(',');
- if ((i & 7) == 0) {
- sb.append('\n');
- }
- }
- sb.append("{");
- sb.append("abc").append(i&127).append(':');
- sb.append((i & 1) != 0);
- sb.append("}\n");
- }
- sb.append("]");
- String JSON = sb.toString();
- JsonFactory f = new JsonFactory();
- f.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
- JsonParser jp = useStream ?
- createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON)
- ;
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- for (int i = 0; i < REPS; ++i) {
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("abc"+(i&127), jp.getCurrentName());
- assertToken(((i&1) != 0) ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE, jp.nextToken());
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- }
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- jp.close();
- }
-
-
- private void _testSimpleUnquoted(boolean useStream) throws Exception
- {
- final JsonFactory f = new JsonFactory();
- f.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
-
- String JSON = "{ a : 1, _foo:true, $:\"money!\", \" \":null }";
- JsonParser jp = useStream ?
- createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON)
- ;
-
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("a", jp.getCurrentName());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("_foo", jp.getCurrentName());
- assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("$", jp.getCurrentName());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals("money!", jp.getText());
-
- // and then regular quoted one should still work too:
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals(" ", jp.getCurrentName());
-
- assertToken(JsonToken.VALUE_NULL, jp.nextToken());
-
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- jp.close();
-
- // Another thing, as per [Issue#102]: numbers
-
- JSON = "{ 123:true,4:false }";
- jp = useStream ?
- createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON)
- ;
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("123", jp.getCurrentName());
- assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
-
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("4", jp.getCurrentName());
- assertToken(JsonToken.VALUE_FALSE, jp.nextToken());
-
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- jp.close();
- }
-
- /**
- * Test to verify that the default parser settings do not
- * accept single-quotes for String values (field names,
- * textual values)
- */
- private void _testSingleQuotesDefault(boolean useStream) throws Exception
- {
- JsonFactory f = new JsonFactory();
- // First, let's see that by default they are not allowed
- String JSON = "[ 'text' ]";
- JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON);
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- try {
- jp.nextToken();
- fail("Expected exception");
- } catch (JsonParseException e) {
- verifyException(e, "Unexpected character ('''");
- } finally {
- jp.close();
- }
-
- JSON = "{ 'a':1 }";
- jp = useStream ? createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON);
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- try {
- jp.nextToken();
- fail("Expected exception");
- } catch (JsonParseException e) {
- verifyException(e, "Unexpected character ('''");
- } finally {
- jp.close();
- }
- }
-
- /**
- * Test to verify [JACKSON-173], optional handling of
- * single quotes, to allow handling invalid (but, alas, common)
- * JSON.
- */
- private void _testSingleQuotesEnabled(boolean useStream) throws Exception
- {
- JsonFactory f = new JsonFactory();
- f.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
-
- String JSON = "{ 'a' : 1, \"foobar\": 'b', '_abcde1234':'d', '\"' : '\"\"', '':'' }";
- JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON);
-
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
-
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("a", jp.getText());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals("1", jp.getText());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("foobar", jp.getText());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals("b", jp.getText());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("_abcde1234", jp.getText());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals("d", jp.getText());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("\"", jp.getText());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- //assertEquals("\"\"", jp.getText());
-
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("", jp.getText());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals("", jp.getText());
-
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- jp.close();
- }
-
- // test to verify that we implicitly allow escaping of apostrophe [JACKSON-548]
- private void _testSingleQuotesEscaped(boolean useStream) throws Exception
- {
- JsonFactory f = new JsonFactory();
- f.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
-
- String JSON = "[ '16\\'' ]";
- JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON);
-
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals("16'", jp.getText());
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- jp.close();
- }
-
- private void _testNonStandardNameChars(boolean useStream) throws Exception
- {
- JsonFactory f = new JsonFactory();
- f.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
- String JSON = "{ @type : \"mytype\", #color : 123, *error* : true, "
- +" hyphen-ated : \"yes\", me+my : null"
- +"}";
- JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON);
-
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
-
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("@type", jp.getText());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals("mytype", jp.getText());
-
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("#color", jp.getText());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(123, jp.getIntValue());
-
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("*error*", jp.getText());
- assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
-
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("hyphen-ated", jp.getText());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals("yes", jp.getText());
-
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("me+my", jp.getText());
- assertToken(JsonToken.VALUE_NULL, jp.nextToken());
-
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- jp.close();
- }
-
- private void _testNonStandarBackslashQuoting(boolean useStream) throws Exception
- {
- // first: verify that we get an exception
- JsonFactory f = new JsonFactory();
- assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER));
- final String JSON = quote("\\'");
- JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON);
- try {
- jp.nextToken();
- jp.getText();
- fail("Should have thrown an exception for doc <"+JSON+">");
- } catch (JsonParseException e) {
- verifyException(e, "unrecognized character escape");
- } finally {
- jp.close();
- }
- // and then verify it's ok...
- f.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
- assertTrue(f.isEnabled(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER));
- jp = useStream ? createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON);
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals("'", jp.getText());
- jp.close();
- }
-
- private void _testLeadingZeroes(boolean useStream, boolean appendSpace) throws Exception
- {
- // first: verify that we get an exception
- JsonFactory f = new JsonFactory();
- assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS));
- String JSON = "00003";
- if (appendSpace) {
- JSON += " ";
- }
- JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON);
- try {
- jp.nextToken();
- jp.getText();
- fail("Should have thrown an exception for doc <"+JSON+">");
- } catch (JsonParseException e) {
- verifyException(e, "invalid numeric value");
- } finally {
- jp.close();
- }
-
- // and then verify it's ok when enabled
- f.configure(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS, true);
- assertTrue(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS));
- jp = useStream ? createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON);
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(3, jp.getIntValue());
- assertEquals("3", jp.getText());
- jp.close();
-
- // Plus, also: verify that leading zero magnitude is ok:
- JSON = "0"+Integer.MAX_VALUE;
- if (appendSpace) {
- JSON += " ";
- }
- jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON);
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(String.valueOf(Integer.MAX_VALUE), jp.getText());
- assertEquals(Integer.MAX_VALUE, jp.getIntValue());
- Number nr = jp.getNumberValue();
- assertSame(Integer.class, nr.getClass());
- jp.close();
- }
-
- private void _testAllowNaN(boolean useStream) throws Exception
- {
- final String JSON = "[ NaN]";
- JsonFactory f = new JsonFactory();
- assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS));
-
- // without enabling, should get an exception
- JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON);
-
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- try {
- jp.nextToken();
- fail("Expected exception");
- } catch (Exception e) {
- verifyException(e, "non-standard");
- } finally {
- jp.close();
- }
-
- // we can enable it dynamically (impl detail)
- f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true);
- jp = useStream ? createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON);
-
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
-
- double d = jp.getDoubleValue();
- assertTrue(Double.isNaN(d));
- assertEquals("NaN", jp.getText());
-
- // [Issue#98]
- try {
- /*BigDecimal dec =*/ jp.getDecimalValue();
- fail("Should fail when trying to access NaN as BigDecimal");
- } catch (NumberFormatException e) {
- verifyException(e, "can not be represented as BigDecimal");
- }
-
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- jp.close();
-
- // finally, should also work with skipping
- f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true);
- jp = useStream ? createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON);
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- jp.close();
- }
-
- private void _testAllowInf(boolean useStream) throws Exception
- {
- final String JSON = "[ -INF, +INF, +Infinity, Infinity, -Infinity ]";
- JsonFactory f = new JsonFactory();
- assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS));
-
- // without enabling, should get an exception
- JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON);
-
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- try {
- jp.nextToken();
- fail("Expected exception");
- } catch (Exception e) {
- verifyException(e, "Non-standard token '-INF'");
- } finally {
- jp.close();
- }
-
- f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true);
- jp = useStream ? createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON);
-
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
-
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- double d = jp.getDoubleValue();
- assertEquals("-INF", jp.getText());
- assertTrue(Double.isInfinite(d));
- assertTrue(d == Double.NEGATIVE_INFINITY);
-
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- d = jp.getDoubleValue();
- assertEquals("+INF", jp.getText());
- assertTrue(Double.isInfinite(d));
- assertTrue(d == Double.POSITIVE_INFINITY);
-
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- d = jp.getDoubleValue();
- assertEquals("+Infinity", jp.getText());
- assertTrue(Double.isInfinite(d));
- assertTrue(d == Double.POSITIVE_INFINITY);
-
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- d = jp.getDoubleValue();
- assertEquals("Infinity", jp.getText());
- assertTrue(Double.isInfinite(d));
- assertTrue(d == Double.POSITIVE_INFINITY);
-
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- d = jp.getDoubleValue();
- assertEquals("-Infinity", jp.getText());
- assertTrue(Double.isInfinite(d));
- assertTrue(d == Double.NEGATIVE_INFINITY);
-
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- jp.close();
-
- // finally, should also work with skipping
- f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true);
- jp = useStream ? createParserUsingStream(f, JSON, "UTF-8")
- : createParserUsingReader(f, JSON);
-
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
-
- jp.close();
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestParserOverrides.java b/src/test/java/com/fasterxml/jackson/core/json/TestParserOverrides.java
index d382428..244effd 100644
--- a/src/test/java/com/fasterxml/jackson/core/json/TestParserOverrides.java
+++ b/src/test/java/com/fasterxml/jackson/core/json/TestParserOverrides.java
@@ -45,14 +45,14 @@
JsonParser jp = useStream ?
jf.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")))
: jf.createParser(DOC);
- assertNull(jp.getCurrentToken());
+ assertNull(jp.currentToken());
jp.clearCurrentToken();
- assertNull(jp.getCurrentToken());
+ assertNull(jp.currentToken());
assertNull(jp.getEmbeddedObject());
assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.START_ARRAY, jp.getCurrentToken());
+ assertToken(JsonToken.START_ARRAY, jp.currentToken());
jp.clearCurrentToken();
- assertNull(jp.getCurrentToken());
+ assertNull(jp.currentToken());
// Also: no codec defined by default
try {
jp.readValueAsTree();
@@ -69,7 +69,7 @@
JsonParser jp = useStream ?
jf.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")))
: jf.createParser(new StringReader(DOC));
- assertNull(jp.getCurrentToken());
+ assertNull(jp.currentToken());
assertToken(JsonToken.START_OBJECT, jp.nextToken());
assertToken(JsonToken.FIELD_NAME, jp.nextToken());
assertEquals("first", jp.getCurrentName());
@@ -94,9 +94,7 @@
assertToken(JsonToken.END_OBJECT, jp.nextToken());
jp.clearCurrentToken();
- assertNull(jp.getCurrentToken());
+ assertNull(jp.currentToken());
jp.close();
}
-
-
}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestRootValues.java b/src/test/java/com/fasterxml/jackson/core/json/TestRootValues.java
index 585dbbe..625d284 100644
--- a/src/test/java/com/fasterxml/jackson/core/json/TestRootValues.java
+++ b/src/test/java/com/fasterxml/jackson/core/json/TestRootValues.java
@@ -45,7 +45,7 @@
// Should fail, right away
try {
p.nextToken();
- fail("Ought to fail! Instead, got token: "+p.getCurrentToken());
+ fail("Ought to fail! Instead, got token: "+p.currentToken());
} catch (JsonParseException e) {
verifyException(e, "unexpected character");
}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestUtf8Generator.java b/src/test/java/com/fasterxml/jackson/core/json/TestUtf8Generator.java
index 3cf4020..50752b2 100644
--- a/src/test/java/com/fasterxml/jackson/core/json/TestUtf8Generator.java
+++ b/src/test/java/com/fasterxml/jackson/core/json/TestUtf8Generator.java
@@ -1,11 +1,17 @@
package com.fasterxml.jackson.core.json;
-import java.io.*;
-
-import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.BaseTest;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.filter.FilteringGeneratorDelegate;
+import com.fasterxml.jackson.core.filter.JsonPointerBasedFilter;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.util.BufferRecycler;
+import java.io.ByteArrayOutputStream;
+
public class TestUtf8Generator extends BaseTest
{
private final JsonFactory JSON_F = new JsonFactory();
@@ -42,11 +48,11 @@
{
final String VALUE = quote("\ud83d\ude0c");
ByteArrayOutputStream out = new ByteArrayOutputStream();
- JsonGenerator jgen = JSON_F.createGenerator(out);
- jgen.writeStartArray();
- jgen.writeRaw(VALUE);
- jgen.writeEndArray();
- jgen.close();
+ JsonGenerator g = JSON_F.createGenerator(out);
+ g.writeStartArray();
+ g.writeRaw(VALUE);
+ g.writeEndArray();
+ g.close();
final byte[] JSON = out.toByteArray();
@@ -60,4 +66,52 @@
assertToken(JsonToken.END_ARRAY, jp.nextToken());
jp.close();
}
+
+ public void testFilteringWithEscapedChars() throws Exception
+ {
+ final String SAMPLE_WITH_QUOTES = "\b\t\f\n\r\"foo\"\u0000";
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ @SuppressWarnings("resource")
+ JsonGenerator g = JSON_F.createGenerator(out);
+
+ FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(g,
+ new JsonPointerBasedFilter("/escapes"),
+ true, // includePath
+ false // multipleMatches
+ );
+
+ //final String JSON = "{'a':123,'array':[1,2],'escapes':'\b\t\f\n\r\"foo\"\u0000'}";
+
+ gen.writeStartObject();
+
+ gen.writeFieldName("a");
+ gen.writeNumber((int) 123);
+
+ gen.writeFieldName("array");
+ gen.writeStartArray();
+ gen.writeNumber((short) 1);
+ gen.writeNumber((short) 2);
+ gen.writeEndArray();
+
+ gen.writeFieldName("escapes");
+
+ final byte[] raw = SAMPLE_WITH_QUOTES.toString().getBytes("UTF-8");
+ gen.writeUTF8String(raw, 0, raw.length);
+
+ gen.writeEndObject();
+ gen.close();
+
+ JsonParser p = JSON_F.createParser(out.toByteArray());
+
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("escapes", p.getCurrentName());
+
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals(SAMPLE_WITH_QUOTES, p.getText());
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ assertNull(p.nextToken());
+ p.close();
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestUtf8Parser.java b/src/test/java/com/fasterxml/jackson/core/json/TestUtf8Parser.java
deleted file mode 100644
index d7f2f7c..0000000
--- a/src/test/java/com/fasterxml/jackson/core/json/TestUtf8Parser.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package com.fasterxml.jackson.core.json;
-
-
-import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.core.io.SerializedString;
-
-import java.io.*;
-import java.util.Random;
-
-/**
- * Set of basic unit tests for verifying that the basic parser
- * functionality works as expected.
- */
-public class TestUtf8Parser
- extends BaseTest
-{
- final static String[] UTF8_2BYTE_STRINGS = new String[] {
- /* This may look funny, but UTF8 scanner has fairly
- * elaborate decoding machinery, and it is indeed
- * necessary to try out various combinations...
- */
- "b", "A\u00D8", "abc", "c3p0",
- "12345", "......", "Long\u00FAer",
- "Latin1-fully-\u00BE-develop\u00A8d",
- "Some very long name, ridiculously long actually to see that buffer expansion works: \u00BF?"
- };
-
- final static String[] UTF8_3BYTE_STRINGS = new String[] {
- "\uC823?", "A\u400F", "1\u1234?",
- "Ab123\u4034",
- "Even-longer:\uC023"
- };
-
- public void testEmptyName()
- throws Exception
- {
- final String DOC = "{ \"\" : \"\" }";
-
- JsonParser jp = createParserUsingStream(DOC, "UTF-8");
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals("", jp.getCurrentName());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals("", jp.getText());
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- jp.close();
- }
-
- public void testUtf8Name2Bytes() throws Exception
- {
- final String[] NAMES = UTF8_2BYTE_STRINGS;
-
- for (int i = 0; i < NAMES.length; ++i) {
- String NAME = NAMES[i];
- String DOC = "{ \""+NAME+"\" : 0 }";
- JsonParser jp = createParserUsingStream(DOC, "UTF-8");
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
-
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
-
- assertTrue(jp.hasToken(JsonToken.FIELD_NAME));
- assertTrue(jp.hasTokenId(JsonTokenId.ID_FIELD_NAME));
-
- assertEquals(NAME, jp.getCurrentName());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertTrue(jp.hasToken(JsonToken.VALUE_NUMBER_INT));
- assertTrue(jp.hasTokenId(JsonTokenId.ID_NUMBER_INT));
-
- // should retain name during value entry, too
- assertEquals(NAME, jp.getCurrentName());
-
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- jp.close();
- }
- }
-
- public void testUtf8Name3Bytes() throws Exception
- {
- final String[] NAMES = UTF8_3BYTE_STRINGS;
-
- for (int i = 0; i < NAMES.length; ++i) {
- String NAME = NAMES[i];
- String DOC = "{ \""+NAME+"\" : true }";
-
- JsonParser jp = createParserUsingStream(DOC, "UTF-8");
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
-
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals(NAME, jp.getCurrentName());
- assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
- assertEquals(NAME, jp.getCurrentName());
-
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
-
- jp.close();
- }
- }
-
- // How about tests for Surrogate-Pairs?
-
- public void testUtf8StringTrivial() throws Exception
- {
- String[] VALUES = UTF8_2BYTE_STRINGS;
- for (int i = 0; i < VALUES.length; ++i) {
- String VALUE = VALUES[i];
- String DOC = "[ \""+VALUE+"\" ]";
- JsonParser jp = createParserUsingStream(DOC, "UTF-8");
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- String act = getAndVerifyText(jp);
- if (act.length() != VALUE.length()) {
- fail("Failed for value #"+(i+1)+"/"+VALUES.length+": length was "+act.length()+", should be "+VALUE.length());
- }
- assertEquals(VALUE, act);
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- jp.close();
- }
-
- VALUES = UTF8_3BYTE_STRINGS;
- for (int i = 0; i < VALUES.length; ++i) {
- String VALUE = VALUES[i];
- String DOC = "[ \""+VALUE+"\" ]";
- JsonParser jp = createParserUsingStream(DOC, "UTF-8");
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals(VALUE, getAndVerifyText(jp));
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- jp.close();
- }
- }
-
- public void testUtf8StringValue() throws Exception
- {
- Random r = new Random(13);
- //int LEN = 72000;
- int LEN = 720;
- StringBuilder sb = new StringBuilder(LEN + 20);
- while (sb.length() < LEN) {
- int c;
- if (r.nextBoolean()) { // ascii
- c = 32 + (r.nextInt() & 0x3F);
- if (c == '"' || c == '\\') {
- c = ' ';
- }
- } else if (r.nextBoolean()) { // 2-byte
- c = 160 + (r.nextInt() & 0x3FF);
- } else if (r.nextBoolean()) { // 3-byte (non-surrogate)
- c = 8000 + (r.nextInt() & 0x7FFF);
- } else { // surrogates (2 chars)
- int value = r.nextInt() & 0x3FFFF; // 20-bit, ~ 1 million
- sb.append((char) (0xD800 + (value >> 10)));
- c = (0xDC00 + (value & 0x3FF));
-
- }
- sb.append((char) c);
- }
-
- ByteArrayOutputStream bout = new ByteArrayOutputStream(LEN);
- OutputStreamWriter out = new OutputStreamWriter(bout, "UTF-8");
- out.write("[\"");
- String VALUE = sb.toString();
- out.write(VALUE);
- out.write("\"]");
- out.close();
-
- byte[] data = bout.toByteArray();
-
- JsonParser jp = new JsonFactory().createParser(new ByteArrayInputStream(data));
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- String act = jp.getText();
-
- assertEquals(VALUE.length(), act.length());
- assertEquals(VALUE, act);
- jp.close();
- }
-
- // [JACKSON-889]
- public void testNextFieldName() throws IOException
- {
- JsonFactory f = new JsonFactory();
- SerializedString id = new SerializedString("id");
-
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- os.write('{');
- for (int i = 0; i < 3994; i++) {
- os.write(' ');
- }
- os.write("\"id\":2".getBytes("UTF-8"));
- os.write('}');
-
- JsonParser parser = f.createParser(new ByteArrayInputStream(os.toByteArray()));
- assertEquals(parser.nextToken(), JsonToken.START_OBJECT);
- assertTrue(parser.nextFieldName(id));
- assertEquals(parser.nextToken(), JsonToken.VALUE_NUMBER_INT);
- assertEquals(parser.nextToken(), JsonToken.END_OBJECT);
- parser.close();
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestValueConversions.java b/src/test/java/com/fasterxml/jackson/core/json/TestValueConversions.java
deleted file mode 100644
index 543caac..0000000
--- a/src/test/java/com/fasterxml/jackson/core/json/TestValueConversions.java
+++ /dev/null
@@ -1,186 +0,0 @@
-package com.fasterxml.jackson.core.json;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonToken;
-
-public class TestValueConversions
- extends com.fasterxml.jackson.core.BaseTest
-{
- public void testAsInt() throws Exception
- {
- final String input = "[ 1, -3, 4.98, true, false, null, \"-17\", \"foo\" ]";
- for (int i = 0; i < 2; ++i) {
- JsonParser jp;
- if (i == 0) {
- jp = createParserUsingReader(input);
- } else {
- jp = this.createParserUsingStream(input, "UTF-8");
- }
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertEquals(0, jp.getValueAsLong());
- assertEquals(9, jp.getValueAsLong(9));
-
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(1, jp.getValueAsLong());
- assertEquals(1, jp.getValueAsLong(-99));
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(-3, jp.getValueAsLong());
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertEquals(4, jp.getValueAsLong());
- assertEquals(4, jp.getValueAsLong(99));
- assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
- assertEquals(1, jp.getValueAsLong());
- assertToken(JsonToken.VALUE_FALSE, jp.nextToken());
- assertEquals(0, jp.getValueAsLong());
- assertToken(JsonToken.VALUE_NULL, jp.nextToken());
- assertEquals(0, jp.getValueAsLong());
- assertEquals(0, jp.getValueAsLong(27));
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals(-17, jp.getValueAsLong());
- assertEquals(-17, jp.getValueAsLong(3));
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals(0, jp.getValueAsLong());
- assertEquals(9, jp.getValueAsLong(9));
-
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- assertEquals(0, jp.getValueAsLong());
- assertEquals(9, jp.getValueAsLong(9));
-
- jp.close();
- }
- }
-
- /**
- * @since 1.7
- */
- public void testAsBoolean() throws Exception
- {
- final String input = "[ true, false, null, 1, 0, \"true\", \"false\", \"foo\" ]";
- for (int i = 0; i < 2; ++i) {
- JsonParser jp;
- if (i == 0) {
- jp = createParserUsingReader(input);
- } else {
- jp = this.createParserUsingStream(input, "UTF-8");
- }
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertEquals(false, jp.getValueAsBoolean());
- assertEquals(true, jp.getValueAsBoolean(true));
-
- assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
- assertEquals(true, jp.getValueAsBoolean());
- assertToken(JsonToken.VALUE_FALSE, jp.nextToken());
- assertEquals(false, jp.getValueAsBoolean());
- assertToken(JsonToken.VALUE_NULL, jp.nextToken());
- assertEquals(false, jp.getValueAsBoolean());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(1, jp.getIntValue());
- assertEquals(true, jp.getValueAsBoolean());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(0, jp.getIntValue());
- assertEquals(false, jp.getValueAsBoolean());
-
- assertToken(JsonToken.VALUE_STRING, jp.nextToken()); // "true"
- assertEquals(true, jp.getValueAsBoolean());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals(false, jp.getValueAsBoolean());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals(false, jp.getValueAsBoolean());
-
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- assertEquals(false, jp.getValueAsBoolean());
- assertEquals(true, jp.getValueAsBoolean(true));
-
- jp.close();
- }
- }
-
- public void testAsLong() throws Exception
- {
- final String input = "[ 1, -3, 4.98, true, false, null, \"-17\", \"foo\" ]";
- for (int i = 0; i < 2; ++i) {
- JsonParser jp;
- if (i == 0) {
- jp = createParserUsingReader(input);
- } else {
- jp = createParserUsingStream(input, "UTF-8");
- }
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertEquals(0L, jp.getValueAsLong());
- assertEquals(9L, jp.getValueAsLong(9L));
-
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(1L, jp.getValueAsLong());
- assertEquals(1L, jp.getValueAsLong(-99L));
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(-3L, jp.getValueAsLong());
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertEquals(4L, jp.getValueAsLong());
- assertEquals(4L, jp.getValueAsLong(99L));
- assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
- assertEquals(1L, jp.getValueAsLong());
- assertToken(JsonToken.VALUE_FALSE, jp.nextToken());
- assertEquals(0L, jp.getValueAsLong());
- assertToken(JsonToken.VALUE_NULL, jp.nextToken());
- assertEquals(0L, jp.getValueAsLong());
- assertEquals(0L, jp.getValueAsLong(27L));
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals(-17L, jp.getValueAsLong());
- assertEquals(-17L, jp.getValueAsLong(3L));
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals(0L, jp.getValueAsLong());
- assertEquals(9L, jp.getValueAsLong(9L));
-
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- assertEquals(0L, jp.getValueAsLong());
- assertEquals(9L, jp.getValueAsLong(9L));
-
- jp.close();
- }
- }
-
- public void testAsDouble() throws Exception
- {
- final String input = "[ 1, -3, 4.98, true, false, null, \"-17.25\", \"foo\" ]";
- for (int i = 0; i < 2; ++i) {
- JsonParser jp;
- if (i == 0) {
- jp = createParserUsingReader(input);
- } else {
- jp = this.createParserUsingStream(input, "UTF-8");
- }
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertEquals(0.0, jp.getValueAsDouble());
- assertEquals(9.0, jp.getValueAsDouble(9.0));
-
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(1., jp.getValueAsDouble());
- assertEquals(1., jp.getValueAsDouble(-99.0));
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(-3., jp.getValueAsDouble());
- assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
- assertEquals(4.98, jp.getValueAsDouble());
- assertEquals(4.98, jp.getValueAsDouble(12.5));
- assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
- assertEquals(1.0, jp.getValueAsDouble());
- assertToken(JsonToken.VALUE_FALSE, jp.nextToken());
- assertEquals(0.0, jp.getValueAsDouble());
- assertToken(JsonToken.VALUE_NULL, jp.nextToken());
- assertEquals(0.0, jp.getValueAsDouble());
- assertEquals(0.0, jp.getValueAsDouble(27.8));
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals(-17.25, jp.getValueAsDouble());
- assertEquals(-17.25, jp.getValueAsDouble(1.9));
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals(0.0, jp.getValueAsDouble());
- assertEquals(1.25, jp.getValueAsDouble(1.25));
-
- assertToken(JsonToken.END_ARRAY, jp.nextToken());
- assertEquals(0.0, jp.getValueAsDouble());
- assertEquals(7.5, jp.getValueAsDouble(7.5));
-
- jp.close();
- }
- }
-
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestWithTonsaSymbols.java b/src/test/java/com/fasterxml/jackson/core/json/TestWithTonsaSymbols.java
index 5b553a7..910962d 100644
--- a/src/test/java/com/fasterxml/jackson/core/json/TestWithTonsaSymbols.java
+++ b/src/test/java/com/fasterxml/jackson/core/json/TestWithTonsaSymbols.java
@@ -43,18 +43,18 @@
* state is different between runs.
*/
for (int x = 0; x < 3; ++x) {
- JsonParser jp = useStream ?
+ JsonParser p = useStream ?
jf.createParser(new ByteArrayInputStream(doc.getBytes("UTF-8")))
: jf.createParser(new StringReader(doc));
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
for (int i = 0; i < FIELD_COUNT; ++i) {
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals(fieldNameFor(i), jp.getCurrentName());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(i, jp.getIntValue());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals(fieldNameFor(i), p.getCurrentName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(i, p.getIntValue());
}
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- jp.close();
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ p.close();
}
}
diff --git a/src/test/java/com/fasterxml/jackson/core/main/TestArrayParsing.java b/src/test/java/com/fasterxml/jackson/core/main/TestArrayParsing.java
index 0dff001..b189cb1 100644
--- a/src/test/java/com/fasterxml/jackson/core/main/TestArrayParsing.java
+++ b/src/test/java/com/fasterxml/jackson/core/main/TestArrayParsing.java
@@ -69,4 +69,116 @@
}
jp.close();
}
+
+ /**
+ * Tests the missing value as 'null' in an array
+ * This needs enabling of the Feature.ALLOW_MISSING_VALUES in JsonParser
+ * This tests both Stream based parsing and the Reader based parsing
+ * @throws Exception
+ */
+ public void testMissingValueAsNullByEnablingFeature() throws Exception
+ {
+ _testMissingValueByEnablingFeature(true);
+ _testMissingValueByEnablingFeature(false);
+ }
+
+ /**
+ * Tests the missing value in an array by not enabling
+ * the Feature.ALLOW_MISSING_VALUES
+ * @throws Exception
+ */
+ public void testMissingValueAsNullByNotEnablingFeature() throws Exception
+ {
+ _testMissingValueNotEnablingFeature(true);
+ _testMissingValueNotEnablingFeature(false);
+ }
+
+ /**
+ * Tests the not missing any value in an array by enabling the
+ * Feature.ALLOW_MISSING_VALUES in JsonParser
+ * This tests both Stream based parsing and the Reader based parsing for not missing any value
+ * @throws Exception
+ */
+ public void testNotMissingValueByEnablingFeature() throws Exception
+ {
+ _testNotMissingValueByEnablingFeature(true);
+ _testNotMissingValueByEnablingFeature(false);
+ }
+
+ private void _testMissingValueByEnablingFeature(boolean useStream) throws Exception {
+ String DOC = "[ \"a\",,,,\"abc\", ] ";
+
+ JsonFactory f = new JsonFactory();
+ f.configure(JsonParser.Feature.ALLOW_MISSING_VALUES, true);
+
+ JsonParser jp = useStream ? createParserUsingStream(f, DOC, "UTF-8")
+ : createParserUsingReader(f, DOC);
+
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals("a", jp.getValueAsString());
+
+ assertToken(JsonToken.VALUE_NULL, jp.nextToken());
+ assertToken(JsonToken.VALUE_NULL, jp.nextToken());
+ assertToken(JsonToken.VALUE_NULL, jp.nextToken());
+ assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ assertToken(JsonToken.VALUE_NULL, jp.nextToken());
+ assertToken(JsonToken.END_ARRAY, jp.nextToken());
+ assertNull(jp.nextToken());
+
+ jp.close();
+
+ // And another take
+ DOC = "[,] ";
+ jp = useStream ? createParserUsingStream(f, DOC, "UTF-8")
+ : createParserUsingReader(f, DOC);
+
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_NULL, jp.nextToken());
+ assertToken(JsonToken.VALUE_NULL, jp.nextToken());
+ assertToken(JsonToken.END_ARRAY, jp.nextToken());
+ assertNull(jp.nextToken());
+
+ jp.close();
+ }
+
+ private void _testMissingValueNotEnablingFeature(boolean useStream) throws Exception {
+ final String DOC = "[ \"a\",,\"abc\"] ";
+
+ JsonFactory f = new JsonFactory();
+
+ JsonParser jp = useStream ? createParserUsingStream(f, DOC, "UTF-8")
+ : createParserUsingReader(f, DOC);
+
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals("a", jp.getValueAsString());
+ try {
+ assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ fail("Expecting exception here");
+ }
+ catch(JsonParseException ex){
+ verifyException(ex, "expected a valid value", "expected a value");
+ }
+ jp.close();
+ }
+
+ private void _testNotMissingValueByEnablingFeature(boolean useStream) throws Exception {
+ final String DOC = "[ \"a\",\"abc\"] ";
+
+ JsonFactory f = new JsonFactory();
+ f.configure(JsonParser.Feature.ALLOW_MISSING_VALUES, true);
+
+ JsonParser jp = useStream ? createParserUsingStream(f, DOC, "UTF-8")
+ : createParserUsingReader(f, DOC);
+
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals("a", jp.getValueAsString());
+
+ assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ assertToken(JsonToken.END_ARRAY, jp.nextToken());
+
+ jp.close();
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorArray.java b/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorArray.java
index 08dfeca..7b86c6d 100644
--- a/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorArray.java
+++ b/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorArray.java
@@ -36,7 +36,7 @@
gen.writeEndArray();
ctxt = gen.getOutputContext();
- assertTrue("Should be in root, was "+ctxt.getTypeDesc(), ctxt.inRoot());
+ assertTrue("Should be in root, was "+ctxt.typeDesc(), ctxt.inRoot());
assertFalse(ctxt.inArray());
assertFalse(ctxt.inObject());
assertEquals(1, ctxt.getEntryCount());
@@ -79,7 +79,7 @@
gen.writeEndObject();
fail("Expected an exception for mismatched array/object write");
} catch (JsonGenerationException e) {
- verifyException(e, "Current context not an object");
+ verifyException(e, "Current context not Object");
}
gen.close();
}
diff --git a/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorCopy.java b/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorCopy.java
index 853ecb7..e9e26f2 100644
--- a/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorCopy.java
+++ b/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorCopy.java
@@ -26,7 +26,7 @@
while ((t = jp.nextToken()) != null) {
gen.copyCurrentEvent(jp);
// should not change parser state:
- assertToken(t, jp.getCurrentToken());
+ assertToken(t, jp.currentToken());
}
jp.close();
gen.close();
@@ -46,14 +46,14 @@
assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
gen.copyCurrentEvent(jp);
// should not change parser state:
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.getCurrentToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.currentToken());
assertEquals(123, jp.getIntValue());
// And then let's copy the array
assertToken(JsonToken.START_ARRAY, jp.nextToken());
gen.copyCurrentStructure(jp);
// which will advance parser to matching close Array
- assertToken(JsonToken.END_ARRAY, jp.getCurrentToken());
+ assertToken(JsonToken.END_ARRAY, jp.currentToken());
jp.close();
gen.close();
@@ -72,7 +72,7 @@
assertToken(JsonToken.START_OBJECT, jp.nextToken());
gen.copyCurrentStructure(jp);
// which will advance parser to matching end Object
- assertToken(JsonToken.END_OBJECT, jp.getCurrentToken());
+ assertToken(JsonToken.END_OBJECT, jp.currentToken());
jp.close();
gen.close();
diff --git a/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorMisc.java b/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorMisc.java
index 08771c8..ded2380 100644
--- a/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorMisc.java
+++ b/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorMisc.java
@@ -11,6 +11,7 @@
* Set of basic unit tests for verifying basic generator
* features.
*/
+@SuppressWarnings("resource")
public class TestGeneratorMisc
extends com.fasterxml.jackson.core.BaseTest
{
@@ -20,8 +21,7 @@
/**********************************************************
*/
- public void testIsClosed()
- throws IOException
+ public void testIsClosed() throws IOException
{
JsonFactory jf = new JsonFactory();
for (int i = 0; i < 2; ++i) {
@@ -240,46 +240,41 @@
*/
public void testLongerObjects() throws Exception
{
- JsonFactory jf = new JsonFactory();
- for (int i = 0; i < 2; ++i) {
- boolean useChars = (i == 0);
- JsonGenerator jgen;
- ByteArrayOutputStream bout = new ByteArrayOutputStream(200);
- if (useChars) {
- jgen = jf.createGenerator(new OutputStreamWriter(bout, "UTF-8"));
- } else {
- jgen = jf.createGenerator(bout, JsonEncoding.UTF8);
+ final JsonFactory jf = new JsonFactory();
+ _testLongerObjects(jf, 0);
+ _testLongerObjects(jf, 1);
+ _testLongerObjects(jf, 2);
+ }
+
+ public void _testLongerObjects(JsonFactory jf, int mode) throws Exception
+ {
+ JsonGenerator g;
+ ByteArrayOutputStream bout = new ByteArrayOutputStream(200);
+
+ switch (mode) {
+ case 0:
+ g = jf.createGenerator(new OutputStreamWriter(bout, "UTF-8"));
+ break;
+ case 1:
+ g = jf.createGenerator(bout, JsonEncoding.UTF8);
+ break;
+ case 2:
+ {
+ DataOutputStream dout = new DataOutputStream(bout);
+ g = jf.createGenerator((DataOutput) dout);
}
+
+ break;
+ default:
+ fail("Unknown mode "+mode);
+ g = null;
+ }
- jgen.writeStartObject();
+ g.writeStartObject();
- for (int rounds = 0; rounds < 1500; ++rounds) {
- for (int letter = 'a'; letter <= 'z'; ++letter) {
- for (int index = 0; index < 20; ++index) {
- String name;
- if (letter > 'f') {
- name = "X"+letter+index;
- } else if (letter > 'p') {
- name = ""+letter+index;
- } else {
- name = "__"+index+letter;
- }
- jgen.writeFieldName(name);
- jgen.writeNumber(index-1);
- }
- jgen.writeRaw('\n');
- }
- }
- jgen.writeEndObject();
- jgen.close();
-
- byte[] json = bout.toByteArray();
- JsonParser jp = jf.createParser(json);
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- for (int rounds = 0; rounds < 1500; ++rounds) {
+ for (int rounds = 0; rounds < 1500; ++rounds) {
for (int letter = 'a'; letter <= 'z'; ++letter) {
for (int index = 0; index < 20; ++index) {
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
String name;
if (letter > 'f') {
name = "X"+letter+index;
@@ -288,14 +283,37 @@
} else {
name = "__"+index+letter;
}
- assertEquals(name, jp.getCurrentName());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertEquals(index-1, jp.getIntValue());
+ g.writeFieldName(name);
+ g.writeNumber(index-1);
}
+ g.writeRaw('\n');
}
- }
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- jp.close();
}
+ g.writeEndObject();
+ g.close();
+
+ byte[] json = bout.toByteArray();
+ JsonParser jp = jf.createParser(json);
+ assertToken(JsonToken.START_OBJECT, jp.nextToken());
+ for (int rounds = 0; rounds < 1500; ++rounds) {
+ for (int letter = 'a'; letter <= 'z'; ++letter) {
+ for (int index = 0; index < 20; ++index) {
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+ String name;
+ if (letter > 'f') {
+ name = "X"+letter+index;
+ } else if (letter > 'p') {
+ name = ""+letter+index;
+ } else {
+ name = "__"+index+letter;
+ }
+ assertEquals(name, jp.getCurrentName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(index-1, jp.getIntValue());
+ }
+ }
+ }
+ assertToken(JsonToken.END_OBJECT, jp.nextToken());
+ jp.close();
}
}
diff --git a/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorObject.java b/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorObject.java
index 3f0eaae..e92f1dd 100644
--- a/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorObject.java
+++ b/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorObject.java
@@ -65,7 +65,7 @@
gen.writeEndArray();
fail("Expected an exception for mismatched array/object write");
} catch (JsonGenerationException e) {
- verifyException(e, "Current context not an array");
+ verifyException(e, "Current context not Array");
}
gen.close();
}
diff --git a/src/test/java/com/fasterxml/jackson/core/main/TestParserFeatures.java b/src/test/java/com/fasterxml/jackson/core/main/TestParserFeatures.java
index c66d5a6..e1841f1 100644
--- a/src/test/java/com/fasterxml/jackson/core/main/TestParserFeatures.java
+++ b/src/test/java/com/fasterxml/jackson/core/main/TestParserFeatures.java
@@ -1,5 +1,7 @@
package com.fasterxml.jackson.core.main;
+import java.io.*;
+
import com.fasterxml.jackson.core.*;
/**
@@ -9,7 +11,7 @@
public class TestParserFeatures
extends com.fasterxml.jackson.core.BaseTest
{
- public void testDefaultSettings()
+ public void testDefaultSettings() throws Exception
{
JsonFactory f = new JsonFactory();
assertTrue(f.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE));
@@ -17,6 +19,13 @@
assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES));
assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_SINGLE_QUOTES));
assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS));
+
+ JsonParser p = f.createParser(new StringReader("{}"));
+ _testDefaultSettings(p);
+ p.close();
+ p = f.createParser(new ByteArrayInputStream("{}".getBytes("UTF-8")));
+ _testDefaultSettings(p);
+ p.close();
}
public void testQuotesRequired() throws Exception
@@ -25,7 +34,6 @@
_testQuotesRequired(true);
}
-
// // Tests for [JACKSON-208], unquoted tabs:
public void testTabsDefault() throws Exception
@@ -46,23 +54,28 @@
/****************************************************************
*/
+ private void _testDefaultSettings(JsonParser p) {
+ assertFalse(p.canReadObjectId());
+ assertFalse(p.canReadTypeId());
+ }
+
private void _testQuotesRequired(boolean useStream) throws Exception
{
final String JSON = "{ test : 3 }";
final String EXP_ERROR_FRAGMENT = "was expecting double-quote to start";
JsonFactory f = new JsonFactory();
- JsonParser jp = useStream ?
+ JsonParser p = useStream ?
createParserUsingStream(f, JSON, "UTF-8")
: createParserUsingReader(f, JSON)
;
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
try {
- jp.nextToken();
+ p.nextToken();
} catch (JsonParseException je) {
verifyException(je, EXP_ERROR_FRAGMENT);
} finally {
- jp.close();
+ p.close();
}
}
@@ -73,16 +86,16 @@
JsonFactory f = new JsonFactory();
// First, let's see that by default unquoted tabs are illegal
String JSON = "[\"tab:\t\"]";
- JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON);
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ JsonParser p = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
try {
- jp.nextToken();
- jp.getText();
+ p.nextToken();
+ p.getText();
fail("Expected exception");
} catch (JsonParseException e) {
verifyException(e, "Illegal unquoted character");
} finally {
- jp.close();
+ p.close();
}
}
@@ -94,14 +107,14 @@
String FIELD = "a\tb";
String VALUE = "\t";
String JSON = "{ "+quote(FIELD)+" : "+quote(VALUE)+"}";
- JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON);
+ JsonParser p = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON);
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertEquals(FIELD, jp.getText());
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
- assertEquals(VALUE, jp.getText());
- assertToken(JsonToken.END_OBJECT, jp.nextToken());
- jp.close();
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals(FIELD, p.getText());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals(VALUE, p.getText());
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ p.close();
}
}
diff --git a/src/test/java/com/fasterxml/jackson/core/main/TestScopeMatching.java b/src/test/java/com/fasterxml/jackson/core/main/TestScopeMatching.java
deleted file mode 100644
index 7a55088..0000000
--- a/src/test/java/com/fasterxml/jackson/core/main/TestScopeMatching.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package com.fasterxml.jackson.core.main;
-
-
-import com.fasterxml.jackson.core.*;
-
-/**
- * Set of basic unit tests for verifying that Array/Object scopes
- * are properly matched.
- */
-public class TestScopeMatching
- extends BaseTest
-{
- public void testUnclosedArray() throws Exception
- {
- @SuppressWarnings("resource")
- JsonParser jp = createParserUsingReader("[ 1, 2");
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
-
- try {
- jp.nextToken();
- fail("Expected an exception for unclosed ARRAY");
- } catch (JsonParseException jpe) {
- verifyException(jpe, "expected close marker for ARRAY");
- }
- }
-
- public void testUnclosedObject() throws Exception
- {
- @SuppressWarnings("resource")
- JsonParser jp = createParserUsingReader("{ \"key\" : 3 ");
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
-
- try {
- jp.nextToken();
- fail("Expected an exception for unclosed OBJECT");
- } catch (JsonParseException jpe) {
- verifyException(jpe, "expected close marker for OBJECT");
- }
- }
-
- public void testEOFInName()
- throws Exception
- {
- final String JSON = "{ \"abcd";
- for (int i = 0; i < 2; ++i) {
- JsonParser jp = (i == 0) ? createParserUsingReader(JSON)
- : createParserUsingStream(JSON, "UTF-8");
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- try {
- jp.nextToken();
- fail("Expected an exception for EOF");
- } catch (JsonParseException jpe) {
- verifyException(jpe, "Unexpected end-of-input");
- }
- jp.close();
- }
- }
-
- public void testWeirdToken()
- throws Exception
- {
- final String JSON = "[ nil ]";
- for (int i = 0; i < 2; ++i) {
- JsonParser jp = (i == 0) ? createParserUsingReader(JSON)
- : createParserUsingStream(JSON, "UTF-8");
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- try {
- jp.nextToken();
- fail("Expected an exception for weird token");
- } catch (JsonParseException jpe) {
- verifyException(jpe, "Unrecognized token");
- }
- jp.close();
- }
- }
-
- public void testMismatchArrayToObject()
- throws Exception
- {
- final String JSON = "[ 1, 2 }";
- for (int i = 0; i < 2; ++i) {
- JsonParser jp = (i == 0) ? createParserUsingReader(JSON)
- : createParserUsingStream(JSON, "UTF-8");
- assertToken(JsonToken.START_ARRAY, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
- try {
- jp.nextToken();
- fail("Expected an exception for incorrectly closed ARRAY");
- } catch (JsonParseException jpe) {
- verifyException(jpe, "Unexpected close marker '}': expected ']'");
- }
- jp.close();
- }
- }
-
- public void testMismatchObjectToArray()
- throws Exception
- {
- final String JSON = "{ ]";
- for (int i = 0; i < 2; ++i) {
- JsonParser jp = (i == 0) ? createParserUsingReader(JSON)
- : createParserUsingStream(JSON, "UTF-8");
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
-
- try {
- jp.nextToken();
- fail("Expected an exception for incorrectly closed OBJECT");
- } catch (JsonParseException jpe) {
- verifyException(jpe, "Unexpected close marker ']': expected '}'");
- }
- jp.close();
- }
- }
-
- public void testMisssingColon()
- throws Exception
- {
- final String JSON = "{ \"a\" \"b\" }";
- for (int i = 0; i < 2; ++i) {
- JsonParser jp = (i == 0) ? createParserUsingReader(JSON)
- : createParserUsingStream(JSON, "UTF-8");
- assertToken(JsonToken.START_OBJECT, jp.nextToken());
- try {
- // can be either here, or with next one...
- assertToken(JsonToken.FIELD_NAME, jp.nextToken());
- jp.nextToken();
- fail("Expected an exception for missing semicolon");
- } catch (JsonParseException jpe) {
- verifyException(jpe, "was expecting a colon");
- }
- jp.close();
- }
- }
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/base64/TestBase64Parsing.java b/src/test/java/com/fasterxml/jackson/core/read/Base64BinaryParsingTest.java
similarity index 68%
rename from src/test/java/com/fasterxml/jackson/core/base64/TestBase64Parsing.java
rename to src/test/java/com/fasterxml/jackson/core/read/Base64BinaryParsingTest.java
index 783efa6..fbd7126 100644
--- a/src/test/java/com/fasterxml/jackson/core/base64/TestBase64Parsing.java
+++ b/src/test/java/com/fasterxml/jackson/core/read/Base64BinaryParsingTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.core.base64;
+package com.fasterxml.jackson.core.read;
import static org.junit.Assert.assertArrayEquals;
@@ -6,24 +6,27 @@
import com.fasterxml.jackson.core.*;
-public class TestBase64Parsing
+public class Base64BinaryParsingTest
extends com.fasterxml.jackson.core.BaseTest
{
public void testBase64UsingInputStream() throws Exception
{
- _testBase64Text(true);
+ _testBase64Text(MODE_INPUT_STREAM);
+ _testBase64Text(MODE_INPUT_STREAM_THROTTLED);
+ _testBase64Text(MODE_DATA_INPUT);
}
public void testBase64UsingReader() throws Exception
{
- _testBase64Text(false);
+ _testBase64Text(MODE_READER);
}
- // [Issue-15] (streaming binary reads)
public void testStreaming() throws IOException
{
- _testStreaming(false);
- _testStreaming(true);
+ _testStreaming(MODE_INPUT_STREAM);
+ _testStreaming(MODE_INPUT_STREAM_THROTTLED);
+ _testStreaming(MODE_DATA_INPUT);
+ _testStreaming(MODE_READER);
}
/*
@@ -31,10 +34,9 @@
/* Test helper methods
/**********************************************************
*/
-
- // Test for [JACKSON-631]
+
@SuppressWarnings("resource")
- public void _testBase64Text(boolean useBytes) throws Exception
+ public void _testBase64Text(int mode) throws Exception
{
// let's actually iterate over sets of encoding modes, lengths
@@ -55,26 +57,27 @@
input[i] = (byte) i;
}
for (Base64Variant variant : VARIANTS) {
- JsonGenerator jgen;
- if (useBytes) {
- bytes.reset();
- jgen = jsonFactory.createGenerator(bytes, JsonEncoding.UTF8);
- } else {
+ JsonGenerator g;
+
+ if (mode == MODE_READER) {
chars = new StringWriter();
- jgen = jsonFactory.createGenerator(chars);
- }
- jgen.writeBinary(variant, input, 0, input.length);
- jgen.close();
- JsonParser jp;
- if (useBytes) {
- jp = jsonFactory.createParser(bytes.toByteArray());
+ g = jsonFactory.createGenerator(chars);
} else {
- jp = jsonFactory.createParser(chars.toString());
+ bytes.reset();
+ g = jsonFactory.createGenerator(bytes, JsonEncoding.UTF8);
}
- assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ g.writeBinary(variant, input, 0, input.length);
+ g.close();
+ JsonParser p;
+ if (mode == MODE_READER) {
+ p = jsonFactory.createParser(chars.toString());
+ } else {
+ p = createParser(jsonFactory, mode, bytes.toByteArray());
+ }
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
byte[] data = null;
try {
- data = jp.getBinaryValue(variant);
+ data = p.getBinaryValue(variant);
} catch (Exception e) {
IOException ioException = new IOException("Failed (variant "+variant+", data length "+len+"): "+e.getMessage());
ioException.initCause(e);
@@ -82,8 +85,10 @@
}
assertNotNull(data);
assertArrayEquals(data, input);
- assertNull(jp.nextToken());
- jp.close();
+ if (mode != MODE_DATA_INPUT) { // no look-ahead for DataInput
+ assertNull(p.nextToken());
+ }
+ p.close();
}
}
}
@@ -97,7 +102,7 @@
return result;
}
- private void _testStreaming(boolean useBytes) throws IOException
+ private void _testStreaming(int mode) throws IOException
{
final int[] SIZES = new int[] {
1, 2, 3, 4, 5, 6,
@@ -113,12 +118,12 @@
for (int size : SIZES) {
byte[] data = _generateData(size);
JsonGenerator g;
- if (useBytes) {
- bytes.reset();
- g = jsonFactory.createGenerator(bytes, JsonEncoding.UTF8);
- } else {
+ if (mode == MODE_READER) {
chars = new StringWriter();
g = jsonFactory.createGenerator(chars);
+ } else {
+ bytes.reset();
+ g = jsonFactory.createGenerator(bytes, JsonEncoding.UTF8);
}
g.writeStartObject();
@@ -129,10 +134,10 @@
// and verify
JsonParser p;
- if (useBytes) {
- p = jsonFactory.createParser(bytes.toByteArray());
- } else {
+ if (mode == MODE_READER) {
p = jsonFactory.createParser(chars.toString());
+ } else {
+ p = createParser(jsonFactory, mode, bytes.toByteArray());
}
assertToken(JsonToken.START_OBJECT, p.nextToken());
@@ -144,7 +149,9 @@
assertEquals(size, gotten);
assertArrayEquals(data, result.toByteArray());
assertToken(JsonToken.END_OBJECT, p.nextToken());
- assertNull(p.nextToken());
+ if (mode != MODE_DATA_INPUT) { // no look-ahead for DataInput
+ assertNull(p.nextToken());
+ }
p.close();
}
}
diff --git a/src/test/java/com/fasterxml/jackson/core/read/CommentParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/CommentParsingTest.java
new file mode 100644
index 0000000..dfc12c5
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/read/CommentParsingTest.java
@@ -0,0 +1,286 @@
+package com.fasterxml.jackson.core.read;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.*;
+
+/**
+ * Unit tests for verifying that support for (non-standard) comments
+ * works as expected.
+ */
+public class CommentParsingTest
+ extends com.fasterxml.jackson.core.BaseTest
+{
+ final static String DOC_WITH_SLASHSTAR_COMMENT =
+ "[ /* comment:\n ends here */ 1 /* one more ok to have \"unquoted\" */ ]"
+ ;
+
+ final static String DOC_WITH_SLASHSLASH_COMMENT =
+ "[ // comment...\n 1 \r // one more, not array: [] \n ]"
+ ;
+
+ /*
+ /**********************************************************
+ /* Test method wrappers
+ /**********************************************************
+ */
+
+ /**
+ * Unit test for verifying that by default comments are not
+ * recognized.
+ */
+ public void testDefaultSettings() throws Exception
+ {
+ JsonFactory jf = new JsonFactory();
+ assertFalse(jf.isEnabled(JsonParser.Feature.ALLOW_COMMENTS));
+ JsonParser p = jf.createParser(new StringReader("[ 1 ]"));
+ assertFalse(p.isEnabled(JsonParser.Feature.ALLOW_COMMENTS));
+ p.close();
+ }
+
+ public void testCommentsDisabled() throws Exception
+ {
+ _testDisabled(DOC_WITH_SLASHSTAR_COMMENT, MODE_INPUT_STREAM);
+ _testDisabled(DOC_WITH_SLASHSLASH_COMMENT, MODE_INPUT_STREAM);
+ _testDisabled(DOC_WITH_SLASHSTAR_COMMENT, MODE_INPUT_STREAM_THROTTLED);
+ _testDisabled(DOC_WITH_SLASHSLASH_COMMENT, MODE_INPUT_STREAM_THROTTLED);
+ _testDisabled(DOC_WITH_SLASHSTAR_COMMENT, MODE_READER);
+ _testDisabled(DOC_WITH_SLASHSLASH_COMMENT, MODE_READER);
+ _testDisabled(DOC_WITH_SLASHSTAR_COMMENT, MODE_DATA_INPUT);
+ _testDisabled(DOC_WITH_SLASHSLASH_COMMENT, MODE_DATA_INPUT);
+ }
+
+ public void testCommentsEnabled() throws Exception
+ {
+ _testEnabled(DOC_WITH_SLASHSTAR_COMMENT, MODE_INPUT_STREAM);
+ _testEnabled(DOC_WITH_SLASHSLASH_COMMENT, MODE_INPUT_STREAM);
+ _testEnabled(DOC_WITH_SLASHSTAR_COMMENT, MODE_INPUT_STREAM_THROTTLED);
+ _testEnabled(DOC_WITH_SLASHSLASH_COMMENT, MODE_INPUT_STREAM_THROTTLED);
+ _testEnabled(DOC_WITH_SLASHSTAR_COMMENT, MODE_READER);
+ _testEnabled(DOC_WITH_SLASHSLASH_COMMENT, MODE_READER);
+ _testEnabled(DOC_WITH_SLASHSTAR_COMMENT, MODE_DATA_INPUT);
+ _testEnabled(DOC_WITH_SLASHSLASH_COMMENT, MODE_DATA_INPUT);
+ }
+
+ public void testCommentsWithUTF8() throws Exception
+ {
+ final String JSON = "/* \u00a9 2099 Yoyodyne Inc. */\n [ \"bar? \u00a9\" ]\n";
+ _testWithUTF8Chars(JSON, MODE_INPUT_STREAM);
+ _testWithUTF8Chars(JSON, MODE_INPUT_STREAM_THROTTLED);
+ _testWithUTF8Chars(JSON, MODE_READER);
+ _testWithUTF8Chars(JSON, MODE_DATA_INPUT);
+ }
+
+ public void testYAMLCommentsBytes() throws Exception {
+ JsonFactory f = new JsonFactory();
+ f.configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true);
+
+ _testYAMLComments(f, MODE_INPUT_STREAM);
+ _testCommentsBeforePropValue(f, MODE_INPUT_STREAM, "# foo\n");
+ _testYAMLComments(f, MODE_INPUT_STREAM_THROTTLED);
+ _testCommentsBeforePropValue(f, MODE_INPUT_STREAM_THROTTLED, "# foo\n");
+ _testYAMLComments(f, MODE_DATA_INPUT);
+ _testCommentsBeforePropValue(f, MODE_DATA_INPUT, "# foo\n");
+ }
+
+ public void testYAMLCommentsChars() throws Exception {
+ JsonFactory f = new JsonFactory();
+ f.configure(JsonParser.Feature.ALLOW_YAML_COMMENTS, true);
+ _testYAMLComments(f, MODE_READER);
+ final String COMMENT = "# foo\n";
+ _testCommentsBeforePropValue(f, MODE_READER, COMMENT);
+ _testCommentsBetweenArrayValues(f, MODE_READER, COMMENT);
+ }
+
+ public void testCCommentsBytes() throws Exception {
+ JsonFactory f = new JsonFactory();
+ f.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
+ final String COMMENT = "/* foo */\n";
+ _testCommentsBeforePropValue(f, MODE_INPUT_STREAM, COMMENT);
+ _testCommentsBeforePropValue(f, MODE_INPUT_STREAM_THROTTLED, COMMENT);
+ _testCommentsBeforePropValue(f, MODE_DATA_INPUT, COMMENT);
+ }
+
+ public void testCCommentsChars() throws Exception {
+ JsonFactory f = new JsonFactory();
+ f.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
+ final String COMMENT = "/* foo */\n";
+ _testCommentsBeforePropValue(f, MODE_READER, COMMENT);
+ }
+
+ public void testCppCommentsBytes() throws Exception {
+ JsonFactory f = new JsonFactory();
+ f.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
+ final String COMMENT = "// foo\n";
+ _testCommentsBeforePropValue(f, MODE_INPUT_STREAM, COMMENT);
+ _testCommentsBeforePropValue(f, MODE_INPUT_STREAM_THROTTLED, COMMENT);
+ _testCommentsBeforePropValue(f, MODE_DATA_INPUT, COMMENT);
+ }
+
+ public void testCppCommentsChars() throws Exception {
+ JsonFactory f = new JsonFactory();
+ f.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
+ final String COMMENT = "// foo \n";
+ _testCommentsBeforePropValue(f, MODE_READER, COMMENT);
+ }
+
+ @SuppressWarnings("resource")
+ private void _testCommentsBeforePropValue(JsonFactory f,
+ int mode, String comment) throws Exception
+ {
+ for (String arg : new String[] {
+ ":%s123",
+ " :%s123",
+ "\t:%s123",
+ ": %s123",
+ ":\t%s123",
+ }) {
+ String commented = String.format(arg, comment);
+
+ final String DOC = "{\"abc\"" + commented + "}";
+ JsonParser p = createParser(f, mode, DOC);
+ assertEquals(JsonToken.START_OBJECT, p.nextToken());
+ JsonToken t = null;
+ try {
+ t = p.nextToken();
+ } catch (Exception e) {
+ throw new RuntimeException("Failed on '"+DOC+"' due to "+e, e);
+ }
+ assertEquals(JsonToken.FIELD_NAME, t);
+
+ try {
+ t = p.nextToken();
+ } catch (Exception e) {
+ throw new RuntimeException("Failed on '"+DOC+"' due to "+e, e);
+ }
+ assertEquals(JsonToken.VALUE_NUMBER_INT, t);
+ assertEquals(123, p.getIntValue());
+ assertEquals(JsonToken.END_OBJECT, p.nextToken());
+ p.close();
+ }
+
+ }
+
+ @SuppressWarnings("resource")
+ private void _testCommentsBetweenArrayValues(JsonFactory f,
+ int mode, String comment) throws Exception
+ {
+ for (String tmpl : new String[] {
+ "%s,",
+ " %s,",
+ "\t%s,",
+ "%s ,",
+ "%s\t,",
+ " %s ,",
+ "\t%s\t,",
+ "\n%s,",
+ "%s\n,",
+ }) {
+ String commented = String.format(tmpl, comment);
+
+ final String DOC = "[1"+commented+"2]";
+ JsonParser p = createParser(f, mode, DOC);
+ assertEquals(JsonToken.START_ARRAY, p.nextToken());
+ JsonToken t = null;
+ try {
+ t = p.nextToken();
+ } catch (Exception e) {
+ throw new RuntimeException("Failed on '"+DOC+"' due to "+e, e);
+ }
+ assertEquals(JsonToken.VALUE_NUMBER_INT, t);
+ assertEquals(1, p.getIntValue());
+
+ try {
+ t = p.nextToken();
+ } catch (Exception e) {
+ throw new RuntimeException("Failed on '"+DOC+"' due to "+e, e);
+ }
+ assertEquals(JsonToken.VALUE_NUMBER_INT, t);
+ assertEquals(2, p.getIntValue());
+ assertEquals(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+ }
+
+ }
+
+ private void _testYAMLComments(JsonFactory f, int mode) throws Exception
+ {
+ final String DOC = "# foo\n"
+ +" {\"a\" # xyz\n"
+ +" : # foo\n"
+ +" 1, # more\n"
+ +"\"b\": [ \n"
+ +" #all!\n"
+ +" 3 #yay!\n"
+ +"] # foobar\n"
+ +"} # x"
+ ;
+ JsonParser p = createParser(f, mode, DOC);
+ assertEquals(JsonToken.START_OBJECT, p.nextToken());
+ assertEquals(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("a", p.getCurrentName());
+ assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(1, p.getIntValue());
+ assertEquals(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("b", p.getCurrentName());
+ assertEquals(JsonToken.START_ARRAY, p.nextToken());
+ assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(3, p.getIntValue());
+ assertEquals(JsonToken.END_ARRAY, p.nextToken());
+ assertEquals(JsonToken.END_OBJECT, p.nextToken());
+ if (mode != MODE_DATA_INPUT) {
+ assertNull(p.nextToken());
+ }
+ p.close();
+ }
+
+ /*
+ /**********************************************************
+ /* Helper methods
+ /**********************************************************
+ */
+
+ private void _testWithUTF8Chars(String doc, int mode) throws IOException
+ {
+ // should basically just stream through
+ JsonParser p = _createParser(doc, mode, true);
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ if (mode != MODE_DATA_INPUT) {
+ assertNull(p.nextToken());
+ }
+ p.close();
+ }
+
+ private void _testDisabled(String doc, int mode) throws IOException
+ {
+ JsonParser p = _createParser(doc, mode, false);
+ try {
+ p.nextToken();
+ fail("Expected exception for unrecognized comment");
+ } catch (JsonParseException je) {
+ // Should have something denoting that user may want to enable 'ALLOW_COMMENTS'
+ verifyException(je, "ALLOW_COMMENTS");
+ }
+ p.close();
+ }
+
+ private void _testEnabled(String doc, int mode) throws IOException
+ {
+ JsonParser p = _createParser(doc, mode, true);
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(1, p.getIntValue());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+ }
+
+ private JsonParser _createParser(String doc, int mode, boolean enabled)
+ throws IOException
+ {
+ JsonFactory f = new JsonFactory();
+ f.configure(JsonParser.Feature.ALLOW_COMMENTS, enabled);
+ JsonParser p = createParser(f, mode, doc);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ return p;
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/read/JsonParserTest.java b/src/test/java/com/fasterxml/jackson/core/read/JsonParserTest.java
new file mode 100644
index 0000000..55442e1
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/read/JsonParserTest.java
@@ -0,0 +1,710 @@
+package com.fasterxml.jackson.core.read;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.testsupport.MockDataInput;
+import com.fasterxml.jackson.core.util.JsonParserDelegate;
+
+import java.io.*;
+import java.net.URL;
+import java.util.*;
+
+/**
+ * Set of basic unit tests for verifying that the basic parser
+ * functionality works as expected.
+ */
+@SuppressWarnings("resource")
+public class JsonParserTest
+ extends com.fasterxml.jackson.core.BaseTest
+{
+ public void testConfig() throws Exception
+ {
+ JsonParser p = createParserUsingReader("[ ]");
+ p.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
+ assertTrue(p.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE));
+ p.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
+ assertFalse(p.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE));
+
+ p.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true);
+ assertTrue(p.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE));
+ p.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false);
+ assertFalse(p.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE));
+ p.close();
+ }
+
+ public void testInterningWithStreams() throws Exception
+ {
+ _testIntern(true, true, "a");
+ _testIntern(true, false, "b");
+ }
+
+ public void testInterningWithReaders() throws Exception
+ {
+ _testIntern(false, true, "c");
+ _testIntern(false, false, "d");
+ }
+
+ private void _testIntern(boolean useStream, boolean enableIntern, String expName) throws IOException
+ {
+ JsonFactory f = new JsonFactory();
+ f.configure(JsonFactory.Feature.INTERN_FIELD_NAMES, enableIntern);
+ assertEquals(enableIntern, f.isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES));
+ final String JSON = "{ \""+expName+"\" : 1}";
+ JsonParser p = useStream ?
+ createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON);
+
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ // needs to be same of cours
+ String actName = p.getCurrentName();
+ assertEquals(expName, actName);
+ if (enableIntern) {
+ assertSame(expName, actName);
+ } else {
+ assertNotSame(expName, actName);
+ }
+ p.close();
+ }
+
+ /**
+ * This basic unit test verifies that example given in the JSON
+ * specification (RFC-4627 or later) is properly parsed at
+ * high-level, without verifying values.
+ */
+ public void testSpecExampleSkipping() throws Exception
+ {
+ _doTestSpec(false);
+ }
+
+ /**
+ * Unit test that verifies that the spec example JSON is completely
+ * parsed, and proper values are given for contents of all
+ * events/tokens.
+ */
+ public void testSpecExampleFully() throws Exception
+ {
+ _doTestSpec(true);
+ }
+
+ /**
+ * Unit test that verifies that 3 basic keywords (null, true, false)
+ * are properly parsed in various contexts.
+ */
+ public void testKeywords() throws Exception
+ {
+ final String DOC = "{\n"
+ +"\"key1\" : null,\n"
+ +"\"key2\" : true,\n"
+ +"\"key3\" : false,\n"
+ +"\"key4\" : [ false, null, true ]\n"
+ +"}"
+ ;
+
+ JsonParser p = createParserUsingStream(JSON_FACTORY, DOC, "UTF-8");
+ _testKeywords(p, true);
+ p.close();
+
+ p = createParserUsingReader(JSON_FACTORY, DOC);
+ _testKeywords(p, true);
+ p.close();
+
+ p = createParserForDataInput(JSON_FACTORY, new MockDataInput(DOC));
+ _testKeywords(p, false);
+ p.close();
+ }
+
+ private void _testKeywords(JsonParser p, boolean checkColumn) throws Exception
+ {
+ JsonStreamContext ctxt = p.getParsingContext();
+ assertEquals("/", ctxt.toString());
+ assertTrue(ctxt.inRoot());
+ assertFalse(ctxt.inArray());
+ assertFalse(ctxt.inObject());
+ assertEquals(0, ctxt.getEntryCount());
+ assertEquals(0, ctxt.getCurrentIndex());
+
+ // Before advancing to content, we should have following default state...
+ assertFalse(p.hasCurrentToken());
+ assertNull(p.getText());
+ assertNull(p.getTextCharacters());
+ assertEquals(0, p.getTextLength());
+ // not sure if this is defined but:
+ assertEquals(0, p.getTextOffset());
+
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertEquals("/", ctxt.toString());
+
+ assertTrue(p.hasCurrentToken());
+ JsonLocation loc = p.getTokenLocation();
+ assertNotNull(loc);
+ assertEquals(1, loc.getLineNr());
+ if (checkColumn) {
+ assertEquals(1, loc.getColumnNr());
+ }
+
+ ctxt = p.getParsingContext();
+ assertFalse(ctxt.inRoot());
+ assertFalse(ctxt.inArray());
+ assertTrue(ctxt.inObject());
+ assertEquals(0, ctxt.getEntryCount());
+ assertEquals(0, ctxt.getCurrentIndex());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ verifyFieldName(p, "key1");
+ assertEquals("{\"key1\"}", ctxt.toString());
+ assertEquals(2, p.getTokenLocation().getLineNr());
+
+ ctxt = p.getParsingContext();
+ assertFalse(ctxt.inRoot());
+ assertFalse(ctxt.inArray());
+ assertTrue(ctxt.inObject());
+ assertEquals(1, ctxt.getEntryCount());
+ assertEquals(0, ctxt.getCurrentIndex());
+ assertEquals("key1", ctxt.getCurrentName());
+
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertEquals("key1", ctxt.getCurrentName());
+
+ ctxt = p.getParsingContext();
+ assertEquals(1, ctxt.getEntryCount());
+ assertEquals(0, ctxt.getCurrentIndex());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ verifyFieldName(p, "key2");
+ ctxt = p.getParsingContext();
+ assertEquals(2, ctxt.getEntryCount());
+ assertEquals(1, ctxt.getCurrentIndex());
+ assertEquals("key2", ctxt.getCurrentName());
+
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+ assertEquals("key2", ctxt.getCurrentName());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ verifyFieldName(p, "key3");
+ assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ verifyFieldName(p, "key4");
+
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ ctxt = p.getParsingContext();
+ assertTrue(ctxt.inArray());
+ assertNull(ctxt.getCurrentName());
+ assertEquals("key4", ctxt.getParent().getCurrentName());
+
+ assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+ assertEquals("[0]", ctxt.toString());
+
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+
+ ctxt = p.getParsingContext();
+ assertTrue(ctxt.inObject());
+
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ ctxt = p.getParsingContext();
+ assertTrue(ctxt.inRoot());
+ assertNull(ctxt.getCurrentName());
+ }
+
+ public void testSkipping() throws Exception {
+ _testSkipping(MODE_INPUT_STREAM);
+ _testSkipping(MODE_INPUT_STREAM_THROTTLED);
+ _testSkipping(MODE_READER);
+ _testSkipping(MODE_DATA_INPUT);
+ }
+
+ private void _testSkipping(int mode) throws Exception
+ {
+ // InputData has some limitations to take into consideration
+ boolean isInputData = (mode == MODE_DATA_INPUT);
+ String DOC =
+ "[ 1, 3, [ true, null ], 3, { \"a\":\"b\" }, [ [ ] ], { } ]";
+ ;
+ JsonParser p = createParser(mode, DOC);
+
+ // First, skipping of the whole thing
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ p.skipChildren();
+ assertEquals(JsonToken.END_ARRAY, p.currentToken());
+ if (!isInputData) {
+ JsonToken t = p.nextToken();
+ if (t != null) {
+ fail("Expected null at end of doc, got "+t);
+ }
+ }
+ p.close();
+
+ // Then individual ones
+ p = createParser(mode, DOC);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ p.skipChildren();
+ // shouldn't move
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.currentToken());
+ assertEquals(1, p.getIntValue());
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ // then skip array
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ p.skipChildren();
+ assertToken(JsonToken.END_ARRAY, p.currentToken());
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ p.skipChildren();
+ assertToken(JsonToken.END_OBJECT, p.currentToken());
+
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ p.skipChildren();
+ assertToken(JsonToken.END_ARRAY, p.currentToken());
+
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ p.skipChildren();
+ assertToken(JsonToken.END_OBJECT, p.currentToken());
+
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+
+ p.close();
+ }
+
+ public void testNameEscaping() throws IOException
+ {
+ _testNameEscaping(MODE_INPUT_STREAM);
+ _testNameEscaping(MODE_READER);
+ _testNameEscaping(MODE_DATA_INPUT);
+ }
+
+ private void _testNameEscaping(int mode) throws IOException
+ {
+ final Map<String,String> NAME_MAP = new LinkedHashMap<String,String>();
+ NAME_MAP.put("", "");
+ NAME_MAP.put("\\\"funny\\\"", "\"funny\"");
+ NAME_MAP.put("\\\\", "\\");
+ NAME_MAP.put("\\r", "\r");
+ NAME_MAP.put("\\n", "\n");
+ NAME_MAP.put("\\t", "\t");
+ NAME_MAP.put("\\r\\n", "\r\n");
+ NAME_MAP.put("\\\"\\\"", "\"\"");
+ NAME_MAP.put("Line\\nfeed", "Line\nfeed");
+ NAME_MAP.put("Yet even longer \\\"name\\\"!", "Yet even longer \"name\"!");
+
+ int entry = 0;
+ for (Map.Entry<String,String> en : NAME_MAP.entrySet()) {
+ ++entry;
+ String input = en.getKey();
+ String expResult = en.getValue();
+ final String DOC = "{ \""+input+"\":null}";
+ JsonParser p = createParser(mode, DOC);
+
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ // first, sanity check (field name == getText()
+ String act = p.getCurrentName();
+ assertEquals(act, getAndVerifyText(p));
+ if (!expResult.equals(act)) {
+ String msg = "Failed for name #"+entry+"/"+NAME_MAP.size();
+ if (expResult.length() != act.length()) {
+ fail(msg+": exp length "+expResult.length()+", actual "+act.length());
+ }
+ assertEquals(msg, expResult, act);
+ }
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ p.close();
+ }
+ }
+
+ /**
+ * Unit test that verifies that long text segments are handled
+ * correctly; mostly to stress-test underlying segment-based
+ * text buffer(s).
+ */
+ public void testLongText() throws Exception {
+ // lengths chosen to tease out problems with buffer allocation...
+ _testLongText(310);
+ _testLongText(7700);
+ _testLongText(49000);
+ _testLongText(96000);
+ }
+
+ private void _testLongText(int LEN) throws Exception
+ {
+ StringBuilder sb = new StringBuilder(LEN + 100);
+ Random r = new Random(LEN);
+ while (sb.length() < LEN) {
+ sb.append(r.nextInt());
+ sb.append(" xyz foo");
+ if (r.nextBoolean()) {
+ sb.append(" and \"bar\"");
+ } else if (r.nextBoolean()) {
+ sb.append(" [whatever].... ");
+ } else {
+ // Let's try some more 'exotic' chars
+ sb.append(" UTF-8-fu: try this {\u00E2/\u0BF8/\uA123!} (look funny?)");
+ }
+ if (r.nextBoolean()) {
+ if (r.nextBoolean()) {
+ sb.append('\n');
+ } else if (r.nextBoolean()) {
+ sb.append('\r');
+ } else {
+ sb.append("\r\n");
+ }
+ }
+ }
+ final String VALUE = sb.toString();
+
+ // Let's use real generator to get JSON done right
+ StringWriter sw = new StringWriter(LEN + (LEN >> 2));
+ JsonGenerator g = JSON_FACTORY.createGenerator(sw);
+ g.writeStartObject();
+ g.writeFieldName("doc");
+ g.writeString(VALUE);
+ g.writeEndObject();
+ g.close();
+
+ final String DOC = sw.toString();
+
+ for (int type = 0; type < 4; ++type) {
+ JsonParser p;
+ switch (type) {
+ case MODE_INPUT_STREAM:
+ case MODE_READER:
+ case MODE_DATA_INPUT:
+ p = createParser(type, DOC);
+ break;
+ default:
+ p = JSON_FACTORY.createParser(encodeInUTF32BE(DOC));
+ }
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("doc", p.getCurrentName());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+
+ String act = getAndVerifyText(p);
+ if (act.length() != VALUE.length()) {
+ fail("Expected length "+VALUE.length()+", got "+act.length()+" (mode = "+type+")");
+ }
+ if (!act.equals(VALUE)) {
+ fail("Long text differs");
+ }
+
+ // should still know the field name
+ assertEquals("doc", p.getCurrentName());
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+
+ // InputDate somewhat special, so:
+ if (type != MODE_DATA_INPUT) {
+ assertNull(p.nextToken());
+ }
+ p.close();
+ }
+ }
+
+ /**
+ * Simple unit test that verifies that passing in a byte array
+ * as source works as expected.
+ */
+ public void testBytesAsSource() throws Exception
+ {
+ String JSON = "[ 1, 2, 3, 4 ]";
+ byte[] b = JSON.getBytes("UTF-8");
+ int offset = 50;
+ int len = b.length;
+ byte[] src = new byte[offset + len + offset];
+
+ System.arraycopy(b, 0, src, offset, len);
+
+ JsonParser p = JSON_FACTORY.createParser(src, offset, len);
+
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(1, p.getIntValue());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(2, p.getIntValue());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(3, p.getIntValue());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(4, p.getIntValue());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertNull(p.nextToken());
+
+ p.close();
+ }
+
+ public void testUtf8BOMHandling() throws Exception
+ {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ // first, write BOM:
+ bytes.write(0xEF);
+ bytes.write(0xBB);
+ bytes.write(0xBF);
+ bytes.write("[ 1 ]".getBytes("UTF-8"));
+ byte[] input = bytes.toByteArray();
+
+ JsonParser p = JSON_FACTORY.createParser(input);
+ assertEquals(JsonToken.START_ARRAY, p.nextToken());
+ // should also have skipped first 3 bytes of BOM; but do we have offset available?
+ /* 08-Oct-2013, tatu: Alas, due to [core#111], we have to omit BOM in calculations
+ * as we do not know what the offset is due to -- may need to revisit, if this
+ * discrepancy becomes an issue. For now it just means that BOM is considered
+ * "out of stream" (not part of input).
+ */
+ JsonLocation loc = p.getTokenLocation();
+ // so if BOM was consider in-stream (part of input), this should expect 3:
+ assertEquals(0, loc.getByteOffset());
+ assertEquals(-1, loc.getCharOffset());
+ assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+
+ p = JSON_FACTORY.createParser(new MockDataInput(input));
+ assertEquals(JsonToken.START_ARRAY, p.nextToken());
+ // same BOM, but DataInput is more restrctive so can skip but offsets
+ // are not reliable...
+ loc = p.getTokenLocation();
+ assertNotNull(loc);
+ assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+ }
+
+ // [core#48]
+ public void testSpacesInURL() throws Exception
+ {
+ File f = File.createTempFile("pre fix&stuff", ".txt");
+ BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f), "UTF-8"));
+ w.write("{ }");
+ w.close();
+ URL url = f.toURI().toURL();
+
+ JsonParser p = JSON_FACTORY.createParser(url);
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ p.close();
+ }
+
+ // [core#142]
+ public void testHandlingOfInvalidSpaceByteStream() throws Exception {
+ _testHandlingOfInvalidSpace(MODE_INPUT_STREAM);
+ _testHandlingOfInvalidSpaceFromResource(true);
+ }
+
+ // [core#142]
+ public void testHandlingOfInvalidSpaceChars() throws Exception {
+ _testHandlingOfInvalidSpace(MODE_READER);
+ _testHandlingOfInvalidSpaceFromResource(false);
+ }
+
+ // [core#142]
+ public void testHandlingOfInvalidSpaceDataInput() throws Exception {
+ _testHandlingOfInvalidSpace(MODE_DATA_INPUT);
+ }
+
+ private void _testHandlingOfInvalidSpace(int mode) throws Exception
+ {
+ final String JSON = "{ \u00A0 \"a\":1}";
+
+ JsonParser p = createParser(mode, JSON);
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ try {
+ p.nextToken();
+ fail("Should have failed");
+ } catch (JsonParseException e) {
+ verifyException(e, "unexpected character");
+ // and correct error code
+ verifyException(e, "code 160");
+ }
+ p.close();
+ }
+
+ private void _testHandlingOfInvalidSpaceFromResource(boolean useStream) throws Exception
+ {
+ InputStream in = getClass().getResourceAsStream("/test_0xA0.json");
+ JsonParser p = useStream
+ ? JSON_FACTORY.createParser(in)
+ : JSON_FACTORY.createParser(new InputStreamReader(in, "UTF-8"));
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ try {
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("request", p.getCurrentName());
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("mac", p.getCurrentName());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertNotNull(p.getText());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("data", p.getCurrentName());
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+
+ // ... and from there on, just loop
+
+ while (p.nextToken() != null) { }
+ fail("Should have failed");
+ } catch (JsonParseException e) {
+ verifyException(e, "unexpected character");
+ // and correct error code
+ verifyException(e, "code 160");
+ }
+ p.close();
+ }
+
+ public void testGetValueAsTextBytes() throws Exception
+ {
+ _testGetValueAsText(MODE_INPUT_STREAM, false);
+ _testGetValueAsText(MODE_INPUT_STREAM, true);
+ }
+
+ public void testGetValueAsTextDataInput() throws Exception
+ {
+ _testGetValueAsText(MODE_DATA_INPUT, false);
+ _testGetValueAsText(MODE_DATA_INPUT, true);
+ }
+
+ public void testGetValueAsTextChars() throws Exception
+ {
+ _testGetValueAsText(MODE_READER, false);
+ _testGetValueAsText(MODE_READER, true);
+ }
+
+ private void _testGetValueAsText(int mode, boolean delegate) throws Exception
+ {
+ String JSON = "{\"a\":1,\"b\":true,\"c\":null,\"d\":\"foo\"}";
+ JsonParser p = createParser(mode, JSON);
+ if (delegate) {
+ p = new JsonParserDelegate(p);
+ }
+
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertNull(p.getValueAsString());
+ assertEquals("foobar", p.getValueAsString("foobar"));
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("a", p.getText());
+ assertEquals("a", p.getValueAsString());
+ assertEquals("a", p.getValueAsString("default"));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals("1", p.getValueAsString());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("b", p.getValueAsString());
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+ assertEquals("true", p.getValueAsString());
+ assertEquals("true", p.getValueAsString("foobar"));
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("c", p.getValueAsString());
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ // null token returned as Java null, as per javadoc
+ assertNull(p.getValueAsString());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("d", p.getValueAsString());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("foo", p.getValueAsString("default"));
+ assertEquals("foo", p.getValueAsString());
+
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ assertNull(p.getValueAsString());
+
+ // InputData can't peek into end-of-input so:
+ if (mode != MODE_DATA_INPUT) {
+ assertNull(p.nextToken());
+ }
+ p.close();
+ }
+
+ public void testGetTextViaWriter() throws Exception
+ {
+ for (int mode : ALL_MODES) {
+ _testGetTextViaWriter(mode);
+ }
+ }
+
+ private void _testGetTextViaWriter(int mode) throws Exception
+ {
+ final String INPUT_TEXT = "this is a sample text for json parsing using readText() method";
+ final String JSON = "{\"a\":\""+INPUT_TEXT+"\",\"b\":true,\"c\":null,\"d\":\"foo\"}";
+ JsonParser parser = createParser(mode, JSON);
+ assertToken(JsonToken.START_OBJECT, parser.nextToken());
+ assertToken(JsonToken.FIELD_NAME, parser.nextToken());
+ assertEquals("a", parser.getCurrentName());
+ assertToken(JsonToken.VALUE_STRING, parser.nextToken());
+
+ Writer writer = new StringWriter();
+ int len = parser.getText(writer);
+ String resultString = writer.toString();
+ assertEquals(len, resultString.length());
+ assertEquals(INPUT_TEXT, resultString);
+ parser.close();
+ }
+
+ public void testLongerReadText() throws Exception
+ {
+ for (int mode : ALL_MODES) {
+ _testLongerReadText(mode);
+ }
+ }
+
+ private void _testLongerReadText(int mode) throws Exception
+ {
+ StringBuilder builder = new StringBuilder();
+ for(int i= 0; i < 1000; i++) {
+ builder.append("Sample Text"+i);
+ }
+ String longText = builder.toString();
+ final String JSON = "{\"a\":\""+ longText +"\",\"b\":true,\"c\":null,\"d\":\"foo\"}";
+ JsonParser parser = createParser(MODE_READER, JSON);
+ assertToken(JsonToken.START_OBJECT, parser.nextToken());
+ assertToken(JsonToken.FIELD_NAME, parser.nextToken());
+ assertEquals("a", parser.getCurrentName());
+ assertToken(JsonToken.VALUE_STRING, parser.nextToken());
+
+ Writer writer = new StringWriter();
+ int len = parser.getText(writer);
+ String resultString = writer.toString();
+ assertEquals(len, resultString.length());
+ assertEquals(longText, resultString);
+ parser.close();
+ }
+
+ /*
+ /**********************************************************
+ /* Helper methods
+ /**********************************************************
+ */
+
+ private void _doTestSpec(boolean verify) throws IOException
+ {
+ JsonParser p;
+
+ // First, using a StringReader:
+ p = createParserUsingReader(JSON_FACTORY, SAMPLE_DOC_JSON_SPEC);
+ verifyJsonSpecSampleDoc(p, verify);
+ p.close();
+
+ // Then with streams using supported encodings:
+ p = createParserUsingStream(JSON_FACTORY, SAMPLE_DOC_JSON_SPEC, "UTF-8");
+ verifyJsonSpecSampleDoc(p, verify);
+ p.close();
+ p = createParserUsingStream(JSON_FACTORY, SAMPLE_DOC_JSON_SPEC, "UTF-16BE");
+ verifyJsonSpecSampleDoc(p, verify);
+ p.close();
+ p = createParserUsingStream(JSON_FACTORY, SAMPLE_DOC_JSON_SPEC, "UTF-16LE");
+ verifyJsonSpecSampleDoc(p, verify);
+ p.close();
+
+ // Hmmh. UTF-32 is harder only because JDK doesn't come with
+ // a codec for it. Can't test it yet using this method
+ p = createParserUsingStream(JSON_FACTORY, SAMPLE_DOC_JSON_SPEC, "UTF-32");
+ verifyJsonSpecSampleDoc(p, verify);
+ p.close();
+
+ // and finally, new (as of May 2016) source, DataInput:
+ p = createParserForDataInput(JSON_FACTORY, new MockDataInput(SAMPLE_DOC_JSON_SPEC));
+ verifyJsonSpecSampleDoc(p, verify);
+ p.close();
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/read/NextXxxAccessTest.java b/src/test/java/com/fasterxml/jackson/core/read/NextXxxAccessTest.java
new file mode 100644
index 0000000..6fb4680
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/read/NextXxxAccessTest.java
@@ -0,0 +1,524 @@
+package com.fasterxml.jackson.core.read;
+
+import java.util.Random;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.SerializedString;
+
+public class NextXxxAccessTest
+ extends com.fasterxml.jackson.core.BaseTest
+{
+ /*
+ /********************************************************
+ /* Wrappers to test InputStream vs Reader
+ /********************************************************
+ */
+
+ public void testIsNextTokenName() throws Exception
+ {
+ _testIsNextTokenName1(MODE_INPUT_STREAM);
+ _testIsNextTokenName1(MODE_INPUT_STREAM_THROTTLED);
+ _testIsNextTokenName1(MODE_DATA_INPUT);
+ _testIsNextTokenName1(MODE_READER);
+ }
+
+ public void testIsNextTokenName2() throws Exception {
+ _testIsNextTokenName2(MODE_INPUT_STREAM);
+ _testIsNextTokenName2(MODE_INPUT_STREAM_THROTTLED);
+ _testIsNextTokenName2(MODE_DATA_INPUT);
+ _testIsNextTokenName2(MODE_READER);
+ }
+
+ public void testIsNextTokenName3() throws Exception {
+ _testIsNextTokenName3(MODE_INPUT_STREAM);
+ _testIsNextTokenName3(MODE_INPUT_STREAM_THROTTLED);
+ _testIsNextTokenName3(MODE_DATA_INPUT);
+ _testIsNextTokenName3(MODE_READER);
+ }
+
+ public void testIsNextTokenName4() throws Exception {
+ _testIsNextTokenName4(MODE_INPUT_STREAM);
+ _testIsNextTokenName4(MODE_INPUT_STREAM_THROTTLED);
+ _testIsNextTokenName4(MODE_DATA_INPUT);
+ _testIsNextTokenName4(MODE_READER);
+ }
+
+ // [jackson-core#34]
+ public void testIssue34() throws Exception
+ {
+ _testIssue34(MODE_INPUT_STREAM);
+ _testIssue34(MODE_INPUT_STREAM_THROTTLED);
+ _testIssue34(MODE_DATA_INPUT);
+ _testIssue34(MODE_READER);
+ }
+
+ // [jackson-core#38] with nextFieldName
+ public void testIssue38() throws Exception
+ {
+ _testIssue38(MODE_INPUT_STREAM);
+ _testIssue38(MODE_INPUT_STREAM_THROTTLED);
+ _testIssue38(MODE_DATA_INPUT);
+ _testIssue38(MODE_READER);
+ }
+
+ public void testNextNameWithLongContent() throws Exception
+ {
+ _testNextNameWithLong(MODE_INPUT_STREAM);
+ _testNextNameWithLong(MODE_INPUT_STREAM_THROTTLED);
+ _testNextNameWithLong(MODE_DATA_INPUT);
+ _testNextNameWithLong(MODE_READER);
+ }
+
+ // for [core#220]: problem with `nextFieldName(str)`, indented content
+ public void testNextNameWithIndentation() throws Exception
+ {
+ _testNextFieldNameIndent(MODE_INPUT_STREAM);
+ _testNextFieldNameIndent(MODE_INPUT_STREAM_THROTTLED);
+ _testNextFieldNameIndent(MODE_DATA_INPUT);
+ _testNextFieldNameIndent(MODE_READER);
+ }
+
+ public void testNextTextValue() throws Exception
+ {
+ _textNextText(MODE_INPUT_STREAM);
+ _textNextText(MODE_INPUT_STREAM_THROTTLED);
+ _textNextText(MODE_DATA_INPUT);
+ _textNextText(MODE_READER);
+ }
+
+ public void testNextIntValue() throws Exception
+ {
+ _textNextInt(MODE_INPUT_STREAM);
+ _textNextInt(MODE_INPUT_STREAM_THROTTLED);
+ _textNextInt(MODE_DATA_INPUT);
+ _textNextInt(MODE_READER);
+ }
+
+ public void testNextLongValue() throws Exception
+ {
+ _textNextLong(MODE_INPUT_STREAM);
+ _textNextLong(MODE_INPUT_STREAM_THROTTLED);
+ _textNextLong(MODE_DATA_INPUT);
+ _textNextLong(MODE_READER);
+ }
+
+ public void testNextBooleanValue() throws Exception
+ {
+ _textNextBoolean(MODE_INPUT_STREAM);
+ _textNextBoolean(MODE_INPUT_STREAM_THROTTLED);
+ _textNextBoolean(MODE_DATA_INPUT);
+ _textNextBoolean(MODE_READER);
+ }
+
+ /*
+ /********************************************************
+ /* Actual test code
+ /********************************************************
+ */
+
+ private void _testIsNextTokenName1(int mode) throws Exception
+ {
+ final String DOC = "{\"name\":123,\"name2\":14,\"x\":\"name\"}";
+ JsonParser p = createParser(mode, DOC);
+ final SerializedString NAME = new SerializedString("name");
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.START_OBJECT, p.currentToken());
+ assertEquals(JsonTokenId.ID_START_OBJECT, p.currentTokenId());
+ assertTrue(p.nextFieldName(NAME));
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals(NAME.getValue(), p.getCurrentName());
+ assertEquals(NAME.getValue(), p.getText());
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.currentToken());
+ assertEquals(123, p.getIntValue());
+
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals("name2", p.getCurrentName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ // do NOT check number value, to enforce skipping
+
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals("x", p.getCurrentName());
+
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.VALUE_STRING, p.currentToken());
+
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.END_OBJECT, p.currentToken());
+
+ if (mode != MODE_DATA_INPUT) {
+ assertFalse(p.nextFieldName(NAME));
+ assertNull(p.currentToken());
+ }
+ p.close();
+
+ // Actually, try again with slightly different sequence...
+ p = createParser(mode, DOC);
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertFalse(p.nextFieldName(new SerializedString("Nam")));
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals(NAME.getValue(), p.getCurrentName());
+ assertEquals(NAME.getValue(), p.getText());
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.currentToken());
+ assertEquals(123, p.getIntValue());
+
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals("name2", p.getCurrentName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals("x", p.getCurrentName());
+
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.VALUE_STRING, p.currentToken());
+
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.END_OBJECT, p.currentToken());
+ if (mode != MODE_DATA_INPUT) {
+ assertFalse(p.nextFieldName(NAME));
+ assertNull(p.currentToken());
+ }
+ p.close();
+ }
+
+ private void _testIsNextTokenName2(int mode) throws Exception
+ {
+ final String DOC = "{\"name\":123,\"name2\":14,\"x\":\"name\"}";
+ JsonParser p = createParser(mode, DOC);
+ SerializableString NAME = new SerializedString("name");
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.START_OBJECT, p.currentToken());
+ assertTrue(p.nextFieldName(NAME));
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals(NAME.getValue(), p.getCurrentName());
+ assertEquals(NAME.getValue(), p.getText());
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.currentToken());
+ assertEquals(123, p.getIntValue());
+
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals("name2", p.getCurrentName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals("x", p.getCurrentName());
+
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.VALUE_STRING, p.currentToken());
+
+ assertFalse(p.nextFieldName(NAME));
+ assertToken(JsonToken.END_OBJECT, p.currentToken());
+ if (mode != MODE_DATA_INPUT) {
+ assertFalse(p.nextFieldName(NAME));
+ assertNull(p.currentToken());
+ }
+ p.close();
+ }
+
+ private void _testIsNextTokenName3(int mode) throws Exception
+ {
+ final String DOC = "{\"name\":123,\"name2\":14,\"x\":\"name\"}";
+ JsonParser p = createParser(mode, DOC);
+ assertNull(p.nextFieldName());
+ assertToken(JsonToken.START_OBJECT, p.currentToken());
+ assertEquals("name", p.nextFieldName());
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals("name", p.getCurrentName());
+ assertEquals("name", p.getText());
+ assertNull(p.nextFieldName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.currentToken());
+ assertEquals(123, p.getIntValue());
+
+ assertEquals("name2", p.nextFieldName());
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals("name2", p.getCurrentName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+
+ assertEquals("x", p.nextFieldName());
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals("x", p.getCurrentName());
+
+ assertNull(p.nextFieldName());
+ assertToken(JsonToken.VALUE_STRING, p.currentToken());
+
+ assertNull(p.nextFieldName());
+ assertToken(JsonToken.END_OBJECT, p.currentToken());
+ if (mode != MODE_DATA_INPUT) {
+ assertNull(p.nextFieldName());
+ assertNull(p.currentToken());
+ }
+ p.close();
+ }
+
+ private void _testIsNextTokenName4(int mode) throws Exception
+ {
+ final String DOC = "{\"name\":-123,\"name2\":99}";
+ JsonParser p = createParser(mode, DOC);
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+
+ assertTrue(p.nextFieldName(new SerializedString("name")));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(-123, p.getIntValue());
+
+ assertTrue(p.nextFieldName(new SerializedString("name2")));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(99, p.getIntValue());
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ if (mode != MODE_DATA_INPUT) {
+ assertNull(p.nextToken());
+ }
+ p.close();
+ }
+
+ private void _testNextFieldNameIndent(int mode) throws Exception
+ {
+ final String DOC = "{\n \"name\" : \n [\n ]\n }";
+ JsonParser p = createParser(mode, DOC);
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertTrue(p.nextFieldName(new SerializedString("name")));
+
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ if (mode != MODE_DATA_INPUT) {
+ assertNull(p.nextToken());
+ }
+ p.close();
+ }
+
+ private void _textNextText(int mode) throws Exception
+ {
+ final String DOC = aposToQuotes("{'a':'123','b':5,'c':[false,'foo']}");
+ JsonParser p = createParser(mode, DOC);
+ assertNull(p.nextTextValue());
+ assertToken(JsonToken.START_OBJECT, p.currentToken());
+ assertNull(p.nextTextValue());
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals("a", p.getCurrentName());
+
+ assertEquals("123", p.nextTextValue());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("b", p.getCurrentName());
+ assertNull(p.nextFieldName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.currentToken());
+
+ assertEquals("c", p.nextFieldName());
+
+ assertNull(p.nextTextValue());
+ assertToken(JsonToken.START_ARRAY, p.currentToken());
+ assertNull(p.nextTextValue());
+ assertToken(JsonToken.VALUE_FALSE, p.currentToken());
+ assertEquals("foo", p.nextTextValue());
+
+ assertNull(p.nextTextValue());
+ assertToken(JsonToken.END_ARRAY, p.currentToken());
+ assertNull(p.nextTextValue());
+ assertToken(JsonToken.END_OBJECT, p.currentToken());
+ if (mode != MODE_DATA_INPUT) {
+ assertNull(p.nextTextValue());
+ assertNull(p.currentToken());
+ }
+ p.close();
+ }
+
+ private void _textNextInt(int mode) throws Exception
+ {
+ final String DOC = aposToQuotes("{'a':'123','b':5,'c':[false,456]}");
+ JsonParser p = createParser(mode, DOC);
+ assertEquals(0, p.nextIntValue(0));
+ assertToken(JsonToken.START_OBJECT, p.currentToken());
+ assertEquals(0, p.nextIntValue(0));
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals("a", p.getCurrentName());
+
+ assertEquals(0, p.nextIntValue(0));
+ assertToken(JsonToken.VALUE_STRING, p.currentToken());
+ assertEquals("123", p.getText());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("b", p.getCurrentName());
+ assertEquals(5, p.nextIntValue(0));
+
+ assertEquals("c", p.nextFieldName());
+
+ assertEquals(0, p.nextIntValue(0));
+ assertToken(JsonToken.START_ARRAY, p.currentToken());
+ assertEquals(0, p.nextIntValue(0));
+ assertToken(JsonToken.VALUE_FALSE, p.currentToken());
+ assertEquals(456, p.nextIntValue(0));
+
+ assertEquals(0, p.nextIntValue(0));
+ assertToken(JsonToken.END_ARRAY, p.currentToken());
+ assertEquals(0, p.nextIntValue(0));
+ assertToken(JsonToken.END_OBJECT, p.currentToken());
+ if (mode != MODE_DATA_INPUT) {
+ assertEquals(0, p.nextIntValue(0));
+ assertNull(p.currentToken());
+ }
+ p.close();
+ }
+
+ private void _textNextLong(int mode) throws Exception
+ {
+ final String DOC = aposToQuotes("{'a':'xyz','b':-59,'c':[false,-1]}");
+ JsonParser p = createParser(mode, DOC);
+ assertEquals(0L, p.nextLongValue(0L));
+ assertToken(JsonToken.START_OBJECT, p.currentToken());
+ assertEquals(0L, p.nextLongValue(0L));
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals("a", p.getCurrentName());
+
+ assertEquals(0L, p.nextLongValue(0L));
+ assertToken(JsonToken.VALUE_STRING, p.currentToken());
+ assertEquals("xyz", p.getText());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("b", p.getCurrentName());
+ assertEquals(-59L, p.nextLongValue(0L));
+
+ assertEquals("c", p.nextFieldName());
+
+ assertEquals(0L, p.nextLongValue(0L));
+ assertToken(JsonToken.START_ARRAY, p.currentToken());
+ assertEquals(0L, p.nextLongValue(0L));
+ assertToken(JsonToken.VALUE_FALSE, p.currentToken());
+ assertEquals(-1L, p.nextLongValue(0L));
+
+ assertEquals(0L, p.nextLongValue(0L));
+ assertToken(JsonToken.END_ARRAY, p.currentToken());
+ assertEquals(0L, p.nextLongValue(0L));
+ assertToken(JsonToken.END_OBJECT, p.currentToken());
+ if (mode != MODE_DATA_INPUT) {
+ assertEquals(0L, p.nextLongValue(0L));
+ assertNull(p.currentToken());
+ }
+ p.close();
+ }
+
+ private void _textNextBoolean(int mode) throws Exception
+ {
+ final String DOC = aposToQuotes("{'a':'xyz','b':true,'c':[false,0]}");
+ JsonParser p = createParser(mode, DOC);
+ assertNull(p.nextBooleanValue());
+ assertToken(JsonToken.START_OBJECT, p.currentToken());
+ assertNull(p.nextBooleanValue());
+ assertToken(JsonToken.FIELD_NAME, p.currentToken());
+ assertEquals("a", p.getCurrentName());
+
+ assertNull(p.nextBooleanValue());
+ assertToken(JsonToken.VALUE_STRING, p.currentToken());
+ assertEquals("xyz", p.getText());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("b", p.getCurrentName());
+ assertEquals(Boolean.TRUE, p.nextBooleanValue());
+
+ assertEquals("c", p.nextFieldName());
+
+ assertNull(p.nextBooleanValue());
+ assertToken(JsonToken.START_ARRAY, p.currentToken());
+ assertEquals(Boolean.FALSE, p.nextBooleanValue());
+ assertNull(p.nextBooleanValue());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.currentToken());
+ assertEquals(0, p.getIntValue());
+
+ assertNull(p.nextBooleanValue());
+ assertToken(JsonToken.END_ARRAY, p.currentToken());
+ assertNull(p.nextBooleanValue());
+ assertToken(JsonToken.END_OBJECT, p.currentToken());
+ if (mode != MODE_DATA_INPUT) {
+ assertNull(p.nextBooleanValue());
+ assertNull(p.currentToken());
+ }
+ p.close();
+ }
+
+ private void _testIssue34(int mode) throws Exception
+ {
+ final int TESTROUNDS = 223;
+ final String DOC_PART = "{ \"fieldName\": 1 }";
+
+ // build the big document to trigger issue
+ StringBuilder sb = new StringBuilder(2000);
+ for (int i = 0; i < TESTROUNDS; ++i) {
+ sb.append(DOC_PART);
+ }
+ final String DOC = sb.toString();
+
+ SerializableString fieldName = new SerializedString("fieldName");
+ JsonParser parser = createParser(mode, DOC);
+
+ for (int i = 0; i < TESTROUNDS - 1; i++) {
+ assertEquals(JsonToken.START_OBJECT, parser.nextToken());
+
+ // These will succeed
+ assertTrue(parser.nextFieldName(fieldName));
+
+ parser.nextLongValue(-1);
+ assertEquals(JsonToken.END_OBJECT, parser.nextToken());
+ }
+
+ assertEquals(JsonToken.START_OBJECT, parser.nextToken());
+
+ // This will fail
+ assertTrue(parser.nextFieldName(fieldName));
+ parser.close();
+ }
+
+ private void _testIssue38(int mode) throws Exception
+ {
+ final String DOC = "{\"field\" :\"value\"}";
+ SerializableString fieldName = new SerializedString("field");
+ JsonParser parser = createParser(mode, DOC);
+ assertEquals(JsonToken.START_OBJECT, parser.nextToken());
+ assertTrue(parser.nextFieldName(fieldName));
+ assertEquals(JsonToken.VALUE_STRING, parser.nextToken());
+ assertEquals("value", parser.getText());
+ assertEquals(JsonToken.END_OBJECT, parser.nextToken());
+ if (mode != MODE_DATA_INPUT) {
+ assertNull(parser.nextToken());
+ }
+ parser.close();
+ }
+
+ private void _testNextNameWithLong(int mode) throws Exception
+ {
+ // do 5 meg thingy
+ final int SIZE = 5 * 1024 * 1024;
+ StringBuilder sb = new StringBuilder(SIZE + 20);
+
+ sb.append("{");
+ Random rnd = new Random(1);
+ int count = 0;
+ while (sb.length() < SIZE) {
+ ++count;
+ if (sb.length() > 1) {
+ sb.append(", ");
+ }
+ int val = rnd.nextInt();
+ sb.append('"');
+ sb.append("f"+val);
+ sb.append("\":");
+ sb.append(String.valueOf(val % 1000));
+ }
+ sb.append("}");
+ final String DOC = sb.toString();
+
+ JsonParser parser = createParser(mode, DOC);
+ assertToken(JsonToken.START_OBJECT, parser.nextToken());
+ rnd = new Random(1);
+ for (int i = 0; i < count; ++i) {
+ int exp = rnd.nextInt();
+ SerializableString expName = new SerializedString("f"+exp);
+ assertTrue(parser.nextFieldName(expName));
+ assertToken(JsonToken.VALUE_NUMBER_INT, parser.nextToken());
+ assertEquals(exp % 1000, parser.getIntValue());
+ }
+ assertToken(JsonToken.END_OBJECT, parser.nextToken());
+ parser.close();
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/read/NonStandardParserFeaturesTest.java b/src/test/java/com/fasterxml/jackson/core/read/NonStandardParserFeaturesTest.java
new file mode 100644
index 0000000..b26c46e
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/read/NonStandardParserFeaturesTest.java
@@ -0,0 +1,537 @@
+package com.fasterxml.jackson.core.read;
+
+import com.fasterxml.jackson.core.*;
+
+public class NonStandardParserFeaturesTest
+ extends com.fasterxml.jackson.core.BaseTest
+{
+ public void testSimpleUnquotedBytes() throws Exception {
+ _testSimpleUnquoted(MODE_INPUT_STREAM);
+ _testSimpleUnquoted(MODE_INPUT_STREAM_THROTTLED);
+ _testSimpleUnquoted(MODE_DATA_INPUT);
+ }
+
+ public void testSimpleUnquotedChars() throws Exception {
+ _testSimpleUnquoted(MODE_READER);
+ }
+
+ public void testLargeUnquoted() throws Exception
+ {
+ _testLargeUnquoted(MODE_INPUT_STREAM);
+ _testLargeUnquoted(MODE_INPUT_STREAM_THROTTLED);
+ _testLargeUnquoted(MODE_DATA_INPUT);
+ _testLargeUnquoted(MODE_READER);
+ }
+
+ public void testSingleQuotesDefault() throws Exception
+ {
+ _testSingleQuotesDefault(MODE_INPUT_STREAM);
+ _testSingleQuotesDefault(MODE_INPUT_STREAM_THROTTLED);
+ _testSingleQuotesDefault(MODE_DATA_INPUT);
+ _testSingleQuotesDefault(MODE_READER);
+ }
+
+ public void testSingleQuotesEnabled() throws Exception
+ {
+ _testSingleQuotesEnabled(MODE_INPUT_STREAM);
+ _testSingleQuotesEnabled(MODE_INPUT_STREAM_THROTTLED);
+ _testSingleQuotesEnabled(MODE_DATA_INPUT);
+ _testSingleQuotesEnabled(MODE_READER);
+
+ _testSingleQuotesEscaped(MODE_INPUT_STREAM);
+ _testSingleQuotesEscaped(MODE_INPUT_STREAM_THROTTLED);
+ _testSingleQuotesEscaped(MODE_DATA_INPUT);
+ _testSingleQuotesEscaped(MODE_READER);
+ }
+
+ public void testNonStandardNameChars() throws Exception
+ {
+ _testNonStandardNameChars(MODE_INPUT_STREAM);
+ _testNonStandardNameChars(MODE_INPUT_STREAM_THROTTLED);
+ _testNonStandardNameChars(MODE_DATA_INPUT);
+ _testNonStandardNameChars(MODE_READER);
+ }
+
+ public void testNonStandardAnyCharQuoting() throws Exception
+ {
+ _testNonStandarBackslashQuoting(MODE_INPUT_STREAM);
+ _testNonStandarBackslashQuoting(MODE_INPUT_STREAM_THROTTLED);
+ _testNonStandarBackslashQuoting(MODE_DATA_INPUT);
+ _testNonStandarBackslashQuoting(MODE_READER);
+ }
+
+ public void testLeadingZeroesUTF8() throws Exception {
+ _testLeadingZeroes(MODE_INPUT_STREAM, false);
+ _testLeadingZeroes(MODE_INPUT_STREAM, true);
+ _testLeadingZeroes(MODE_INPUT_STREAM_THROTTLED, false);
+ _testLeadingZeroes(MODE_INPUT_STREAM_THROTTLED, true);
+
+ // 17-May-2016, tatu: With DataInput, must have trailing space
+ // since there's no way to detect end of input
+ _testLeadingZeroes(MODE_DATA_INPUT, true);
+ }
+
+ public void testLeadingZeroesReader() throws Exception {
+ _testLeadingZeroes(MODE_READER, false);
+ _testLeadingZeroes(MODE_READER, true);
+ }
+
+ // allow NaN
+ public void testAllowNaN() throws Exception {
+ _testAllowNaN(MODE_INPUT_STREAM);
+ _testAllowNaN(MODE_INPUT_STREAM_THROTTLED);
+ _testAllowNaN(MODE_DATA_INPUT);
+ _testAllowNaN(MODE_READER);
+ }
+
+ // allow +Inf/-Inf
+ public void testAllowInfinity() throws Exception {
+ _testAllowInf(MODE_INPUT_STREAM);
+ _testAllowInf(MODE_INPUT_STREAM_THROTTLED);
+ _testAllowInf(MODE_DATA_INPUT);
+ _testAllowInf(MODE_READER);
+ }
+
+ /*
+ /****************************************************************
+ /* Secondary test methods
+ /****************************************************************
+ */
+
+ private void _testLargeUnquoted(int mode) throws Exception
+ {
+ StringBuilder sb = new StringBuilder(5000);
+ sb.append("[\n");
+ //final int REPS = 2000;
+ final int REPS = 1050;
+ for (int i = 0; i < REPS; ++i) {
+ if (i > 0) {
+ sb.append(',');
+ if ((i & 7) == 0) {
+ sb.append('\n');
+ }
+ }
+ sb.append("{");
+ sb.append("abc").append(i&127).append(':');
+ sb.append((i & 1) != 0);
+ sb.append("}\n");
+ }
+ sb.append("]");
+ String JSON = sb.toString();
+ JsonFactory f = new JsonFactory();
+ f.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
+ JsonParser p = createParser(f, mode, JSON);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ for (int i = 0; i < REPS; ++i) {
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("abc"+(i&127), p.getCurrentName());
+ assertToken(((i&1) != 0) ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE, p.nextToken());
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ }
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+ }
+
+
+ private void _testSimpleUnquoted(int mode) throws Exception
+ {
+ final JsonFactory f = new JsonFactory();
+ f.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
+
+ String JSON = "{ a : 1, _foo:true, $:\"money!\", \" \":null }";
+ JsonParser p = createParser(f, mode, JSON);
+
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("a", p.getCurrentName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("_foo", p.getCurrentName());
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("$", p.getCurrentName());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("money!", p.getText());
+
+ // and then regular quoted one should still work too:
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals(" ", p.getCurrentName());
+
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ p.close();
+
+ // Another thing, as per [Issue#102]: numbers
+
+ JSON = "{ 123:true,4:false }";
+ p = createParser(f, mode, JSON);
+
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("123", p.getCurrentName());
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("4", p.getCurrentName());
+ assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ p.close();
+ }
+
+ /**
+ * Test to verify that the default parser settings do not
+ * accept single-quotes for String values (field names,
+ * textual values)
+ */
+ private void _testSingleQuotesDefault(int mode) throws Exception
+ {
+ JsonFactory f = new JsonFactory();
+ // First, let's see that by default they are not allowed
+ String JSON = "[ 'text' ]";
+ JsonParser p = createParser(f, mode, JSON);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ try {
+ p.nextToken();
+ fail("Expected exception");
+ } catch (JsonParseException e) {
+ verifyException(e, "Unexpected character ('''");
+ } finally {
+ p.close();
+ }
+
+ JSON = "{ 'a':1 }";
+ p = createParser(f, mode, JSON);
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ try {
+ p.nextToken();
+ fail("Expected exception");
+ } catch (JsonParseException e) {
+ verifyException(e, "Unexpected character ('''");
+ } finally {
+ p.close();
+ }
+ }
+
+ /**
+ * Test to verify optional handling of
+ * single quotes, to allow handling invalid (but, alas, common)
+ * JSON.
+ */
+ private void _testSingleQuotesEnabled(int mode) throws Exception
+ {
+ JsonFactory f = new JsonFactory();
+ f.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
+
+ String JSON = "{ 'a' : 1, \"foobar\": 'b', '_abcde1234':'d', '\"' : '\"\"', '':'' }";
+ JsonParser p = createParser(f, mode, JSON);
+
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("a", p.getText());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals("1", p.getText());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("foobar", p.getText());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("b", p.getText());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("_abcde1234", p.getText());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("d", p.getText());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("\"", p.getText());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ //assertEquals("\"\"", p.getText());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("", p.getText());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("", p.getText());
+
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ p.close();
+
+
+ JSON = "{'b':1,'array':[{'b':3}],'ob':{'b':4,'x':0,'y':3,'a':false }}";
+ p = createParser(f, mode, JSON);
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("b", p.getCurrentName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("b", p.getCurrentName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(3, p.getIntValue());
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("b", p.getCurrentName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(4, p.getIntValue());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("x", p.getCurrentName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(0, p.getIntValue());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("y", p.getCurrentName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(3, p.getIntValue());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("a", p.getCurrentName());
+ assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ p.close();
+ }
+
+ // test to verify that we implicitly allow escaping of apostrophe
+ private void _testSingleQuotesEscaped(int mode) throws Exception
+ {
+ JsonFactory f = new JsonFactory();
+ f.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
+
+ String JSON = "[ '16\\'' ]";
+ JsonParser p = createParser(f, mode, JSON);
+
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("16'", p.getText());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+ }
+
+ private void _testNonStandardNameChars(int mode) throws Exception
+ {
+ JsonFactory f = new JsonFactory();
+ f.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
+ String JSON = "{ @type : \"mytype\", #color : 123, *error* : true, "
+ +" hyphen-ated : \"yes\", me+my : null"
+ +"}";
+ JsonParser p = createParser(f, mode, JSON);
+
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("@type", p.getText());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("mytype", p.getText());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("#color", p.getText());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(123, p.getIntValue());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("*error*", p.getText());
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("hyphen-ated", p.getText());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("yes", p.getText());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("me+my", p.getText());
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ p.close();
+ }
+
+ private void _testNonStandarBackslashQuoting(int mode) throws Exception
+ {
+ // first: verify that we get an exception
+ JsonFactory f = new JsonFactory();
+ assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER));
+ final String JSON = quote("\\'");
+ JsonParser p = createParser(f, mode, JSON);
+ try {
+ p.nextToken();
+ p.getText();
+ fail("Should have thrown an exception for doc <"+JSON+">");
+ } catch (JsonParseException e) {
+ verifyException(e, "unrecognized character escape");
+ } finally {
+ p.close();
+ }
+ // and then verify it's ok...
+ f.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
+ assertTrue(f.isEnabled(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER));
+ p = createParser(f, mode, JSON);
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("'", p.getText());
+ p.close();
+ }
+
+ private void _testLeadingZeroes(int mode, boolean appendSpace) throws Exception
+ {
+ // first: verify that we get an exception
+ JsonFactory f = new JsonFactory();
+ assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS));
+ String JSON = "00003";
+ if (appendSpace) {
+ JSON += " ";
+ }
+ JsonParser p = createParser(f, mode, JSON);
+ try {
+ p.nextToken();
+ p.getText();
+ fail("Should have thrown an exception for doc <"+JSON+">");
+ } catch (JsonParseException e) {
+ verifyException(e, "invalid numeric value");
+ } finally {
+ p.close();
+ }
+
+ // and then verify it's ok when enabled
+ f.configure(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS, true);
+ assertTrue(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS));
+ p = createParser(f, mode, JSON);
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(3, p.getIntValue());
+ assertEquals("3", p.getText());
+ p.close();
+
+ // Plus, also: verify that leading zero magnitude is ok:
+ JSON = "0"+Integer.MAX_VALUE;
+ if (appendSpace) {
+ JSON += " ";
+ }
+ p = createParser(f, mode, JSON);
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(String.valueOf(Integer.MAX_VALUE), p.getText());
+ assertEquals(Integer.MAX_VALUE, p.getIntValue());
+ Number nr = p.getNumberValue();
+ assertSame(Integer.class, nr.getClass());
+ p.close();
+ }
+
+ private void _testAllowNaN(int mode) throws Exception
+ {
+ final String JSON = "[ NaN]";
+ JsonFactory f = new JsonFactory();
+ assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS));
+
+ // without enabling, should get an exception
+ JsonParser p = createParser(f, mode, JSON);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ try {
+ p.nextToken();
+ fail("Expected exception");
+ } catch (Exception e) {
+ verifyException(e, "non-standard");
+ } finally {
+ p.close();
+ }
+
+ // we can enable it dynamically (impl detail)
+ f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true);
+ p = createParser(f, mode, JSON);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+
+ double d = p.getDoubleValue();
+ assertTrue(Double.isNaN(d));
+ assertEquals("NaN", p.getText());
+
+ // [Issue#98]
+ try {
+ /*BigDecimal dec =*/ p.getDecimalValue();
+ fail("Should fail when trying to access NaN as BigDecimal");
+ } catch (NumberFormatException e) {
+ verifyException(e, "can not be represented as BigDecimal");
+ }
+
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+
+ // finally, should also work with skipping
+ f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true);
+ p = createParser(f, mode, JSON);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+ }
+
+ private void _testAllowInf(int mode) throws Exception
+ {
+ final String JSON = "[ -INF, +INF, +Infinity, Infinity, -Infinity ]";
+ JsonFactory f = new JsonFactory();
+ assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS));
+
+ // without enabling, should get an exception
+ JsonParser p = createParser(f, mode, JSON);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ try {
+ p.nextToken();
+ fail("Expected exception");
+ } catch (Exception e) {
+ verifyException(e, "Non-standard token '-INF'");
+ } finally {
+ p.close();
+ }
+ f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true);
+ p = createParser(f, mode, JSON);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ double d = p.getDoubleValue();
+ assertEquals("-INF", p.getText());
+ assertTrue(Double.isInfinite(d));
+ assertTrue(d == Double.NEGATIVE_INFINITY);
+
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ d = p.getDoubleValue();
+ assertEquals("+INF", p.getText());
+ assertTrue(Double.isInfinite(d));
+ assertTrue(d == Double.POSITIVE_INFINITY);
+
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ d = p.getDoubleValue();
+ assertEquals("+Infinity", p.getText());
+ assertTrue(Double.isInfinite(d));
+ assertTrue(d == Double.POSITIVE_INFINITY);
+
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ d = p.getDoubleValue();
+ assertEquals("Infinity", p.getText());
+ assertTrue(Double.isInfinite(d));
+ assertTrue(d == Double.POSITIVE_INFINITY);
+
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ d = p.getDoubleValue();
+ assertEquals("-Infinity", p.getText());
+ assertTrue(Double.isInfinite(d));
+ assertTrue(d == Double.NEGATIVE_INFINITY);
+
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+
+ // finally, should also work with skipping
+ f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true);
+ p = createParser(f, mode, JSON);
+
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+
+ 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
new file mode 100644
index 0000000..d91f5e9
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/read/NumberParsingTest.java
@@ -0,0 +1,578 @@
+package com.fasterxml.jackson.core.read;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+
+/**
+ * Set of basic unit tests for verifying that the basic parser
+ * functionality works as expected.
+ */
+@SuppressWarnings("resource")
+public class NumberParsingTest
+ extends com.fasterxml.jackson.core.BaseTest
+{
+ private final JsonFactory FACTORY = new JsonFactory();
+
+ public void testSimpleBoolean() throws Exception
+ {
+ _testSimpleBoolean(MODE_INPUT_STREAM);
+ _testSimpleBoolean(MODE_INPUT_STREAM_THROTTLED);
+ _testSimpleBoolean(MODE_READER);
+ _testSimpleBoolean(MODE_DATA_INPUT);
+ }
+
+ private void _testSimpleBoolean(int mode) throws Exception
+ {
+ JsonParser p = createParser(mode, "[ true ]");
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+ assertEquals(true, p.getBooleanValue());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+ }
+
+ public void testSimpleInt() throws Exception
+ {
+ for (int EXP_I : new int[] { 1234, -999, 0, 1, -2 }) {
+ _testSimpleInt(EXP_I, MODE_INPUT_STREAM);
+ _testSimpleInt(EXP_I, MODE_INPUT_STREAM_THROTTLED);
+ _testSimpleInt(EXP_I, MODE_READER);
+ _testSimpleInt(EXP_I, MODE_DATA_INPUT);
+ }
+ }
+
+ private void _testSimpleInt(int EXP_I, int mode) throws Exception
+ {
+ String DOC = "[ "+EXP_I+" ]";
+ JsonParser p = createParser(mode, DOC);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(JsonParser.NumberType.INT, p.getNumberType());
+ assertEquals(""+EXP_I, p.getText());
+
+ assertEquals(EXP_I, p.getIntValue());
+ assertEquals(EXP_I, p.getValueAsInt(EXP_I + 3));
+ assertEquals(EXP_I, p.getValueAsInt());
+ assertEquals((long) EXP_I, p.getLongValue());
+ assertEquals((double) EXP_I, p.getDoubleValue());
+ assertEquals(BigDecimal.valueOf((long) EXP_I), p.getDecimalValue());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+
+ DOC = String.valueOf(EXP_I);
+ p = createParser(mode, DOC + " "); // DataInput requires separator
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(DOC, p.getText());
+
+ int i = 0;
+
+ try {
+ i = p.getIntValue();
+ } catch (Exception e) {
+ throw new Exception("Failed to parse input '"+DOC+"' (parser of type "+p.getClass().getSimpleName()+")", e);
+ }
+ assertEquals(EXP_I, i);
+ assertEquals((long) EXP_I, p.getLongValue());
+ assertEquals((double) EXP_I, p.getDoubleValue());
+ assertEquals(BigDecimal.valueOf((long) EXP_I), p.getDecimalValue());
+ p.close();
+ }
+
+ public void testIntRange() throws Exception
+ {
+ // let's test with readers and streams, separate code paths:
+ for (int mode : ALL_MODES) {
+ String DOC = "[ "+Integer.MAX_VALUE+","+Integer.MIN_VALUE+" ]";
+ JsonParser p = createParser(mode, DOC);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(JsonParser.NumberType.INT, p.getNumberType());
+ assertEquals(Integer.MAX_VALUE, p.getIntValue());
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(JsonParser.NumberType.INT, p.getNumberType());
+ assertEquals(Integer.MIN_VALUE, p.getIntValue());
+ p.close();
+ }
+ }
+
+ public void testSimpleLong() throws Exception
+ {
+ _testSimpleLong(MODE_INPUT_STREAM);
+ _testSimpleLong(MODE_INPUT_STREAM_THROTTLED);
+ _testSimpleLong(MODE_READER);
+ _testSimpleLong(MODE_DATA_INPUT);
+ }
+
+ private void _testSimpleLong(int mode) throws Exception
+ {
+ long EXP_L = 12345678907L;
+
+ JsonParser p = createParser(mode, "[ "+EXP_L+" ]");
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ // beyond int, should be long
+ assertEquals(JsonParser.NumberType.LONG, p.getNumberType());
+ assertEquals(""+EXP_L, p.getText());
+
+ assertEquals(EXP_L, p.getLongValue());
+ // Should get an exception if trying to convert to int
+ try {
+ p.getIntValue();
+ } catch (JsonParseException pe) {
+ verifyException(pe, "out of range");
+ }
+ assertEquals((double) EXP_L, p.getDoubleValue());
+ assertEquals(BigDecimal.valueOf((long) EXP_L), p.getDecimalValue());
+ p.close();
+ }
+
+ public void testLongRange() throws Exception
+ {
+ for (int mode : ALL_MODES) {
+ long belowMinInt = -1L + Integer.MIN_VALUE;
+ long aboveMaxInt = 1L + Integer.MAX_VALUE;
+ String input = "[ "+Long.MAX_VALUE+","+Long.MIN_VALUE+","+aboveMaxInt+", "+belowMinInt+" ]";
+ JsonParser p = createParser(mode, input);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(JsonParser.NumberType.LONG, p.getNumberType());
+ assertEquals(Long.MAX_VALUE, p.getLongValue());
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(JsonParser.NumberType.LONG, p.getNumberType());
+ assertEquals(Long.MIN_VALUE, p.getLongValue());
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(JsonParser.NumberType.LONG, p.getNumberType());
+ assertEquals(aboveMaxInt, p.getLongValue());
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(JsonParser.NumberType.LONG, p.getNumberType());
+ assertEquals(belowMinInt, p.getLongValue());
+
+
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+ }
+ }
+
+ public void testBigDecimalRange() throws Exception
+ {
+ for (int mode : ALL_MODES) {
+ // let's test first values outside of Long range
+ BigInteger small = new BigDecimal(Long.MIN_VALUE).toBigInteger();
+ small = small.subtract(BigInteger.ONE);
+ BigInteger big = new BigDecimal(Long.MAX_VALUE).toBigInteger();
+ big = big.add(BigInteger.ONE);
+ String input = "[ "+small+" , "+big+"]";
+ JsonParser p = createParser(mode, input);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(JsonParser.NumberType.BIG_INTEGER, p.getNumberType());
+ assertEquals(small, p.getBigIntegerValue());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(JsonParser.NumberType.BIG_INTEGER, p.getNumberType());
+ assertEquals(big, p.getBigIntegerValue());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+ }
+ }
+
+ // for [core#78]
+ public void testBigNumbers() throws Exception
+ {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < 520; ++i) { // input buffer is 512 bytes by default
+ sb.append('1');
+ }
+ final String NUMBER_STR = sb.toString();
+ BigInteger biggie = new BigInteger(NUMBER_STR);
+
+ for (int mode : ALL_MODES) {
+ JsonParser p = createParser(mode, NUMBER_STR +" ");
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(JsonParser.NumberType.BIG_INTEGER, p.getNumberType());
+ assertEquals(NUMBER_STR, p.getText());
+ assertEquals(biggie, p.getBigIntegerValue());
+ p.close();
+ }
+ }
+
+ public void testSimpleDouble() throws Exception
+ {
+ final String[] INPUTS = new String[] {
+ "1234.00", "2.1101567E-16", "1.0e5", "0.0", "1.0", "-1.0",
+ "-0.5", "-12.9", "-999.0",
+ "2.5e+5", "9e4", "-12e-3", "0.25",
+ };
+ for (int mode : ALL_MODES) {
+ for (int i = 0; i < INPUTS.length; ++i) {
+
+ // First in array
+
+ String STR = INPUTS[i];
+ double EXP_D = Double.parseDouble(STR);
+ String DOC = "["+STR+"]";
+
+ JsonParser p = createParser(mode, DOC+" ");
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertEquals(STR, p.getText());
+ assertEquals(EXP_D, p.getDoubleValue());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ if (mode != MODE_DATA_INPUT) {
+ assertNull(p.nextToken());
+ }
+ p.close();
+
+ // then outside
+ p = createParser(mode, STR + " ");
+ JsonToken t = null;
+
+ try {
+ t = p.nextToken();
+ } catch (Exception e) {
+ throw new Exception("Failed to parse input '"+STR+"' (parser of type "+p.getClass().getSimpleName()+")", e);
+ }
+
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, t);
+ assertEquals(STR, p.getText());
+ if (mode != MODE_DATA_INPUT) {
+ assertNull(p.nextToken());
+ }
+ p.close();
+ }
+ }
+ }
+
+ public void testNumbers() throws Exception
+ {
+ _testNumbers(MODE_INPUT_STREAM);
+ _testNumbers(MODE_INPUT_STREAM_THROTTLED);
+ _testNumbers(MODE_READER);
+ _testNumbers(MODE_DATA_INPUT);
+ }
+
+ private void _testNumbers(int mode) throws Exception
+ {
+ final String DOC = "[ -13, 8100200300, 13.5, 0.00010, -2.033 ]";
+
+ JsonParser p = createParser(mode, DOC);
+
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(-13, p.getIntValue());
+ assertEquals(-13L, p.getLongValue());
+ assertEquals(-13., p.getDoubleValue());
+ assertEquals("-13", p.getText());
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(8100200300L, p.getLongValue());
+ // Should get exception for overflow:
+ try {
+ /*int x =*/ p.getIntValue();
+ fail("Expected an exception for overflow");
+ } catch (Exception e) {
+ verifyException(e, "out of range of int");
+ }
+ assertEquals(8100200300.0, p.getDoubleValue());
+ assertEquals("8100200300", p.getText());
+
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertEquals(13, p.getIntValue());
+ assertEquals(13L, p.getLongValue());
+ assertEquals(13.5, p.getDoubleValue());
+ assertEquals("13.5", p.getText());
+
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertEquals(0, p.getIntValue());
+ assertEquals(0L, p.getLongValue());
+ assertEquals(0.00010, p.getDoubleValue());
+ assertEquals("0.00010", p.getText());
+
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertEquals(-2, p.getIntValue());
+ assertEquals(-2L, p.getLongValue());
+ assertEquals(-2.033, p.getDoubleValue());
+ assertEquals("-2.033", p.getText());
+
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+
+ p.close();
+ }
+
+ public void testLongOverflow() throws Exception
+ {
+ BigInteger below = BigInteger.valueOf(Long.MIN_VALUE);
+ below = below.subtract(BigInteger.ONE);
+ BigInteger above = BigInteger.valueOf(Long.MAX_VALUE);
+ above = above.add(BigInteger.ONE);
+
+ String DOC_BELOW = below.toString() + " ";
+ String DOC_ABOVE = below.toString() + " ";
+
+ for (int mode : ALL_MODES) {
+ JsonParser p = createParser(mode, DOC_BELOW);
+ p.nextToken();
+ try {
+ long x = p.getLongValue();
+ fail("Expected an exception for underflow (input "+p.getText()+"): instead, got long value: "+x);
+ } catch (JsonParseException e) {
+ verifyException(e, "out of range of long");
+ }
+ p.close();
+
+ p = createParser(mode, DOC_ABOVE);
+ p.nextToken();
+ try {
+ long x = p.getLongValue();
+ fail("Expected an exception for underflow (input "+p.getText()+"): instead, got long value: "+x);
+ } catch (JsonParseException e) {
+ verifyException(e, "out of range of long");
+ }
+ p.close();
+
+ }
+ }
+
+ /**
+ * Method that tries to test that number parsing works in cases where
+ * input is split between buffer boundaries.
+ */
+ public void testParsingOfLongerSequences() throws Exception
+ {
+ double[] values = new double[] { 0.01, -10.5, 2.1e9, 4.0e-8 };
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = 0; i < values.length; ++i) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ sb.append(values[i]);
+ }
+ String segment = sb.toString();
+
+ int COUNT = 1000;
+ sb = new StringBuilder(COUNT * segment.length() + 20);
+ sb.append("[");
+ for (int i = 0; i < COUNT; ++i) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ sb.append(segment);
+ sb.append('\n');
+ // let's add somewhat arbitrary number of spaces
+ int x = (i & 3);
+ if (i > 300) {
+ x += i % 5;
+ }
+ while (--x > 0) {
+ sb.append(' ');
+ }
+ }
+ sb.append("]");
+ String DOC = sb.toString();
+
+ for (int input = 0; input < 2; ++input) {
+ JsonParser p;
+
+ if (input == 0) {
+ p = createParserUsingStream(DOC, "UTF-8");
+ } else {
+ p = FACTORY.createParser(DOC);
+ }
+
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ for (int i = 0; i < COUNT; ++i) {
+ for (double d : values) {
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertEquals(d, p.getDoubleValue());
+ }
+ }
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+ }
+ }
+
+ // [jackson-core#157]
+ public void testLongNumbers() throws Exception
+ {
+ StringBuilder sb = new StringBuilder(9000);
+ for (int i = 0; i < 9000; ++i) {
+ sb.append('9');
+ }
+ String NUM = sb.toString();
+ // force use of new factory, just in case (might still recycle same buffers tho?)
+ JsonFactory f = new JsonFactory();
+ _testLongNumbers(f, NUM, false);
+ _testLongNumbers(f, NUM, true);
+ }
+
+ private void _testLongNumbers(JsonFactory f, String num, boolean useStream) throws Exception
+ {
+ final String doc = "[ "+num+" ]";
+ JsonParser p = useStream
+ ? f.createParser(doc.getBytes("UTF-8"))
+ : f.createParser(doc);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(num, p.getText());
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ }
+
+ // and alternate take on for #157 (with negative num)
+ public void testLongNumbers2() throws Exception
+ {
+ StringBuilder input = new StringBuilder();
+ // test this with negative
+ input.append('-');
+ for (int i = 0; i < 2100; i++) {
+ input.append(1);
+ }
+ final String DOC = input.toString();
+ JsonFactory f = new JsonFactory();
+ _testIssue160LongNumbers(f, DOC, false);
+ _testIssue160LongNumbers(f, DOC, true);
+ }
+
+ private void _testIssue160LongNumbers(JsonFactory f, String doc, boolean useStream) throws Exception
+ {
+ JsonParser p = useStream
+ ? FACTORY.createParser(doc.getBytes("UTF-8"))
+ : FACTORY.createParser(doc);
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ BigInteger v = p.getBigIntegerValue();
+ assertNull(p.nextToken());
+ assertEquals(doc, v.toString());
+ }
+
+ // for [jackson-core#181]
+ /**
+ * Method that tries to test that number parsing works in cases where
+ * input is split between buffer boundaries.
+ */
+ public void testParsingOfLongerSequencesWithNonNumeric() throws Exception
+ {
+ JsonFactory factory = new JsonFactory();
+ factory.enable(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS);
+ double[] values = new double[] {
+ 0.01, -10.5, 2.1e9, 4.0e-8,
+ Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY
+ };
+ for (int i = 0; i < values.length; ++i) {
+ int COUNT = 4096;
+ // Don't see the failure with a multiple of 1
+ int VCOUNT = 2 * COUNT;
+ String arrayJson = toJsonArray(values[i], VCOUNT);
+ StringBuilder sb = new StringBuilder(COUNT + arrayJson.length() + 20);
+ for (int j = 0; j < COUNT; ++j) {
+ sb.append(' ');
+ }
+ sb.append(arrayJson);
+ String DOC = sb.toString();
+ for (int input = 0; input < 2; ++input) {
+ JsonParser p;
+ if (input == 0) {
+ p = createParserUsingStream(factory, DOC, "UTF-8");
+ } else {
+ p = factory.createParser(DOC);
+ }
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ for (int j = 0; j < VCOUNT; ++j) {
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertEquals(values[i], p.getDoubleValue());
+ }
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+ }
+ }
+ }
+
+ /*
+ /**********************************************************
+ /* Tests for invalid access
+ /**********************************************************
+ */
+
+ public void testInvalidBooleanAccess() throws Exception {
+ _testInvalidBooleanAccess(MODE_INPUT_STREAM);
+ _testInvalidBooleanAccess(MODE_INPUT_STREAM_THROTTLED);
+ _testInvalidBooleanAccess(MODE_READER);
+ _testInvalidBooleanAccess(MODE_DATA_INPUT);
+ }
+
+ private void _testInvalidBooleanAccess(int mode) throws Exception
+ {
+ JsonParser p = createParser(mode, "[ \"abc\" ]");
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ try {
+ p.getBooleanValue();
+ fail("Expected error trying to call getBooleanValue on non-boolean value");
+ } catch (JsonParseException e) {
+ verifyException(e, "not of boolean type");
+ }
+ p.close();
+ }
+
+ public void testInvalidIntAccess() throws Exception {
+ _testInvalidIntAccess(MODE_INPUT_STREAM);
+ _testInvalidIntAccess(MODE_INPUT_STREAM_THROTTLED);
+ _testInvalidIntAccess(MODE_READER);
+ _testInvalidIntAccess(MODE_DATA_INPUT);
+ }
+
+ private void _testInvalidIntAccess(int mode) throws Exception
+ {
+ JsonParser p = createParser(mode, "[ \"abc\" ]");
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ try {
+ p.getIntValue();
+ fail("Expected error trying to call getIntValue on non-numeric value");
+ } catch (JsonParseException e) {
+ verifyException(e, "can not use numeric value accessors");
+ }
+ p.close();
+ }
+
+ public void testInvalidLongAccess() throws Exception {
+ _testInvalidLongAccess(MODE_INPUT_STREAM);
+ _testInvalidLongAccess(MODE_INPUT_STREAM_THROTTLED);
+ _testInvalidLongAccess(MODE_READER);
+ _testInvalidLongAccess(MODE_DATA_INPUT);
+ }
+
+ private void _testInvalidLongAccess(int mode) throws Exception
+ {
+ JsonParser p = createParser(mode, "[ false ]");
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+ try {
+ p.getLongValue();
+ fail("Expected error trying to call getLongValue on non-numeric value");
+ } catch (JsonParseException e) {
+ verifyException(e, "can not use numeric value accessors");
+ }
+ p.close();
+ }
+
+ /*
+ /**********************************************************
+ /* Helper methods
+ /**********************************************************
+ */
+
+ private String toJsonArray(double v, int n) {
+ StringBuilder sb = new StringBuilder().append('[').append(v);
+ for (int i = 1; i < n; ++i) {
+ sb.append(',').append(v);
+ }
+ return sb.append(']').toString();
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/read/ParserDupHandlingTest.java b/src/test/java/com/fasterxml/jackson/core/read/ParserDupHandlingTest.java
new file mode 100644
index 0000000..79dd355
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/read/ParserDupHandlingTest.java
@@ -0,0 +1,141 @@
+package com.fasterxml.jackson.core.read;
+
+import com.fasterxml.jackson.core.*;
+
+public class ParserDupHandlingTest
+ extends com.fasterxml.jackson.core.BaseTest
+{
+ private final String[] DUP_DOCS = new String[] {
+ "{ 'a':1, 'a':2 }",
+ "[{ 'a':1, 'a':2 }]",
+ "{ 'a':1, 'b':2, 'c':3,'a':true,'e':false }",
+ "{ 'foo': { 'bar': [ [ { 'x':3, 'a':1 } ]], 'x':0, 'a':'y', 'b':3,'a':13 } }",
+ "[{'b':1},{'b\":3},[{'a':3}], {'a':1,'a':2}]",
+ "{'b':1,'array':[{'b':3}],'ob':{'b':4,'x':0,'y':3,'a':true,'a':false }}",
+ };
+ {
+ for (int i = 0; i < DUP_DOCS.length; ++i) {
+ DUP_DOCS[i] = aposToQuotes(DUP_DOCS[i]);
+ }
+ }
+
+ public void testSimpleDupCheckDisabled() throws Exception
+ {
+ // first: verify no problems if detection NOT enabled
+ final JsonFactory f = new JsonFactory();
+ assertFalse(f.isEnabled(JsonParser.Feature.STRICT_DUPLICATE_DETECTION));
+ for (String doc : DUP_DOCS) {
+ _testSimpleDupsOk(doc, f, MODE_INPUT_STREAM);
+ _testSimpleDupsOk(doc, f, MODE_INPUT_STREAM_THROTTLED);
+ _testSimpleDupsOk(doc, f, MODE_READER);
+ _testSimpleDupsOk(doc, f, MODE_DATA_INPUT);
+ }
+ }
+
+ public void testSimpleDupsBytes() throws Exception
+ {
+ JsonFactory nonDupF = new JsonFactory();
+ JsonFactory dupF = new JsonFactory();
+ dupF.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION);
+ for (String doc : DUP_DOCS) {
+ // First, with static setting
+ _testSimpleDupsFail(doc, dupF, MODE_INPUT_STREAM, "a", false);
+ // and then dynamic
+ _testSimpleDupsFail(doc, nonDupF, MODE_INPUT_STREAM, "a", true);
+
+ _testSimpleDupsFail(doc, dupF, MODE_INPUT_STREAM_THROTTLED, "a", false);
+ _testSimpleDupsFail(doc, nonDupF, MODE_INPUT_STREAM_THROTTLED, "a", true);
+ }
+ }
+
+ public void testSimpleDupsDataInput() throws Exception
+ {
+ JsonFactory nonDupF = new JsonFactory();
+ JsonFactory dupF = new JsonFactory();
+ dupF.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION);
+ for (String doc : DUP_DOCS) {
+ _testSimpleDupsFail(doc, dupF, MODE_DATA_INPUT, "a", false);
+ _testSimpleDupsFail(doc, nonDupF, MODE_DATA_INPUT, "a", true);
+ }
+ }
+
+ public void testSimpleDupsChars() throws Exception
+ {
+ JsonFactory nonDupF = new JsonFactory();
+ JsonFactory dupF = new JsonFactory();
+ dupF.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION);
+ for (String doc : DUP_DOCS) {
+ _testSimpleDupsFail(doc, dupF, MODE_READER, "a", false);
+ _testSimpleDupsFail(doc, nonDupF, MODE_READER, "a", true);
+ }
+ }
+
+ private void _testSimpleDupsOk(final String doc, JsonFactory f,
+ int mode) throws Exception
+ {
+ JsonParser p = createParser(f, mode, doc);
+ JsonToken t = p.nextToken();
+ assertNotNull(t);
+ assertTrue(t.isStructStart());
+
+ int depth = 1;
+
+ while (depth > 0) {
+ switch (p.nextToken()) {
+ case START_ARRAY:
+ case START_OBJECT:
+ ++depth;
+ break;
+ case END_ARRAY:
+ case END_OBJECT:
+ --depth;
+ break;
+ default:
+ }
+ }
+ if (mode != MODE_DATA_INPUT) {
+ assertNull(p.nextToken());
+ }
+ p.close();
+ }
+
+ private void _testSimpleDupsFail(final String doc, JsonFactory f,
+ int mode, String name, boolean lazily) throws Exception
+ {
+ JsonParser p = createParser(f, mode, doc);
+ if (lazily) {
+ p.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION);
+ }
+ JsonToken t = p.nextToken();
+ assertNotNull(t);
+ assertTrue(t.isStructStart());
+
+ int depth = 1;
+ JsonParseException e = null;
+
+ while (depth > 0) {
+ try {
+ switch (p.nextToken()) {
+ case START_ARRAY:
+ case START_OBJECT:
+ ++depth;
+ break;
+ case END_ARRAY:
+ case END_OBJECT:
+ --depth;
+ break;
+ default:
+ }
+ } catch (JsonParseException e0) {
+ e = e0;
+ break;
+ }
+ }
+ p.close();
+
+ if (e == null) {
+ fail("Should have caught exception for dup");
+ }
+ verifyException(e, "duplicate field '"+name+"'");
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/read/ParserErrorHandlingTest.java b/src/test/java/com/fasterxml/jackson/core/read/ParserErrorHandlingTest.java
new file mode 100644
index 0000000..bb84134
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/read/ParserErrorHandlingTest.java
@@ -0,0 +1,111 @@
+package com.fasterxml.jackson.core.read;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+
+public class ParserErrorHandlingTest
+ extends com.fasterxml.jackson.core.BaseTest
+{
+ public void testInvalidKeywordsBytes() throws Exception {
+ _testInvalidKeywords(MODE_INPUT_STREAM);
+ _testInvalidKeywords(MODE_INPUT_STREAM_THROTTLED);
+ _testInvalidKeywords(MODE_DATA_INPUT);
+ }
+
+ public void testInvalidKeywordsChars() throws Exception {
+ _testInvalidKeywords(MODE_READER);
+ }
+
+ // Tests for [core#105] ("eager number parsing misses errors")
+ public void testMangledNumbersBytes() throws Exception {
+ _testMangledNumbers(MODE_INPUT_STREAM);
+ _testMangledNumbers(MODE_INPUT_STREAM_THROTTLED);
+ _testInvalidKeywords(MODE_DATA_INPUT);
+ }
+
+ public void testMangledNumbersChars() throws Exception {
+ _testMangledNumbers(MODE_READER);
+ }
+
+ /*
+ /**********************************************************
+ /* Helper methods
+ /**********************************************************
+ */
+
+ private void _testInvalidKeywords(int mode) throws Exception
+ {
+ doTestInvalidKeyword1(mode, "nul");
+ doTestInvalidKeyword1(mode, "Null");
+ doTestInvalidKeyword1(mode, "nulla");
+ doTestInvalidKeyword1(mode, "fal");
+ doTestInvalidKeyword1(mode, "False");
+ doTestInvalidKeyword1(mode, "fals0");
+ doTestInvalidKeyword1(mode, "falsett0");
+ doTestInvalidKeyword1(mode, "tr");
+ doTestInvalidKeyword1(mode, "truE");
+ doTestInvalidKeyword1(mode, "treu");
+ doTestInvalidKeyword1(mode, "trueenough");
+ doTestInvalidKeyword1(mode, "C");
+ }
+
+ private void doTestInvalidKeyword1(int mode, String value)
+ throws IOException
+ {
+ String doc = "{ \"key1\" : "+value+" }";
+ JsonParser p = createParser(mode, doc);
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ // Note that depending on parser impl, we may
+ // get the exception early or late...
+ try {
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ p.nextToken();
+ fail("Expected an exception for malformed value keyword");
+ } catch (JsonParseException jex) {
+ verifyException(jex, "Unrecognized token");
+ verifyException(jex, value);
+ } finally {
+ p.close();
+ }
+
+ // Try as root-level value as well:
+ doc = value + " "; // may need space after for DataInput
+ p = createParser(mode, doc);
+ try {
+ p.nextToken();
+ fail("Expected an exception for malformed value keyword");
+ } catch (JsonParseException jex) {
+ verifyException(jex, "Unrecognized token");
+ verifyException(jex, value);
+ } finally {
+ p.close();
+ }
+ }
+
+ private void _testMangledNumbers(int mode) throws Exception
+ {
+ String doc = "123true";
+ JsonParser p = createParser(mode, doc);
+ try {
+ JsonToken t = p.nextToken();
+ fail("Should have gotten an exception; instead got token: "+t);
+ } catch (JsonParseException e) {
+ verifyException(e, "expected space");
+ }
+ p.close();
+
+ // Also test with floats
+ doc = "1.5false";
+ p = createParser(mode, doc);
+ try {
+ JsonToken t = p.nextToken();
+ fail("Should have gotten an exception; instead got token: "+t);
+ } catch (JsonParseException e) {
+ verifyException(e, "expected space");
+ }
+ p.close();
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/read/ParserScopeMatchingTest.java b/src/test/java/com/fasterxml/jackson/core/read/ParserScopeMatchingTest.java
new file mode 100644
index 0000000..55c261c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/read/ParserScopeMatchingTest.java
@@ -0,0 +1,188 @@
+package com.fasterxml.jackson.core.read;
+
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+/**
+ * Set of basic unit tests for verifying that Array/Object scopes
+ * are properly matched.
+ */
+@SuppressWarnings("resource")
+public class ParserScopeMatchingTest extends BaseTest
+{
+ public void testUnclosedArray() throws Exception
+ {
+ for (int mode : ALL_MODES) {
+ _testUnclosedArray(mode);
+ }
+ }
+
+ public void _testUnclosedArray(int mode) throws Exception
+ {
+ JsonParser p = createParser(mode, "[ 1, 2 ");
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+
+ try {
+ p.nextToken();
+ } 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
+ {
+ for (int mode : ALL_MODES) {
+ _testUnclosedObject(mode);
+ }
+ }
+
+ private void _testUnclosedObject(int mode) throws Exception
+ {
+ JsonParser p = createParser(mode, "{ \"key\" : 3 ");
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+
+ try {
+ p.nextToken();
+ fail("Expected an exception for unclosed OBJECT");
+ } 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;
+ }
+ }
+ }
+
+ public void testEOFInName() throws Exception
+ {
+ for (int mode : ALL_MODES) {
+ _testEOFInName(mode);
+ }
+ }
+
+ public void _testEOFInName(int mode) throws Exception
+ {
+ final String JSON = "{ \"abcd";
+ JsonParser p = createParser(mode, JSON);
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ try {
+ p.nextToken();
+ fail("Expected an exception for EOF");
+ } catch (JsonParseException pe) {
+ verifyException(pe, "Unexpected end-of-input");
+ } catch (IOException ie) {
+ // DataInput behaves bit differently
+ if (mode == MODE_DATA_INPUT) {
+ verifyException(ie, "end-of-input");
+ return;
+ }
+ }
+ }
+
+ public void testWeirdToken() throws Exception
+ {
+ for (int mode : ALL_MODES) {
+ _testWeirdToken(mode);
+ }
+ }
+
+ private void _testWeirdToken(int mode) throws Exception
+ {
+ final String JSON = "[ nil ]";
+ JsonParser p = createParser(mode, JSON);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ try {
+ p.nextToken();
+ fail("Expected an exception for weird token");
+ } catch (JsonParseException pe) {
+ verifyException(pe, "Unrecognized token");
+ }
+ p.close();
+ }
+
+ public void testMismatchArrayToObject() throws Exception
+ {
+ for (int mode : ALL_MODES) {
+ _testMismatchArrayToObject(mode);
+ }
+ }
+
+ private void _testMismatchArrayToObject(int mode) throws Exception
+ {
+ final String JSON = "[ 1, 2 }";
+ JsonParser p = createParser(mode, JSON);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ try {
+ p.nextToken();
+ fail("Expected an exception for incorrectly closed ARRAY");
+ } catch (JsonParseException pe) {
+ verifyException(pe, "Unexpected close marker '}': expected ']'");
+ }
+ p.close();
+ }
+
+ public void testMismatchObjectToArray() throws Exception
+ {
+ for (int mode : ALL_MODES) {
+ _testMismatchObjectToArray(mode);
+ }
+ }
+
+ private void _testMismatchObjectToArray(int mode) throws Exception
+ {
+ final String JSON = "{ ]";
+ JsonParser p = createParser(mode, JSON);
+
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+
+ try {
+ p.nextToken();
+ fail("Expected an exception for incorrectly closed OBJECT");
+ } catch (JsonParseException pe) {
+ verifyException(pe, "Unexpected close marker ']': expected '}'");
+ }
+ p.close();
+ }
+
+ public void testMisssingColon() throws Exception
+ {
+ for (int mode : ALL_MODES) {
+ _testMisssingColon(mode);
+ }
+ }
+
+ private void _testMisssingColon(int mode) throws Exception
+ {
+ final String JSON = "{ \"a\" \"b\" }";
+ JsonParser p = createParser(mode, JSON);
+
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ try {
+ // can be either here, or with next one...
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ p.nextToken();
+ fail("Expected an exception for missing semicolon");
+ } catch (JsonParseException pe) {
+ verifyException(pe, "was expecting a colon");
+ }
+ p.close();
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/TestParserSymbols.java b/src/test/java/com/fasterxml/jackson/core/read/ParserSymbolHandlingTest.java
similarity index 93%
rename from src/test/java/com/fasterxml/jackson/core/json/TestParserSymbols.java
rename to src/test/java/com/fasterxml/jackson/core/read/ParserSymbolHandlingTest.java
index 8d87beb..e2cab9e 100644
--- a/src/test/java/com/fasterxml/jackson/core/json/TestParserSymbols.java
+++ b/src/test/java/com/fasterxml/jackson/core/read/ParserSymbolHandlingTest.java
@@ -1,11 +1,11 @@
-package com.fasterxml.jackson.core.json;
+package com.fasterxml.jackson.core.read;
import com.fasterxml.jackson.core.*;
-public class TestParserSymbols
+public class ParserSymbolHandlingTest
extends com.fasterxml.jackson.core.BaseTest
{
- // For [Issue#148]
+ // For [core#148]
public void testSymbolsWithNullBytes() throws Exception {
JsonFactory f = new JsonFactory();
_testSymbolsWithNull(f, true);
@@ -13,7 +13,7 @@
_testSymbolsWithNull(f, true);
}
- // For [Issue#148]
+ // For [core#148]
public void testSymbolsWithNullChars() throws Exception {
JsonFactory f = new JsonFactory();
_testSymbolsWithNull(f, false);
diff --git a/src/test/java/com/fasterxml/jackson/core/read/TestJsonParserBinary.java b/src/test/java/com/fasterxml/jackson/core/read/TestJsonParserBinary.java
new file mode 100644
index 0000000..3c5801c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/read/TestJsonParserBinary.java
@@ -0,0 +1,139 @@
+package com.fasterxml.jackson.core.read;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.*;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests for verifying that accessing base64 encoded content works ok.
+ */
+public class TestJsonParserBinary
+ extends com.fasterxml.jackson.core.BaseTest
+{
+ /*
+ /**********************************************************************
+ /* Unit tests
+ /**********************************************************************
+ */
+
+ public void testSimple() throws IOException
+ {
+ for (int mode : ALL_MODES) {
+ _testSimple(mode);
+ }
+ }
+
+ public void testInArray() throws IOException
+ {
+ for (int mode : ALL_MODES) {
+ _testInArray(mode);
+ }
+ }
+
+ public void testWithEscaped() throws IOException {
+ for (int mode : ALL_MODES) {
+ _testEscaped(mode);
+ }
+ }
+
+ /*
+ /**********************************************************************
+ /* Actual test methods
+ /**********************************************************************
+ */
+
+ private void _testSimple(int mode)
+ throws IOException
+ {
+ /* The usual sample input string, from Thomas Hobbes's "Leviathan"
+ * (via Wikipedia)
+ */
+ final String RESULT = "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.";
+ final byte[] RESULT_BYTES = RESULT.getBytes("US-ASCII");
+
+ // And here's what should produce it...
+ final String INPUT_STR =
+ "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
++"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
++"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu"
++"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
++"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="
+ ;
+
+ final String DOC = "\""+INPUT_STR+"\"";
+ JsonParser p = createParser(mode, DOC);
+
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ byte[] data = p.getBinaryValue();
+ assertNotNull(data);
+ assertArrayEquals(RESULT_BYTES, data);
+ p.close();
+ }
+
+ private void _testInArray(int mode) throws IOException
+ {
+ JsonFactory f = new JsonFactory();
+
+ final int entryCount = 7;
+
+ StringWriter sw = new StringWriter();
+ JsonGenerator jg = f.createGenerator(sw);
+ jg.writeStartArray();
+
+ byte[][] entries = new byte[entryCount][];
+ for (int i = 0; i < entryCount; ++i) {
+ byte[] b = new byte[200 + i * 100];
+ for (int x = 0; x < b.length; ++x) {
+ b[x] = (byte) (i + x);
+ }
+ entries[i] = b;
+ jg.writeBinary(b);
+ }
+
+ jg.writeEndArray();
+ jg.close();
+
+ JsonParser p = createParser(f, mode, sw.toString());
+
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+
+ for (int i = 0; i < entryCount; ++i) {
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ byte[] b = p.getBinaryValue();
+ assertArrayEquals(entries[i], b);
+ }
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+ }
+
+ private void _testEscaped(int mode) throws IOException
+ {
+ // Input: "Test!" -> "VGVzdCE="
+
+ // First, try with embedded linefeed half-way through:
+
+ String DOC = quote("VGVz\\ndCE="); // note: must double-quote to get linefeed
+ JsonParser p = createParser(mode, DOC);
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ byte[] b = p.getBinaryValue();
+ assertEquals("Test!", new String(b, "US-ASCII"));
+ if (mode != MODE_DATA_INPUT) {
+ assertNull(p.nextToken());
+ }
+ p.close();
+
+ // and then with escaped chars
+// DOC = quote("V\\u0047V\\u007AdCE="); // note: must escape backslash...
+ DOC = quote("V\\u0047V\\u007AdCE="); // note: must escape backslash...
+ p = createParser(mode, DOC);
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ b = p.getBinaryValue();
+ assertEquals("Test!", new String(b, "US-ASCII"));
+ if (mode != MODE_DATA_INPUT) {
+ assertNull(p.nextToken());
+ }
+ p.close();
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/read/UTF8NamesParseTest.java b/src/test/java/com/fasterxml/jackson/core/read/UTF8NamesParseTest.java
new file mode 100644
index 0000000..029bcc3
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/read/UTF8NamesParseTest.java
@@ -0,0 +1,237 @@
+package com.fasterxml.jackson.core.read;
+
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.SerializedString;
+
+import java.io.*;
+import java.util.Random;
+
+/**
+ * Set of basic unit tests for verifying that the basic parser
+ * functionality works as expected.
+ */
+public class UTF8NamesParseTest
+ extends BaseTest
+{
+ final static String[] UTF8_2BYTE_STRINGS = new String[] {
+ /* This may look funny, but UTF8 scanner has fairly
+ * elaborate decoding machinery, and it is indeed
+ * necessary to try out various combinations...
+ */
+ "b", "A\u00D8", "abc", "c3p0",
+ "12345", "......", "Long\u00FAer",
+ "Latin1-fully-\u00BE-develop\u00A8d",
+ "Some very long name, ridiculously long actually to see that buffer expansion works: \u00BF?"
+ };
+
+ final static String[] UTF8_3BYTE_STRINGS = new String[] {
+ "\uC823?", "A\u400F", "1\u1234?",
+ "Ab123\u4034",
+ "Even-longer:\uC023"
+ };
+
+ public void testEmptyName() throws Exception
+ {
+ _testEmptyName(MODE_INPUT_STREAM);
+ _testEmptyName(MODE_INPUT_STREAM_THROTTLED);
+ _testEmptyName(MODE_DATA_INPUT);
+ }
+
+ private void _testEmptyName(int mode) throws Exception
+ {
+ final String DOC = "{ \"\" : \"\" }";
+ JsonParser p = createParser(mode, DOC);
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals("", p.getCurrentName());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals("", p.getText());
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ p.close();
+ }
+
+ public void testUtf8Name2Bytes() throws Exception
+ {
+ _testUtf8Name2Bytes(MODE_INPUT_STREAM);
+ _testUtf8Name2Bytes(MODE_INPUT_STREAM_THROTTLED);
+ _testUtf8Name2Bytes(MODE_DATA_INPUT);
+ }
+
+ private void _testUtf8Name2Bytes(int mode) throws Exception
+ {
+ final String[] NAMES = UTF8_2BYTE_STRINGS;
+
+ for (int i = 0; i < NAMES.length; ++i) {
+ String NAME = NAMES[i];
+ String DOC = "{ \""+NAME+"\" : 0 }";
+ JsonParser p = createParser(mode, DOC);
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+
+ assertTrue(p.hasToken(JsonToken.FIELD_NAME));
+ assertTrue(p.hasTokenId(JsonTokenId.ID_FIELD_NAME));
+
+ assertEquals(NAME, p.getCurrentName());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertTrue(p.hasToken(JsonToken.VALUE_NUMBER_INT));
+ assertTrue(p.hasTokenId(JsonTokenId.ID_NUMBER_INT));
+
+ // should retain name during value entry, too
+ assertEquals(NAME, p.getCurrentName());
+
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+ p.close();
+ }
+ }
+
+ public void testUtf8Name3Bytes() throws Exception
+ {
+ _testUtf8Name3Bytes(MODE_INPUT_STREAM);
+ _testUtf8Name3Bytes(MODE_DATA_INPUT);
+ _testUtf8Name3Bytes(MODE_INPUT_STREAM_THROTTLED);
+ }
+
+ public void _testUtf8Name3Bytes(int mode) throws Exception
+ {
+ final String[] NAMES = UTF8_3BYTE_STRINGS;
+
+ for (int i = 0; i < NAMES.length; ++i) {
+ String NAME = NAMES[i];
+ String DOC = "{ \""+NAME+"\" : true }";
+
+ JsonParser p = createParser(mode, DOC);
+ assertToken(JsonToken.START_OBJECT, p.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, p.nextToken());
+ assertEquals(NAME, p.getCurrentName());
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+ assertEquals(NAME, p.getCurrentName());
+
+ assertToken(JsonToken.END_OBJECT, p.nextToken());
+
+ p.close();
+ }
+ }
+
+ // How about tests for Surrogate-Pairs?
+
+ public void testUtf8StringTrivial() throws Exception
+ {
+ _testUtf8StringTrivial(MODE_INPUT_STREAM);
+ _testUtf8StringTrivial(MODE_DATA_INPUT);
+ _testUtf8StringTrivial(MODE_INPUT_STREAM_THROTTLED);
+ }
+
+ public void _testUtf8StringTrivial(int mode) throws Exception
+ {
+ String[] VALUES = UTF8_2BYTE_STRINGS;
+ for (int i = 0; i < VALUES.length; ++i) {
+ String VALUE = VALUES[i];
+ String DOC = "[ \""+VALUE+"\" ]";
+ JsonParser p = createParser(mode, DOC);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ String act = getAndVerifyText(p);
+ if (act.length() != VALUE.length()) {
+ fail("Failed for value #"+(i+1)+"/"+VALUES.length+": length was "+act.length()+", should be "+VALUE.length());
+ }
+ assertEquals(VALUE, act);
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+ }
+
+ VALUES = UTF8_3BYTE_STRINGS;
+ for (int i = 0; i < VALUES.length; ++i) {
+ String VALUE = VALUES[i];
+ String DOC = "[ \""+VALUE+"\" ]";
+ JsonParser p = createParser(mode, DOC);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals(VALUE, getAndVerifyText(p));
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ p.close();
+ }
+ }
+
+ public void testUtf8StringValue() throws Exception
+ {
+ _testUtf8StringValue(MODE_INPUT_STREAM);
+ _testUtf8StringValue(MODE_DATA_INPUT);
+ _testUtf8StringValue(MODE_INPUT_STREAM_THROTTLED);
+ }
+
+ public void _testUtf8StringValue(int mode) throws Exception
+ {
+ Random r = new Random(13);
+ //int LEN = 72000;
+ int LEN = 720;
+ StringBuilder sb = new StringBuilder(LEN + 20);
+ while (sb.length() < LEN) {
+ int c;
+ if (r.nextBoolean()) { // ascii
+ c = 32 + (r.nextInt() & 0x3F);
+ if (c == '"' || c == '\\') {
+ c = ' ';
+ }
+ } else if (r.nextBoolean()) { // 2-byte
+ c = 160 + (r.nextInt() & 0x3FF);
+ } else if (r.nextBoolean()) { // 3-byte (non-surrogate)
+ c = 8000 + (r.nextInt() & 0x7FFF);
+ } else { // surrogates (2 chars)
+ int value = r.nextInt() & 0x3FFFF; // 20-bit, ~ 1 million
+ sb.append((char) (0xD800 + (value >> 10)));
+ c = (0xDC00 + (value & 0x3FF));
+
+ }
+ sb.append((char) c);
+ }
+
+ ByteArrayOutputStream bout = new ByteArrayOutputStream(LEN);
+ OutputStreamWriter out = new OutputStreamWriter(bout, "UTF-8");
+ out.write("[\"");
+ String VALUE = sb.toString();
+ out.write(VALUE);
+ out.write("\"]");
+ out.close();
+
+ byte[] data = bout.toByteArray();
+
+ JsonParser p = createParser(mode, data);
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ String act = p.getText();
+
+ assertEquals(VALUE.length(), act.length());
+ assertEquals(VALUE, act);
+ p.close();
+ }
+
+ public void testNextFieldName() throws IOException
+ {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ os.write('{');
+ for (int i = 0; i < 3994; i++) {
+ os.write(' ');
+ }
+ os.write("\"id\":2".getBytes("UTF-8"));
+ os.write('}');
+ byte[] data = os.toByteArray();
+
+ _testNextFieldName(MODE_INPUT_STREAM, data);
+ _testNextFieldName(MODE_DATA_INPUT, data);
+ _testNextFieldName(MODE_INPUT_STREAM_THROTTLED, data);
+ }
+
+ private void _testNextFieldName(int mode, byte[] doc) throws IOException
+ {
+ SerializedString id = new SerializedString("id");
+ JsonParser parser = createParser(mode, doc);
+ assertEquals(parser.nextToken(), JsonToken.START_OBJECT);
+ assertTrue(parser.nextFieldName(id));
+ assertEquals(parser.nextToken(), JsonToken.VALUE_NUMBER_INT);
+ assertEquals(parser.nextToken(), JsonToken.END_OBJECT);
+ parser.close();
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/read/ValueConversionsTest.java b/src/test/java/com/fasterxml/jackson/core/read/ValueConversionsTest.java
new file mode 100644
index 0000000..1ec2d3e
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/read/ValueConversionsTest.java
@@ -0,0 +1,187 @@
+package com.fasterxml.jackson.core.read;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+
+public class ValueConversionsTest
+ extends com.fasterxml.jackson.core.BaseTest
+{
+ public void testAsInt() throws Exception
+ {
+ for (int mode : ALL_MODES) {
+ _testAsInt(mode);
+ }
+ }
+
+ private void _testAsInt(int mode) throws Exception
+ {
+ final String input = "[ 1, -3, 4.98, true, false, null, \"-17\", \"foo\" ]";
+ JsonParser p = createParser(mode, input);
+
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertEquals(0, p.getValueAsLong());
+ assertEquals(9, p.getValueAsLong(9));
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(1, p.getValueAsLong());
+ assertEquals(1, p.getValueAsLong(-99));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(-3, p.getValueAsLong());
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertEquals(4, p.getValueAsLong());
+ assertEquals(4, p.getValueAsLong(99));
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+ assertEquals(1, p.getValueAsLong());
+ assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+ assertEquals(0, p.getValueAsLong());
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertEquals(0, p.getValueAsLong());
+ assertEquals(0, p.getValueAsLong(27));
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals(-17, p.getValueAsLong());
+ assertEquals(-17, p.getValueAsLong(3));
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals(0, p.getValueAsLong());
+ assertEquals(9, p.getValueAsLong(9));
+
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertEquals(0, p.getValueAsLong());
+ assertEquals(9, p.getValueAsLong(9));
+
+ p.close();
+ }
+
+ public void testAsBoolean() throws Exception
+ {
+ for (int mode : ALL_MODES) {
+ _testAsBoolean(mode);
+ }
+ }
+
+ private void _testAsBoolean(int mode) throws Exception
+ {
+ final String input = "[ true, false, null, 1, 0, \"true\", \"false\", \"foo\" ]";
+ JsonParser p = createParser(mode, input);
+
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertEquals(false, p.getValueAsBoolean());
+ assertEquals(true, p.getValueAsBoolean(true));
+
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+ assertEquals(true, p.getValueAsBoolean());
+ assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+ assertEquals(false, p.getValueAsBoolean());
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertEquals(false, p.getValueAsBoolean());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(1, p.getIntValue());
+ assertEquals(true, p.getValueAsBoolean());
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(0, p.getIntValue());
+ assertEquals(false, p.getValueAsBoolean());
+
+ assertToken(JsonToken.VALUE_STRING, p.nextToken()); // "true"
+ assertEquals(true, p.getValueAsBoolean());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals(false, p.getValueAsBoolean());
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals(false, p.getValueAsBoolean());
+
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertEquals(false, p.getValueAsBoolean());
+ assertEquals(true, p.getValueAsBoolean(true));
+
+ p.close();
+ }
+
+ public void testAsLong() throws Exception
+ {
+ for (int mode : ALL_MODES) {
+ _testAsLong(mode);
+ }
+ }
+
+ public void _testAsLong(int mode) throws Exception
+ {
+ final String input = "[ 1, -3, 4.98, true, false, null, \"-17\", \"foo\" ]";
+ JsonParser p = createParser(mode, input);
+
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertEquals(0L, p.getValueAsLong());
+ assertEquals(9L, p.getValueAsLong(9L));
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(1L, p.getValueAsLong());
+ assertEquals(1L, p.getValueAsLong(-99L));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(-3L, p.getValueAsLong());
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertEquals(4L, p.getValueAsLong());
+ assertEquals(4L, p.getValueAsLong(99L));
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+ assertEquals(1L, p.getValueAsLong());
+ assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+ assertEquals(0L, p.getValueAsLong());
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertEquals(0L, p.getValueAsLong());
+ assertEquals(0L, p.getValueAsLong(27L));
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals(-17L, p.getValueAsLong());
+ assertEquals(-17L, p.getValueAsLong(3L));
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals(0L, p.getValueAsLong());
+ assertEquals(9L, p.getValueAsLong(9L));
+
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertEquals(0L, p.getValueAsLong());
+ assertEquals(9L, p.getValueAsLong(9L));
+
+ p.close();
+ }
+
+ public void testAsDouble() throws Exception
+ {
+ for (int mode : ALL_MODES) {
+ _testAsDouble(mode);
+ }
+ }
+
+ private void _testAsDouble(int mode) throws Exception
+ {
+ final String input = "[ 1, -3, 4.98, true, false, null, \"-17.25\", \"foo\" ]";
+ JsonParser p = createParser(mode, input);
+
+ assertToken(JsonToken.START_ARRAY, p.nextToken());
+ assertEquals(0.0, p.getValueAsDouble());
+ assertEquals(9.0, p.getValueAsDouble(9.0));
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(1., p.getValueAsDouble());
+ assertEquals(1., p.getValueAsDouble(-99.0));
+ assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+ assertEquals(-3., p.getValueAsDouble());
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+ assertEquals(4.98, p.getValueAsDouble());
+ assertEquals(4.98, p.getValueAsDouble(12.5));
+ assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+ assertEquals(1.0, p.getValueAsDouble());
+ assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+ assertEquals(0.0, p.getValueAsDouble());
+ assertToken(JsonToken.VALUE_NULL, p.nextToken());
+ assertEquals(0.0, p.getValueAsDouble());
+ assertEquals(0.0, p.getValueAsDouble(27.8));
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals(-17.25, p.getValueAsDouble());
+ assertEquals(-17.25, p.getValueAsDouble(1.9));
+ assertToken(JsonToken.VALUE_STRING, p.nextToken());
+ assertEquals(0.0, p.getValueAsDouble());
+ assertEquals(1.25, p.getValueAsDouble(1.25));
+
+ assertToken(JsonToken.END_ARRAY, p.nextToken());
+ assertEquals(0.0, p.getValueAsDouble());
+ assertEquals(7.5, p.getValueAsDouble(7.5));
+
+ p.close();
+ }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/testsupport/MockDataInput.java b/src/test/java/com/fasterxml/jackson/core/testsupport/MockDataInput.java
new file mode 100644
index 0000000..bdd71b2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/testsupport/MockDataInput.java
@@ -0,0 +1,99 @@
+package com.fasterxml.jackson.core.testsupport;
+
+import java.io.*;
+
+public class MockDataInput implements DataInput
+{
+ private final InputStream _input;
+
+ public MockDataInput(byte[] data) {
+ _input = new ByteArrayInputStream(data);
+ }
+
+ public MockDataInput(String utf8Data) throws IOException {
+ _input = new ByteArrayInputStream(utf8Data.getBytes("UTF-8"));
+ }
+
+ public MockDataInput(InputStream in) {
+ _input = in;
+ }
+
+ @Override
+ public void readFully(byte[] b) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void readFully(byte[] b, int off, int len) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int skipBytes(int n) throws IOException {
+ return (int) _input.skip(n);
+ }
+
+ @Override
+ public boolean readBoolean() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public byte readByte() throws IOException {
+ int ch = _input.read();
+ if (ch < 0) {
+ throw new IOException("End-of-input for readByte()");
+ }
+ return (byte) ch;
+ }
+
+ @Override
+ public int readUnsignedByte() throws IOException {
+ return readByte() & 0xFF;
+ }
+
+ @Override
+ public short readShort() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int readUnsignedShort() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public char readChar() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int readInt() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long readLong() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public float readFloat() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public double readDouble() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String readLine() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String readUTF() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/testsupport/ThrottledInputStream.java b/src/test/java/com/fasterxml/jackson/core/testsupport/ThrottledInputStream.java
new file mode 100644
index 0000000..4b26a1e
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/testsupport/ThrottledInputStream.java
@@ -0,0 +1,32 @@
+package com.fasterxml.jackson.core.testsupport;
+
+import java.io.ByteArrayInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ThrottledInputStream extends FilterInputStream
+{
+ protected final int _maxBytes;
+
+ public ThrottledInputStream(byte[] data, int maxBytes)
+ {
+ this(new ByteArrayInputStream(data), maxBytes);
+ }
+
+ public ThrottledInputStream(InputStream in, int maxBytes)
+ {
+ super(in);
+ _maxBytes = maxBytes;
+ }
+
+ @Override
+ public int read(byte[] buf) throws IOException {
+ return read(buf, 0, buf.length);
+ }
+
+ @Override
+ public int read(byte[] buf, int offset, int len) throws IOException {
+ return in.read(buf, offset, Math.min(_maxBytes, len));
+ }
+}
\ No newline at end of file
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 9cce04a..8fe7db3 100644
--- a/src/test/java/com/fasterxml/jackson/core/util/TestDelegates.java
+++ b/src/test/java/com/fasterxml/jackson/core/util/TestDelegates.java
@@ -18,7 +18,7 @@
JsonParser parser = JSON_F.createParser("[ 1, true, null, { } ]");
JsonParserDelegate del = new JsonParserDelegate(parser);
- assertNull(del.getCurrentToken());
+ assertNull(del.currentToken());
assertToken(JsonToken.START_ARRAY, del.nextToken());
assertEquals("[", del.getText());
assertToken(JsonToken.VALUE_NUMBER_INT, del.nextToken());
diff --git a/src/test/java/com/fasterxml/jackson/core/util/TestNumberPrinting.java b/src/test/java/com/fasterxml/jackson/core/util/TestNumberPrinting.java
index 4f467fe..bad5bdb 100644
--- a/src/test/java/com/fasterxml/jackson/core/util/TestNumberPrinting.java
+++ b/src/test/java/com/fasterxml/jackson/core/util/TestNumberPrinting.java
@@ -11,8 +11,7 @@
public class TestNumberPrinting
extends com.fasterxml.jackson.core.BaseTest
{
- public void testIntPrinting()
- throws Exception
+ public void testIntPrinting() throws Exception
{
assertIntPrint(0);
assertIntPrint(-3);
@@ -39,8 +38,7 @@
}
}
- public void testLongPrinting()
- throws Exception
+ public void testLongPrinting() throws Exception
{
// First, let's just cover couple of edge cases
assertLongPrint(0L, 0);
@@ -60,9 +58,9 @@
}
/*
- ////////////////////////////////////////////////////////
- // Internal methods
- ////////////////////////////////////////////////////////
+ /**********************************************************
+ /* Internal methods
+ /**********************************************************
*/
private void assertIntPrint(int value)
@@ -73,6 +71,10 @@
if (!exp.equals(act)) {
assertEquals("Expected conversion (exp '"+exp+"', len "+exp.length()+"; act len "+act.length()+")", exp, act);
}
+ String alt = NumberOutput.toString(value);
+ if (!exp.equals(alt)) {
+ assertEquals("Expected conversion (exp '"+exp+"', len "+exp.length()+"; act len "+act.length()+")", exp, act);
+ }
}
private void assertLongPrint(long value, int index)
@@ -83,6 +85,10 @@
if (!exp.equals(act)) {
assertEquals("Expected conversion (exp '"+exp+"', len "+exp.length()+"; act len "+act.length()+"; number index "+index+")", exp, act);
}
+ String alt = NumberOutput.toString(value);
+ if (!exp.equals(alt)) {
+ assertEquals("Expected conversion (exp '"+exp+"', len "+exp.length()+"; act len "+act.length()+"; number index "+index+")", exp, act);
+ }
}
private String printToString(int value)
@@ -99,4 +105,3 @@
return new String(buffer, 0, offset);
}
}
-