pax_global_header00006660000000000000000000000064135616424730014525gustar00rootroot0000000000000052 comment=60956fccd24d6bdb66abb3182cb2234122039b7a jackson-core-jackson-core-2.10.1/000077500000000000000000000000001356164247300165405ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/.gitattributes000066400000000000000000000001371356164247300214340ustar00rootroot00000000000000# Do not merge `pom.xml` from older version, as it will typically conflict pom.xml merge=ours jackson-core-jackson-core-2.10.1/.gitignore000066400000000000000000000002621356164247300205300ustar00rootroot00000000000000# use glob syntax. syntax: glob *.class *~ *.bak *.off *.old *.java.orig .DS_Store # building /target # Eclipse .classpath .project .settings # IDEA *.iml *.ipr *.iws /.idea/ jackson-core-jackson-core-2.10.1/.travis.yml000066400000000000000000000020551356164247300206530ustar00rootroot00000000000000language: java # 08-Dec-2018, tatu: While it should be possible to run core streaming on Java 6, # build won't work with anything below Java 8 now - openjdk8 - oraclejdk11 # Below this line is configuration for deploying to the Sonatype OSS repo # http://blog.xeiam.com/2013/05/configure-travis-ci-to-deploy-snapshots.html before_install: "git clone -b travis `git config --get remote.origin.url` target/travis" after_success: "mvn deploy --settings target/travis/settings.xml" # whitelist branches: only: - master - "2.10" # to make jdk6 work, as per: https://github.com/travis-ci/travis-ci/issues/8199 addons: apt: packages: - openjdk-6-jdk env: global: - secure: "YW0hrdsHvH41pb5uPJ2DGzXrBgOVT7nEyag/bAQoDcSlOQ/g55tnY6rIGkqE/aYD47IroTEhW4yLyM3tZpbrqwagX4dUX90ukjcUwUvFE1ePTSEfdBtuHVwl8f6HmLIIw2yK0dQ1gOJ21T+3g+wddvK+6sWBJJ+s3O1FePDh6X0=" - secure: "sGQxvyfg98BFcJcWHQ5BjvDNhbwdgD1yEfkE3qzH4/gzwD/ND1jKhkCX++Glt3DuyAKhENNzXlSkztdCE5wKfK3X6MVvOgzMgiV/BhHIf09EtAjZ35fe4pr+GZImfGZO3qkViooTz3FDJyKJBA3YyMTuo9/eWK8HlUFCZHTjKP8=" jackson-core-jackson-core-2.10.1/LICENSE000066400000000000000000000004751356164247300175530ustar00rootroot00000000000000This copy of Jackson JSON processor annotations is licensed under the Apache (Software) License, version 2.0 ("the License"). See the License for details about distribution rights, and the specific rights regarding derivate works. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 jackson-core-jackson-core-2.10.1/README.md000066400000000000000000000115761356164247300200310ustar00rootroot00000000000000# Overview This project contains core low-level incremental ("streaming") parser and generator abstractions used by [Jackson Data Processor](http://wiki.fasterxml.com/JacksonHome). It also includes the default implementation of handler types (parser, generator) that handle JSON format. The core abstractions are not JSON specific, although naming does contain 'JSON' in many places, due to historical reasons. Only packages that specifically contain word 'json' are JSON-specific. This package is the base on which [Jackson data-binding](https://github.com/FasterXML/jackson-databind) package builds on. It is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0). For additional/alternative licensing questions, please contact `info@fasterxml.com`: affordable commercial licenses available for use cases like Android app development. Alternate data format implementations (like [Smile (binary JSON)](https://github.com/FasterXML/jackson-dataformat-smile), [XML](https://github.com/FasterXML/jackson-dataformat-xml), [CSV](https://github.com/FasterXML/jackson-dataformat-csv)) and [CBOR](https://github.com/FasterXML/jackson-dataformat-cbor) also build on this base package, implementing the core interfaces, making it possible to use standard [data-binding package](https://github.com/FasterXML/jackson-databind) regardless of underlying data format. Project contains versions 2.0 and above: source code for earlier (1.x) versions is available from [Codehaus](http://jackson.codehaus.org) SVN repository. [![Build Status](https://travis-ci.org/FasterXML/jackson-core.svg?branch=master)](https://travis-ci.org/FasterXML/jackson-core) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.jackson.core/jackson-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.jackson.core/jackson-core) [![Javadoc](https://javadoc-emblem.rhcloud.com/doc/com.fasterxml.jackson.core/jackson-core/badge.svg)](http://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-core) [![Coverage Status](https://coveralls.io/repos/github/FasterXML/jackson-core/badge.svg?branch=master)](https://coveralls.io/github/FasterXML/jackson-core?branch=master) # Get it! ## Maven Functionality of this package is contained in Java package `com.fasterxml.jackson.core`. To use the package, you need to use following Maven dependency: ```xml com.fasterxml.jackson.core jackson-core ${jackson-core-version} ``` or download jars from Maven repository or links on [Wiki](../../wiki). Core jar is a functional OSGi bundle, with proper import/export declarations. Package has no external dependencies, except for testing (which uses `JUnit`). ## Non-Maven For non-Maven use cases, you download jars from [Central Maven repository](http://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-core/) or [Wiki](../../wiki). Core jar is also a functional OSGi bundle, with proper import/export declarations, so it can be use on OSGi container as is. ----- # Use it! ## General Usage typically starts with creation of a reusable (and thread-safe, once configured) `JsonFactory` instance: ```java JsonFactory factory = new JsonFactory(); // configure, if necessary: factory.enable(JsonParser.Feature.ALLOW_COMMENTS); ``` Alternatively, you have a `ObjectMapper` (from [Jackson Databind package](https://github.com/FasterXML/jackson-databind)) handy; if so, you can do: ```java JsonFactory factory = objectMapper.getFactory(); ``` ## Usage, simple reading All reading is by using `JsonParser` (or its sub-classes, in case of data formats other than JSON), instance of which is constructed by `JsonFactory`. An example can be found from [Reading and Writing Event Streams](http://www.cowtowncoder.com/blog/archives/2009/01/entry_132.html) ## Usage, simple writing All writing is by using `JsonGenerator` (or its sub-classes, in case of data formats other than JSON), instance of which is constructed by `JsonFactory`: An example can be found from [Reading and Writing Event Streams](http://www.cowtowncoder.com/blog/archives/2009/01/entry_132.html) ----- # Further reading ## Differences from Jackson 1.x Project contains versions 2.0 and above: source code for earlier (1.x) versions is available from [Codehaus](http://jackson.codehaus.org) SVN repository Note that the main differences compared to 1.0 core jar are: * Maven build instead of Ant * Annotations carved out to a separate package (that this package depends on) * Java package is now `com.fasterxml.jackson.core` (instead of `org.codehaus.jackson`) ## Links * Project [Wiki](../../wiki) has JavaDocs and links to downloadable artifacts * [Jackson Github Hub](https://github.com/FasterXML/jackson) has links to all official Jackson components * [Jackson Github Doc](https://github.com/FasterXML/jackson-docs) is the hub for official Jackson documentation jackson-core-jackson-core-2.10.1/create-test-report.sh000077500000000000000000000000501356164247300226230ustar00rootroot00000000000000#!/bin/sh mvn surefire-report:report jackson-core-jackson-core-2.10.1/pom.xml000066400000000000000000000110401356164247300200510ustar00rootroot00000000000000 4.0.0 com.fasterxml.jackson jackson-base 2.10.1 com.fasterxml.jackson.core jackson-core Jackson-core 2.10.1 bundle Core Jackson processing abstractions (aka Streaming API), implementation for JSON 2008 https://github.com/FasterXML/jackson-core scm:git:git@github.com:FasterXML/jackson-core.git scm:git:git@github.com:FasterXML/jackson-core.git http://github.com/FasterXML/jackson-core jackson-core-2.10.1 1.6 1.6 1.6 1.6 com.fasterxml.jackson.core;version=${project.version}, com.fasterxml.jackson.core.*;version=${project.version} com/fasterxml/jackson/core/json ${project.groupId}.json sonatype-nexus-snapshots Sonatype Nexus Snapshots https://oss.sonatype.org/content/repositories/snapshots false true org.jacoco jacoco-maven-plugin 0.8.4 prepare-agent report test report maven-enforcer-plugin enforce-properties validate enforce org.apache.maven.plugins maven-site-plugin org.apache.maven.plugins maven-surefire-plugin ${version.plugin.surefire} ${surefire.redirectTestOutputToFile} **/failing/**/*.java com.google.code.maven-replacer-plugin replacer org.moditect moditect-maven-plugin jackson-core-jackson-core-2.10.1/release-notes/000077500000000000000000000000001356164247300213065ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/release-notes/CREDITS-2.x000066400000000000000000000151751356164247300227440ustar00rootroot00000000000000Here are people who have contributed to the development of Jackson JSON processor core component, version 2.x (version numbers in brackets indicate release in which the problem was fixed) (note: for older credits, check out release notes for 1.x versions) Tatu Saloranta, tatu.saloranta@iki.fi: author Pascal G�linas: * Reported [JACKSON-827]: 2.0.0 was accidentally requiring JDK 1.6 (should still be 1.5) (2.0.1) Ben Gertzfield (bgertzfield@github): * Contributed [Issue#49]: Improvements to VersionUtil to more efficiently read dynamically generated/embedded version information, to improve Android startup time (2.2.0) Klaus Brunner (KlausBrunner@github) * Reported [Issue#48]: Problem with URLs, spaces Eugene Brevdo (ebrevdo@github) * Contributed #84: Support 'Infinity' as alternative (no leading plus) (2.2.3) Marcin Zukowski (eruure@github) * Reported #115: JsonGenerator writeRawValue problem with surrogate UTF-8 characters (2.3.0) Steve van Loben Sels * Reported #116: WriterBasedJsonGenerator produces truncated Unicode escape sequences (2.3.0) Shay Banon * Reported #145: NPE at BytesToNameCanonicalizer (2.4.2) rjmac@github * Reported #146: Error while parsing negative floats at the end of the input buffer (2.4.2) * Reported #148: BytesToNameCanonicalizer can mishandle leading null byte(s). (2.5.0) Alex Soto: (lordofthejars@github) * Reported #173: An exception is thrown for a valid JsonPointer expression (2.4.5) Aaron Digulla: * Contributed #166: Allow to configure line endings and indentation (2.5.0) Derek Clarkson (drekka@github) * Reported #184: WRITE_NUMBERS_AS_STRINGS disables WRITE_BIGDECIMAL_AS_PLAIN (2.4.6 / 2.5.2) Masaru Hasegawa (masaruh@github): * Reported, contributed fix for#182: Inconsistent TextBuffer#getTextBuffer behavior (2.6.0) Ruediger Moeller (RuedigerMoeller@github) * Requested #195: Add `JsonGenerator.getOutputBuffered()` to find out amount of content buffered, not yet flushed. (2.6.0) Florian Schoppmann (fschopp@github@github) * Reported #207: `ArrayIndexOutOfBoundsException` in `ByteQuadsCanonicalizer` (2.6.1) Iskren Ivov Chernev (ichernev@github) * Reported #213: Parser is sometimes wrong when using CANONICALIZE_FIELD_NAMES (2.6.2) Michael Lehenbauer (mikelehen@github) * Reported #37: JsonParser.getTokenLocation() doesn't update after field names (2.7.0) 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() should delegate to writeUTF8String() (2.7.5) Mike Naseef (mtnaseef@github) * Reported #307: JsonGenerationException: Split surrogate on writeRaw() input thrown for input of a certain size (2.7.7) Allar Haav (haav@github) * Reportef #317: ArrayIndexOutOfBoundsException: 200 on floating point number with exactly 200-length decimal part (2.7.8) 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) Alessio Soldano (asoldano@github) * Contributed #322: Trim tokens in error messages to 256 byte to prevent attacks (2.8.6) Arnaud Roger (arnaudroger@github) * Contributed #359: FilteringGeneratorDelegate does not override writeStartObject(Object forValue) (2.8.8) Emily Selwood (emilyselwood@github) * Reported #382: ArrayIndexOutOfBoundsException from UTF32Reader.read on invalid input (2.8.9) * Reported #578: Array index out of bounds in hex lookup (2.10.1) Alex Yursha (AlexYursha@github) * Contributed #312: Add `JsonProcessingException.clearLocation()` to allow clearing possibly security-sensitive information (2.9.0) Brad Hess (bdhess@github) * Contributed #323: Add `JsonParser.ALLOW_TRAILING_COMMA` to work for Arrays and Objects (2.9.0) * Reported #325: `DataInput` backed parser should handle `EOFException` at end of doc (2.9.0) Logan Widick (uhhhh2@github) * Contributed #17: Add 'JsonGenerator.writeString(Reader r, int charLength)' (2.9.0) Michael Sims (MichaelSims@github) * Reported, contributed fix for #372: JsonParserSequence#skipChildren() throws exception when current delegate is TokenBuffer.Parser with "incomplete" JSON (2.9.0) Rafal Foltynski (rfoltyns@github) * Contributed #374: Minimal and DefaultPrettyPrinter with configurable separators (2.9.0) * Contributed#208: Make use of `_matchCount` in `FilteringParserDelegate` (2.9.0) Jeroen Borgers (jborgers@github) * Reported, contributed impl for #400: Add mechanism for forcing `BufferRecycler` released (to call on shutdown) (2.9.6) Doug Roper (htmldoug@github) * Suggested #463: Ensure that `skipChildren()` of non-blocking `JsonParser` will throw exception if not enough input (2.9.6) * Reported, Contributed test for #563: Async parser does not keep track of Array context properly (2.10.0) Alexander Eyers-Taylor (aeyerstaylor@github) * Reported #510: Fix ArrayIndexOutofBoundsException found by LGTM.com (2.9.9) Henrik Gustafsson (gsson@github) * Reported #516: _inputPtr off-by-one in UTF8StreamJsonParser._parseNumber2() (2.9.9) Alex Rebert (alpire@github) * Reported #540, suggested fix: UTF8StreamJsonParser: fix byte to int conversion for malformed escapes (2.9.10) * Reported #547: `CharsToNameCanonicalizer`: Internal error on `SymbolTable.rehash()` with high number of hash collisions (2.10.0) * Reported #548: ByteQuadsCanonicalizer: ArrayIndexOutOfBoundsException in addName (2.10.0) Sam Smith (Oracle Security Researcher) * Reported #540 (concurrently with Alex R, before fix was included) (2.9.10) Philippe Marschall (marschall@github) * Requested #480: `SerializableString` value can not directly render to Writer (2.10.0) David Nault (dnault@github) * Reported #531: Non-blocking parser reports incorrect locations when fed with non-zero offset (2.10.0) Fabien Renaud (fabienrenaud@github) * Reported, contributed fix fir #533: UTF-8 BOM not accounted for in `JsonLocation.getByteOffset()` (2.10.0) Todd O'Bryan (toddobryan@github) * Contributed fix fox #455: Jackson reports wrong locations for JsonEOFException (2.10.1) jackson-core-jackson-core-2.10.1/release-notes/VERSION-2.x000066400000000000000000000556641356164247300230030ustar00rootroot00000000000000Project: jackson-core Contains core streaming reader (`JsonParser`) and writer (`JsonGenerator`) abstractions, factory for constructing readers/writers (JsonFactory), as well as a minimal set of interfaces needed for streaming level to make callbacks and call-throughs, via `ObjectCodec` and `TreeNode`. Also includes implementation of this API for JSON. Forms the base for other data formats as well, despite naming that suggests JSON-specificity: naming is due to history, as Jackson started out as pure JSON library. ------------------------------------------------------------------------ === Releases === ------------------------------------------------------------------------ 2.10.1 (09-Nov-2019) #455: Jackson reports wrong locations for JsonEOFException (reported by wastevenson@github, fix contributed by Todd O'B #567: Add `uses` for `ObjectCodec` in module-info (reported by Marc M) #578: Array index out of bounds in hex lookup (reported by Emily S) 2.10.0 (26-Sep-2019) #433: Add Builder pattern for creating configured Stream factories #464: Add "maximum unescaped char" configuration option for `JsonFactory` via builder #467: Create `JsonReadFeature` to move JSON-specific `JsonParser.Feature`s to #479: Improve thread-safety of buffer recycling #480: `SerializableString` value can not directly render to Writer (requested by Philippe M) #481: Create `JsonWriteFeature` to move JSON-specific `JsonGenerator.Feature`s to #484: Implement `UTF8JsonGenerator.writeRawValue(SerializableString)` (and `writeRaw(..)`) more efficiently #495: Create `StreamReadFeature` to move non-json specific `JsonParser.Feature`s to #496: Create `StreamWriteFeature` to take over non-json-specific `JsonGenerator.Feature`s #502: Make `DefaultPrettyPrinter.createInstance()` to fail for sub-classes #506: Add missing type parameter for `TypeReference` in `ObjectCodec` #508: Add new exception type `InputCoercionException` to be used for failed coercions like overflow for `int` #517: Add `JsonGenerator.writeStartObject(Object, int)` (needed by CBOR, maybe Avro) #527: Add simple module-info for JDK9+, using Moditect #533: UTF-8 BOM not accounted for in JsonLocation.getByteOffset() (contributed by Fabien R) #539: Reduce max size of recycled byte[]/char[] blocks by `TextBuffer`, `ByteArrayBuilder` #547: `CharsToNameCanonicalizer`: Internal error on `SymbolTable.rehash()` with high number of hash collisions (reported by Alex R) #548: ByteQuadsCanonicalizer: ArrayIndexOutOfBoundsException in addName (reported by Alex R) #549: Add configurability of "quote character" for JSON factory #561: Misleading exception for unquoted String parsing #563: Async parser does not keep track of Array context properly (reported by Doug R) - Rewrite `JsonGenerator.copyCurrentStructure()` to remove recursion) - Add `missingNode()`, `nullNode()` in `TreeCodec` - Add `JsonParserDelegate.delegate()` methods 2.9.10 (21-Sep-2019) #540: UTF8StreamJsonParser: fix byte to int conversion for malformed escapes (reported by Alex R and Sam S) #556: 'IndexOutOfBoundsException' in UTF8JsonGenerator.writeString(Reader, len) when using a negative length (reported by jacob-alan-ward@github) 2.9.9 (16-May-2019) #516: _inputPtr off-by-one in UTF8StreamJsonParser._parseNumber2() (reported by Henrik G) #531: Non-blocking parser reports incorrect locations when fed with non-zero offset (reported by David N) 2.9.8 (15-Dec-2018) #488: Fail earlier on coercions from "too big" `BigInteger` into fixed-size types (`int`, `long`, `short`) #510: Fix ArrayIndexOutofBoundsException found by LGTM.com (reported by Alexander E-T) - Improve exception message for missing Base64 padding (see databind#2183) 2.9.7 (19-Sep-2018) #476: Problem with `BufferRecycler` via async parser (or when sharing parser across threads) #477: Exception while decoding Base64 value with escaped `=` character #488: Fail earlier on coercions from "too big" `BigInteger` into fixed-size types (`int`, `long`, `short`) 2.9.6 (12-Jun-2018) #400: Add mechanism for forcing `BufferRecycler` released (to call on shutdown) (contributed by Jeroen B) #460: Failing to link `ObjectCodec` with `JsonFactory` copy constructor #463: Ensure that `skipChildren()` of non-blocking `JsonParser` will throw exception if not enough input (requested by Doug R) 2.9.5 (26-Mar-2018) No changes since 2.9.4 2.9.4 (24-Jan-2018) #414: Base64 MIME variant does not ignore white space chars as per RFC2045 (reported by tmoschou@github) #437: `ArrayIndexOutOfBoundsException` in `UTF8StreamJsonParser` (reported by Igor A) 2.9.3 (09-Dec-2017) #419: `ArrayIndexOutOfBoundsException` from `UTF32Reader.read()` on invalid input 2.9.2 (13-Oct-2017) - New parent pom (`jackson-base`) 2.9.1 (07-Sep-2017) #397: Add `Automatic-Module-Name` ("com.fasterxml.jackson.core") for JDK 9 module system 2.9.0 (30-Jul-2017)) #17: Add 'JsonGenerator.writeString(Reader r, int charLength)' (constributed by Logan W) #57: Add support for non-blocking ("async") JSON parsing #208: Make use of `_matchCount` in `FilteringParserDelegate` (contributed by Rafal F) #242: Add new write methods in `JsonGenerator` for writing type id containers #304: Optimize `NumberOutput.outputLong()` method #306: Add new method in `JsonStreamContext` to construct `JsonPointer` #312: Add `JsonProcessingException.clearLocation()` to allow clearing possibly security-sensitive information (contributed by Alex Y) #314: Add a method in `JsonParser` to allow checking for "NaN" values #323: Add `JsonParser.ALLOW_TRAILING_COMMA` to work for Arrays and Objects (contributed by Brad H) #325: `DataInput` backed parser should handle `EOFException` at end of doc (reported by Brad H) #330: `FilteringParserDelegate` seems to miss last closing `END_OBJECT` (contributed by Rafal F) #340: Making `WriterBasedJsonGenerator` non-final (requested by rfoltyns@github) #356: Improve indication of "source reference" in `JsonLocation` wrt `byte[]`,`char[]` #372: JsonParserSequence#skipChildren() throws exception when current delegate is TokenBuffer.Parser with "incomplete" JSON (contributed by Michael S) #374: Minimal and DefaultPrettyPrinter with configurable separators (contributed by Rafal F) 2.8.11 (23-Dec-2017) #418: ArrayIndexOutOfBoundsException from UTF32Reader.read on invalid input (reported, contributed fix for by pfitzsimons-r7@github) 2.8.10 (24-Aug-2017) No changes since 2.8.9 2.8.9 (12-Jun-2017) #382: ArrayIndexOutOfBoundsException from UTF32Reader.read on invalid input (reported by Wil S) 2.8.8 (05-Apr-2017) #359: FilteringGeneratorDelegate does not override writeStartObject(Object forValue) (contributed by Arnaud R) #362: Use correct length variable for UTF-8 surrogate writing 2.8.7 (21-Feb-2017) #349: CharsToNameCanonicalizer performance bottleneck (reported by Nuno D, nmldiegues@github) #351: `java.lang.NegativeArraySizeException` at `ByteArrayBuilder.toByteArray()` #354: Buffer size dependency in UTF8JsonGenerator writeRaw (reported by Chistopher C) 2.8.6 (12-Jan-2017) #322: Trim tokens in error messages to 256 byte to prevent attacks (contributed by Alessio S) #335: Missing exception for invalid last character of base64 string to decode using `Base64Variant.decode()` 2.8.5 (14-Nov-2016) 2.8.4 (14-Oct-2016) No changes since 2.8.3 2.8.3 (17-Sep-2016) #318: Add support for writing `byte[]` via `JsonGenerator.writeEmbeddedObject()` 2.8.2 (30-Aug-2016) 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.9.3: #1872: NullPointerException in SubTypeValidator.validateSubType when validating Spring interface #1931: Two more c3p0 gadgets to exploit default typing issue 2.7.9.2 (20-Dec-2017) #1607: `@JsonIdentityReference` not used when setup on class only #1628: Don't print to error stream about failure to load JDK 7 types #1680: Blacklist couple more types for deserialization #1737: Block more JDK types from polymorphic deserialization #1855: Blacklist for more serialization gadgets (dbcp/tomcat, spring) 2.7.9.1 (18-Apr-2017) #1599: Jackson Deserializer security vulnerability 2.7.9 (04-Feb-2017) No changes since 2.7.8 2.7.8 (26-Sep-2016) #317: ArrayIndexOutOfBoundsException: 200 on floating point number with exactly 200-length decimal part (reported by Allar H) 2.7.7 (27-Aug-2016) #307: JsonGenerationException: Split surrogate on writeRaw() input thrown for input of a certain size (reported by Mike N) #315: `OutOfMemoryError` when writing BigDecimal (reported by gmethwin@github) 2.7.6 (23-Jul-2016) - Clean up of FindBugs reported possible issues. 2.7.5 (11-Jun-2016) #280: FilteringGeneratorDelegate.writeUTF8String() should delegate to writeUTF8String() (reported by Tanguy L) 2.7.4 (29-Apr-2016) #209: Make use of `_allowMultipleMatches` in `FilteringParserDelegate` (contributed by Lokesh N) - Make `processor` transient in `JsonParseException`, `JsonGenerationException` to keep both Serializable 2.7.3 (16-Mar-2016) No changes since 2.7.2. 2.7.2 (26-Feb-2016) #246: Fix UTF8JsonGenerator to allow QUOTE_FIELD_NAMES to be toggled (suggested by philipa@github) 2.7.1 (02-Feb-2016) No changes since 2.7.0. 2.7.0 (10-Jun-2016) #37: JsonParser.getTokenLocation() doesn't update after field names (reported by Michael L) #198: Add back-references to `JsonParser` / `JsonGenerator` for low-level parsing issues (via `JsonParseException`, `JsonGenerationException`) #211: Typo of function name com.fasterxml.jackson.core.Version.isUknownVersion() (reported by timray@github) #229: Array element and field token spans include previous comma. - Implemented `ReaderBasedJsonParser.nextFieldName(SerializableString)` (to improved Afterburner performance over String/char[] sources) 2.6.6 (05-Apr-2016) #248: VersionUtil.versionFor() unexpectedly return null instead of Version.unknownVersion() (reported by sammyhk@github) 2.6.5 (19-Jan-2016) 2.6.4 (07-Dec-2015) No changes since 2.6.3. 2.6.3 (12-Oct-2015) #220: Problem with `JsonParser.nextFieldName(SerializableString)` for byte-backed parser 2.6.2 (14-Sep-2015) #213: Parser is sometimes wrong when using CANONICALIZE_FIELD_NAMES (reported by ichernev@github) #216: ArrayIndexOutOfBoundsException: 128 when repeatedly serializing to a byte array (reported by geekbeast@github) 2.6.1 (09-Aug-2015) #207: `ArrayIndexOutOfBoundsException` in `ByteQuadsCanonicalizer` (reported by Florian S, fschopp@github) 2.6.0 (17-Jul-2015) #137: Allow filtering content read via `JsonParser` by specifying `JsonPointer`; uses new class `com.fasterxml.jackson.core.filter.FilteringParserDelegate` (and related, `TokenFilter`) #177: Add a check so `JsonGenerator.writeString()` won't work if `writeFieldName()` expected. #182: Inconsistent TextBuffer#getTextBuffer behavior (contributed by Masaru H) #185: Allow filtering content written via `JsonGenerator` by specifying `JsonPointer`; uses new class `com.fasterxml.jackson.core.filter.FilteringGeneratorDelegate` (and related, `TokenFilter`) #188: `JsonParser.getValueAsString()` should return field name for `JsonToken.FIELD_NAME`, not `null` #189: Add `JsonFactory.Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING` (default: true), which may be disabled to prevent use of ThreadLocal-based buffer recyling. (suggested by soldierkam@github) #195: Add `JsonGenerator.getOutputBuffered()` to find out amount of content buffered, not yet flushed. (requested by Ruediger M) #196: Add support for `FormatFeature` extension, for format-specifc Enum-backed parser/generator options - Minor improvement to construction of "default PrettyPrinter": now overridable by data format modules - Implement a new yet more optimized symbol table for byte-backed parsers - Add `JsonParser.Feature.IGNORE_UNDEFINED`, useful for data formats like protobuf - Optimize writing of String names (remove intermediate copy; with JDK7 no speed benefit) 2.5.5 (07-Dec-2015) #220: Problem with `JsonParser.nextFieldName(SerializableString)` for byte-backed parser #221: Fixed ArrayIndexOutOfBounds exception for character-based `JsonGenerator` (reported by a-lerion@github) 2.5.4 (09-Jun-2015) No changes. 2.5.3 (24-Apr-2015) #191: Longest collision chain in symbol table now exceeds maximum -- suspect a DoS attack (reported by Paul D) 2.5.2 (29-Mar-2015) #181: Failure parsing -Infinity on buffer boundary (reported by brharrington@github) #187: Longest collision chain in symbol table exceeds maximum length routinely in non-malicious code (reported by mazzaferri@github) 2.5.1 (06-Feb-2015) #178: Add `Lf2SpacesIndenter.withLinefeed` back to keep binary-compatibility with 2.4.x (reported by ansell@github) - Minor fix to alignment of null bytes in the last 4 bytes of name, in case where name may cross the input boundary 2.5.0 (01-Jan-2015) #148: BytesToNameCanonicalizer can mishandle leading null byte(s). (reported by rjmac@github) #164: Add `JsonGenerator.Feature.IGNORE_UNKNOWN` (but support via individual data format modules) #166: Allow to configure line endings and indentation (contributed by Aaron D) #167: `JsonGenerator` not catching problem of writing field name twice in a row #168: Add methods in `JsonStreamContext` for keeping track of "current value" #169: Add `JsonPointer.head()` (contributed by Alex S, lordofthejars@github) - Added `ResolvedType.getParameterSource()` to support better resolution of generic types. - Added `JsonGenerator.writeRawValue(SerializableString)` - Added `JsonParser.hasExpectedStartObjectToken()` convenience method - Added `JsonParser.hasTokenId(id)` convenience method - Added `JsonParser.nextFieldName()` (no args) 2.4.6 (23-Apr-2015) #184: WRITE_NUMBERS_AS_STRINGS disables WRITE_BIGDECIMAL_AS_PLAIN (reported by Derek C) 2.4.5 (13-Jan-2015) No changes since 2.4.4. 2.4.4 (24-Nov-2014) #157: ArrayIndexOutOfBoundsException: 200 on numbers with more than 200 digits. (reported by Lars P, larsp@github) #173: An exception is thrown for a valid JsonPointer expression (reported by Alex S) #176: `JsonPointer` should not consider "00" to be valid index (reported by fge@gitub) - Fix `JsonGenerator.setFeatureMask()` to better handle dynamic changes. 2.4.3 (02-Oct-2014) #152: Exception for property names longer than 256k (reported by CrendKing@github) 2.4.2 (13-Aug-2014) #145: NPE at BytesToNameCanonicalizer (reported by Shay B) #146: Error while parsing negative floats at the end of the input buffer (reported by rjmac@github) 2.4.1 (16-Jun-2014) #143: Flaw in `BufferRecycler.allocByteBuffer(int,int)` that results in performance regression 2.4.0 (29-May-2014) #121: Increase size of low-level byte[]/char[] input/output buffers (from 4k->8k for bytes, 2k->4k for chars) #127: Add `JsonGenerator.writeStartArray(int size)` for binary formats #138: Add support for using `char[]` as input source; optimize handling of `String` input as well. - Refactor `BufferRecycler` to eliminate helper enums 2.3.5 (13-Jan-2015) #152: Exception for property names longer than 256k #173: An exception is thrown for a valid JsonPointer expression #176: `JsonPointer` should not consider "00" to be valid index 2.3.4 (17-Jul-2014) 2.3.3 (10-Apr-2014) No changes since 2.3.2. 2.3.2 (01-Mar-2014) #126: Revert some 1.6 back to make core lib work with Android 2.2 (FroYo) (contributed by Goncalo S) #129: Missing delegation method, `JsonParserDelegate.isExpectedStartArrayToken()` (Pascal G) #133: Prevent error on JsonPointer expressions for properties that have numeric ids above 32-bit range (reported by mrstlee@github) 2.3.1 (28-Dec-2013) No functional changes. 2.3.0 (13-Nov-2013) #8: Add methods in `JsonParser`/`JsonGenerator` for reading/writing Object Ids #47: Support YAML-style comments with `JsonParser.Feature.ALLOW_YAML_COMMENTS` #60: Add a feature (`JsonParser.Feature.STRICT_DUPLICATE_DETECTION`) to verify that input does not contain duplicate filed names #77: Improve error reporting for unrecognized tokens (requested by cowwoc@github) #85: Add `JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN` #91: Add methods in `JsonGenerator` for writing native Type Ids #92: Add methods in `JsonParser` for reading native Type Ids #93: Add `getFeatureMask()`, `setFeatureMask()` in `JsonGenerator`/`JsonParser` #94: Allow coercion of String value "null" (similar to handling of null token) #96: Add `JsonFactory.requiresPropertyOrdering()` introspection method #97: JsonGenerator's `JsonWriteContext` not maintained properly, loses current field name (reported by Sam R) #98: Improve handling of failures for `BigDecimal`, for "NaN" (and infinity) #102: Unquoted field names can not start with a digit #103: Add `JsonFactory.canHandleBinaryNatively`, `JsonGenerator.canWriteBinaryNatively` to let databind module detect level of support for binary data. #105: Parser parsers numbers eagerly; does not report error with missing space #106: Add `JsonGenerator.Feature.STRICT_DUPLICATE_DETECTION` for preventing dup names #110: Improve overridability of `JsonGeneratorDelegate` (suggested by qpliu@github) #111: _currInputRowStart isn't initialized in UTF8StreamJsonParser() constructor (reported by dreamershl@github) #115: JsonGenerator writeRawValue problem with surrogate UTF-8 characters (reported by Marcin Z) #116: WriterBasedJsonGenerator produces truncated Unicode escape sequences (reported by Steve L-S) - Improve `DefaultPrettyPrinter`, `Lf2SpacesIndenter` (from databind #276) - Add `JsonGenerator.canOmitFields()` method to support discovery of positional formats, needed for handling of filtering for CSV - Rewrite `InternCache` to use `ConcurrentHashMap`, to work more efficiently both for common case of few misses (no block on access), and slowest cases (lots of misses). - Add `JsonPointer` implementation, to be used by tree model, streaming - Make `UTF8StreamJsonParser` non-final, for potential sub-classing 2.2.3 (23-Aug-2013) #78: ArrayIndexOutOfBoundsException for very long numbers (>500 digits) (reported by boothen@github) #81: CharTypes.appendQuoted misencodes first 32 Unicode values as '\0000' (reported by githubaff0@github) #84: Support for parsing 'Infinity' when feature ALLOW_NON_NUMERIC_NUMBERS is on (contributed by ebrevdo@github) - Add `Base64Variant.decode()` convenience methods 2.2.2 (26-May-2013) No changes since previous version. 2.2.1 (03-May-2013) #72: JsonFactory.copy() was not copying settings properly (reported by Christian S (squiddle@github)) - Moved VERSION/LICENSE contained in jars under META-INF/, to resolve Android packaging (APK) issues 2.2.0 (22-Apr-2013) Fixes: #51: JsonLocation had non-serializable field, mark as transient Improvements #46, #49: Improve VersionUtil to generate PackageVersion, instead of reading VERSION.txt from jar -- improves startup perf on Android significantly (contributed by Ben G) #59: Add more functionality in `TreeNode` interface, to allow some level of traversal over any and all Tree Model implementations #69: Add support for writing `short` values in JsonGenerator 2.1.3 (19-Jan-2013) * [JACKSON-884]: JsonStringEncoder.quoteAsStringValue() fails to encode ctrl chars correctly. * [Issue#48] Problems with spaces in URLs (reported by KlausBrunner) 2.1.2 (04-Dec-2012) * [Issue#42] Problems with UTF32Reader (reported by James R [jroper@github]) * Added missing methods (like 'setPrettyPrinter()' in JsonGeneratorDelegate 2.1.1 (11-Nov-2012) * [Issue#34] `JsonParser.nextFieldName()` fails on buffer boundary (reported by gsson@github) * [Issue#38] `JsonParser.nextFieldName()` problems when handling names with trailing spaces (reported by matjazs@github) 2.1.0 (08-Oct-2012) A new minor version for 2.x. New features: * [Issue#14]: add 'readBinaryValue(...)' method in JsonParser * [Issue#16]: add 'writeBinary(InputStream, int)' method in JsonGenerator (and implement for JSON backend) * [Issue#26]: Allow overriding "root value separator" (suggested by Henning S) Improvements: * [JACKSON-837]: Made JsonGenerator implement Flushable. (suggested by Matt G) * [Issue#10]: add 'JsonProcessingException.getOriginalMessage()' for accessing message without location info * [Issue#31]: make `JsonFactory` java.io.Serializable (via JDK) Other: * [Issue-25]: Add 'createParser' and 'createGenerator' (as eventual replacements for 'createJsonParser'/'createJsonGenerator') in 'JsonFactory' * Try to improve locking aspects of symbol tables, by reducing scope of synchronized sections when creating, merging table contents. * Added 'JsonFactory.copy()' method to support databinding's 'ObjectMapper.copy()' * Added method 'requiresCustomCodec()' for JsonFactory and JsonParser * Added 'JsonParser.getValueAsString()' method (to support flexible conversions) * Added META-INF/services/com.fasterxml.jackson.core.JsonFactory SPI to register `JsonFactory` for even more automatic format discovery in future. 2.0.4 (26-Jun-2012) Fixes: * [Issue-6] PrettyPrinter, count wrong for end-object case * 1.9.x fixes up to 1.9.8 2.0.3: skipped; only some modules use this version 2.0.2 (14-May-2012) * 1.9.x fixes up to 1.9.7 2.0.1 (22-Apr-2012) Fixes: * [JACKSON-827] Fix incompatibilities with JDK 1.5 (2.0.0 accidentally required 1.6) (reported Pascal G) 2.0.0 (25-Mar-2012) Fixes: (all fixes up until 1.9.6) Improvements * [JACKSON-730]: Add checks to ensure that Features are applicable for instances (parsers, generators), or if not, throw IllegalArgumentException * [JACKSON-742]: Add append-methods in SerializableString New features: * [JACKSON-782]: Add 'JsonParser.overrideCurrentName()', needed as a workaround for some exotic data binding cases (and/or formats) [entries for versions 1.x and earlier not retained; refer to earlier releases) jackson-core-jackson-core-2.10.1/src/000077500000000000000000000000001356164247300173275ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/000077500000000000000000000000001356164247300202535ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/000077500000000000000000000000001356164247300211745ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/000077500000000000000000000000001356164247300217525ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/000077500000000000000000000000001356164247300237575ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/000077500000000000000000000000001356164247300254075ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/000077500000000000000000000000001356164247300263375ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/Base64Variant.java000066400000000000000000000540401356164247300315560ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; import java.util.Arrays; import com.fasterxml.jackson.core.util.ByteArrayBuilder; /** * Class used to define specific details of which * variant of Base64 encoding/decoding is to be used. Although there is * somewhat standard basic version (so-called "MIME Base64"), other variants * exists, see Base64 Wikipedia entry for details. * * @author Tatu Saloranta */ public final class Base64Variant implements java.io.Serializable { private final static int INT_SPACE = 0x20; // We'll only serialize name private static final long serialVersionUID = 1L; /** * Placeholder used by "no padding" variant, to be used when a character * value is needed. */ final static char PADDING_CHAR_NONE = '\0'; /** * Marker used to denote ascii characters that do not correspond * to a 6-bit value (in this variant), and is not used as a padding * character. */ public final static int BASE64_VALUE_INVALID = -1; /** * Marker used to denote ascii character (in decoding table) that * is the padding character using this variant (if any). */ public final static int BASE64_VALUE_PADDING = -2; /* /********************************************************** /* Encoding/decoding tables /********************************************************** */ /** * Decoding table used for base 64 decoding. */ private final transient int[] _asciiToBase64 = new int[128]; /** * Encoding table used for base 64 decoding when output is done * as characters. */ private final transient char[] _base64ToAsciiC = new char[64]; /** * Alternative encoding table used for base 64 decoding when output is done * as ascii bytes. */ private final transient byte[] _base64ToAsciiB = new byte[64]; /* /********************************************************** /* Other configuration /********************************************************** */ /** * Symbolic name of variant; used for diagnostics/debugging. *

* Note that this is the only non-transient field; used when reading * back from serialized state. *

* Also: must not be private, accessed from `BaseVariants` */ final String _name; /** * Whether this variant uses padding or not. */ private final transient boolean _usesPadding; /** * Character used for padding, if any ({@link #PADDING_CHAR_NONE} if not). */ private final transient char _paddingChar; /** * Maximum number of encoded base64 characters to output during encoding * before adding a linefeed, if line length is to be limited * ({@link java.lang.Integer#MAX_VALUE} if not limited). *

* Note: for some output modes (when writing attributes) linefeeds may * need to be avoided, and this value ignored. */ private final transient int _maxLineLength; /* /********************************************************** /* Life-cycle /********************************************************** */ public Base64Variant(String name, String base64Alphabet, boolean usesPadding, char paddingChar, int maxLineLength) { _name = name; _usesPadding = usesPadding; _paddingChar = paddingChar; _maxLineLength = maxLineLength; // Ok and then we need to create codec tables. // First the main encoding table: int alphaLen = base64Alphabet.length(); if (alphaLen != 64) { throw new IllegalArgumentException("Base64Alphabet length must be exactly 64 (was "+alphaLen+")"); } // And then secondary encoding table and decoding table: base64Alphabet.getChars(0, alphaLen, _base64ToAsciiC, 0); Arrays.fill(_asciiToBase64, BASE64_VALUE_INVALID); for (int i = 0; i < alphaLen; ++i) { char alpha = _base64ToAsciiC[i]; _base64ToAsciiB[i] = (byte) alpha; _asciiToBase64[alpha] = i; } // Plus if we use padding, add that in too if (usesPadding) { _asciiToBase64[(int) paddingChar] = BASE64_VALUE_PADDING; } } /** * "Copy constructor" that can be used when the base alphabet is identical * to one used by another variant except for the maximum line length * (and obviously, name). */ public Base64Variant(Base64Variant base, String name, int maxLineLength) { this(base, name, base._usesPadding, base._paddingChar, maxLineLength); } /** * "Copy constructor" that can be used when the base alphabet is identical * to one used by another variant, but other details (padding, maximum * line length) differ */ public Base64Variant(Base64Variant base, String name, boolean usesPadding, char paddingChar, int maxLineLength) { _name = name; byte[] srcB = base._base64ToAsciiB; System.arraycopy(srcB, 0, this._base64ToAsciiB, 0, srcB.length); char[] srcC = base._base64ToAsciiC; System.arraycopy(srcC, 0, this._base64ToAsciiC, 0, srcC.length); int[] srcV = base._asciiToBase64; System.arraycopy(srcV, 0, this._asciiToBase64, 0, srcV.length); _usesPadding = usesPadding; _paddingChar = paddingChar; _maxLineLength = maxLineLength; } /* /********************************************************** /* Serializable overrides /********************************************************** */ /** * Method used to "demote" deserialized instances back to * canonical ones */ protected Object readResolve() { return Base64Variants.valueOf(_name); } /* /********************************************************** /* Public accessors /********************************************************** */ public String getName() { return _name; } public boolean usesPadding() { return _usesPadding; } public boolean usesPaddingChar(char c) { return c == _paddingChar; } public boolean usesPaddingChar(int ch) { return ch == (int) _paddingChar; } public char getPaddingChar() { return _paddingChar; } public byte getPaddingByte() { return (byte)_paddingChar; } public int getMaxLineLength() { return _maxLineLength; } /* /********************************************************** /* Decoding support /********************************************************** */ /** * @return 6-bit decoded value, if valid character; */ public int decodeBase64Char(char c) { int ch = (int) c; return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID; } public int decodeBase64Char(int ch) { return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID; } public int decodeBase64Byte(byte b) { int ch = (int) b; // note: cast retains sign, so it's from -128 to +127 if (ch < 0) { return BASE64_VALUE_INVALID; } return _asciiToBase64[ch]; } /* /********************************************************** /* Encoding support /********************************************************** */ public char encodeBase64BitsAsChar(int value) { /* Let's assume caller has done necessary checks; this * method must be fast and inlinable */ return _base64ToAsciiC[value]; } /** * Method that encodes given right-aligned (LSB) 24-bit value * into 4 base64 characters, stored in given result buffer. */ public int encodeBase64Chunk(int b24, char[] buffer, int ptr) { buffer[ptr++] = _base64ToAsciiC[(b24 >> 18) & 0x3F]; buffer[ptr++] = _base64ToAsciiC[(b24 >> 12) & 0x3F]; buffer[ptr++] = _base64ToAsciiC[(b24 >> 6) & 0x3F]; buffer[ptr++] = _base64ToAsciiC[b24 & 0x3F]; return ptr; } public void encodeBase64Chunk(StringBuilder sb, int b24) { sb.append(_base64ToAsciiC[(b24 >> 18) & 0x3F]); sb.append(_base64ToAsciiC[(b24 >> 12) & 0x3F]); sb.append(_base64ToAsciiC[(b24 >> 6) & 0x3F]); sb.append(_base64ToAsciiC[b24 & 0x3F]); } /** * Method that outputs partial chunk (which only encodes one * or two bytes of data). Data given is still aligned same as if * it as full data; that is, missing data is at the "right end" * (LSB) of int. * * @param outputBytes Number of encoded bytes included (either 1 or 2) */ public int encodeBase64Partial(int bits, int outputBytes, char[] buffer, int outPtr) { buffer[outPtr++] = _base64ToAsciiC[(bits >> 18) & 0x3F]; buffer[outPtr++] = _base64ToAsciiC[(bits >> 12) & 0x3F]; if (_usesPadding) { buffer[outPtr++] = (outputBytes == 2) ? _base64ToAsciiC[(bits >> 6) & 0x3F] : _paddingChar; buffer[outPtr++] = _paddingChar; } else { if (outputBytes == 2) { buffer[outPtr++] = _base64ToAsciiC[(bits >> 6) & 0x3F]; } } return outPtr; } public void encodeBase64Partial(StringBuilder sb, int bits, int outputBytes) { sb.append(_base64ToAsciiC[(bits >> 18) & 0x3F]); sb.append(_base64ToAsciiC[(bits >> 12) & 0x3F]); if (_usesPadding) { sb.append((outputBytes == 2) ? _base64ToAsciiC[(bits >> 6) & 0x3F] : _paddingChar); sb.append(_paddingChar); } else { if (outputBytes == 2) { sb.append(_base64ToAsciiC[(bits >> 6) & 0x3F]); } } } public byte encodeBase64BitsAsByte(int value) { // As with above, assuming it is 6-bit value return _base64ToAsciiB[value]; } /** * Method that encodes given right-aligned (LSB) 24-bit value * into 4 base64 bytes (ascii), stored in given result buffer. */ public int encodeBase64Chunk(int b24, byte[] buffer, int ptr) { buffer[ptr++] = _base64ToAsciiB[(b24 >> 18) & 0x3F]; buffer[ptr++] = _base64ToAsciiB[(b24 >> 12) & 0x3F]; buffer[ptr++] = _base64ToAsciiB[(b24 >> 6) & 0x3F]; buffer[ptr++] = _base64ToAsciiB[b24 & 0x3F]; return ptr; } /** * Method that outputs partial chunk (which only encodes one * or two bytes of data). Data given is still aligned same as if * it as full data; that is, missing data is at the "right end" * (LSB) of int. * * @param outputBytes Number of encoded bytes included (either 1 or 2) */ public int encodeBase64Partial(int bits, int outputBytes, byte[] buffer, int outPtr) { buffer[outPtr++] = _base64ToAsciiB[(bits >> 18) & 0x3F]; buffer[outPtr++] = _base64ToAsciiB[(bits >> 12) & 0x3F]; if (_usesPadding) { byte pb = (byte) _paddingChar; buffer[outPtr++] = (outputBytes == 2) ? _base64ToAsciiB[(bits >> 6) & 0x3F] : pb; buffer[outPtr++] = pb; } else { if (outputBytes == 2) { buffer[outPtr++] = _base64ToAsciiB[(bits >> 6) & 0x3F]; } } return outPtr; } /* /********************************************************** /* Convenience conversion methods for String to/from bytes /* use case. /********************************************************** */ /** * Convenience method for converting given byte array as base64 encoded * String using this variant's settings. * Resulting value is "raw", that is, not enclosed in double-quotes. * * @param input Byte array to encode */ public String encode(byte[] input) { return encode(input, false); } /** * Convenience method for converting given byte array as base64 encoded String * using this variant's settings, optionally enclosed in double-quotes. * Linefeeds added, if needed, are expressed as 2-character JSON (and Java source) * escape sequence of backslash + `n`. * * @param input Byte array to encode * @param addQuotes Whether to surround resulting value in double quotes or not */ public String encode(byte[] input, boolean addQuotes) { final int inputEnd = input.length; final StringBuilder sb = new StringBuilder(inputEnd + (inputEnd >> 2) + (inputEnd >> 3)); if (addQuotes) { sb.append('"'); } int chunksBeforeLF = getMaxLineLength() >> 2; // Ok, first we loop through all full triplets of data: int inputPtr = 0; int safeInputEnd = inputEnd-3; // to get only full triplets while (inputPtr <= safeInputEnd) { // First, mash 3 bytes into lsb of 32-bit int int b24 = ((int) input[inputPtr++]) << 8; b24 |= ((int) input[inputPtr++]) & 0xFF; b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF); encodeBase64Chunk(sb, b24); if (--chunksBeforeLF <= 0) { // note: must quote in JSON value, so not really useful... sb.append('\\'); sb.append('n'); chunksBeforeLF = getMaxLineLength() >> 2; } } // And then we may have 1 or 2 leftover bytes to encode int inputLeft = inputEnd - inputPtr; // 0, 1 or 2 if (inputLeft > 0) { // yes, but do we have room for output? int b24 = ((int) input[inputPtr++]) << 16; if (inputLeft == 2) { b24 |= (((int) input[inputPtr++]) & 0xFF) << 8; } encodeBase64Partial(sb, b24, inputLeft); } if (addQuotes) { sb.append('"'); } return sb.toString(); } /** * Convenience method for converting given byte array as base64 encoded String * using this variant's settings, optionally enclosed in double-quotes. * Linefeed character to use is passed explicitly. * * @param input Byte array to encode * @param addQuotes Whether to surround resulting value in double quotes or not * * @since 2.10 */ public String encode(byte[] input, boolean addQuotes, String linefeed) { final int inputEnd = input.length; final StringBuilder sb = new StringBuilder(inputEnd + (inputEnd >> 2) + (inputEnd >> 3)); if (addQuotes) { sb.append('"'); } int chunksBeforeLF = getMaxLineLength() >> 2; int inputPtr = 0; int safeInputEnd = inputEnd-3; while (inputPtr <= safeInputEnd) { int b24 = ((int) input[inputPtr++]) << 8; b24 |= ((int) input[inputPtr++]) & 0xFF; b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF); encodeBase64Chunk(sb, b24); if (--chunksBeforeLF <= 0) { sb.append(linefeed); chunksBeforeLF = getMaxLineLength() >> 2; } } int inputLeft = inputEnd - inputPtr; if (inputLeft > 0) { int b24 = ((int) input[inputPtr++]) << 16; if (inputLeft == 2) { b24 |= (((int) input[inputPtr++]) & 0xFF) << 8; } encodeBase64Partial(sb, b24, inputLeft); } if (addQuotes) { sb.append('"'); } return sb.toString(); } /** * Convenience method for decoding contents of a Base64-encoded String, * using this variant's settings. * * @param input * * @since 2.3 * * @throws IllegalArgumentException if input is not valid base64 encoded data */ @SuppressWarnings("resource") public byte[] decode(String input) throws IllegalArgumentException { ByteArrayBuilder b = new ByteArrayBuilder(); decode(input, b); return b.toByteArray(); } /** * Convenience method for decoding contents of a Base64-encoded String, * using this variant's settings * and appending decoded binary data using provided {@link ByteArrayBuilder}. *

* NOTE: builder will NOT be reset before decoding (nor cleared afterwards); * assumption is that caller will ensure it is given in proper state, and * used as appropriate afterwards. * * @since 2.3 * * @throws IllegalArgumentException if input is not valid base64 encoded data */ public void decode(String str, ByteArrayBuilder builder) throws IllegalArgumentException { int ptr = 0; int len = str.length(); main_loop: while (true) { // first, we'll skip preceding white space, if any char ch; do { if (ptr >= len) { break main_loop; } ch = str.charAt(ptr++); } while (ch <= INT_SPACE); int bits = decodeBase64Char(ch); if (bits < 0) { _reportInvalidBase64(ch, 0, null); } int decodedData = bits; // then second base64 char; can't get padding yet, nor ws if (ptr >= len) { _reportBase64EOF(); } ch = str.charAt(ptr++); bits = decodeBase64Char(ch); if (bits < 0) { _reportInvalidBase64(ch, 1, null); } decodedData = (decodedData << 6) | bits; // third base64 char; can be padding, but not ws if (ptr >= len) { // but as per [JACKSON-631] can be end-of-input, iff not using padding if (!usesPadding()) { decodedData >>= 4; builder.append(decodedData); break; } _reportBase64EOF(); } ch = str.charAt(ptr++); bits = decodeBase64Char(ch); // First branch: can get padding (-> 1 byte) if (bits < 0) { if (bits != Base64Variant.BASE64_VALUE_PADDING) { _reportInvalidBase64(ch, 2, null); } // Ok, must get padding if (ptr >= len) { _reportBase64EOF(); } ch = str.charAt(ptr++); if (!usesPaddingChar(ch)) { _reportInvalidBase64(ch, 3, "expected padding character '"+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 if (ptr >= len) { // but as per [JACKSON-631] can be end-of-input, iff not using padding if (!usesPadding()) { decodedData >>= 2; builder.appendTwoBytes(decodedData); break; } _reportBase64EOF(); } ch = str.charAt(ptr++); bits = decodeBase64Char(ch); if (bits < 0) { if (bits != Base64Variant.BASE64_VALUE_PADDING) { _reportInvalidBase64(ch, 3, null); } decodedData >>= 2; builder.appendTwoBytes(decodedData); } else { // otherwise, our triple is now complete decodedData = (decodedData << 6) | bits; builder.appendThreeBytes(decodedData); } } } /* /********************************************************** /* Overridden standard methods /********************************************************** */ @Override public String toString() { return _name; } @Override public boolean equals(Object o) { // identity comparison should be dine return (o == this); } @Override public int hashCode() { return _name.hashCode(); } /* /********************************************************** /* Internal helper methods /********************************************************** */ /** * @param bindex Relative index within base64 character unit; between 0 * and 3 (as unit has exactly 4 characters) */ protected void _reportInvalidBase64(char ch, int bindex, String msg) throws IllegalArgumentException { String base; if (ch <= INT_SPACE) { base = "Illegal white space character (code 0x"+Integer.toHexString(ch)+") as character #"+(bindex+1)+" of 4-char base64 unit: can only used between units"; } else if (usesPaddingChar(ch)) { base = "Unexpected padding character ('"+getPaddingChar()+"') as character #"+(bindex+1)+" of 4-char base64 unit: padding only legal as 3rd or 4th character"; } else if (!Character.isDefined(ch) || Character.isISOControl(ch)) { // Not sure if we can really get here... ? (most illegal xml chars are caught at lower level) base = "Illegal character (code 0x"+Integer.toHexString(ch)+") in base64 content"; } else { base = "Illegal character '"+ch+"' (code 0x"+Integer.toHexString(ch)+") in base64 content"; } if (msg != null) { base = base + ": " + msg; } throw new IllegalArgumentException(base); } protected void _reportBase64EOF() throws IllegalArgumentException { throw new IllegalArgumentException(missingPaddingMessage()); } /** * Helper method that will construct a message to use in exceptions for cases where input ends * prematurely in place where padding would be expected. * * @since 2.10 */ public String missingPaddingMessage() { return String.format("Unexpected end of base64-encoded String: base64 variant '%s' expects padding (one or more '%c' characters) at the end", getName(), getPaddingChar()); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/Base64Variants.java000066400000000000000000000077501356164247300317470ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; /** * Container for commonly used Base64 variants: *

* * @author Tatu Saloranta */ public final class Base64Variants { final static String STD_BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /** * This variant is what most people would think of "the standard" * Base64 encoding. *

* See wikipedia Base64 entry for details. *

* Note that although this can be thought of as the standard variant, * it is not the default for Jackson: no-linefeeds alternative * is because of JSON requirement of escaping all linefeeds. */ public final static Base64Variant MIME; static { MIME = new Base64Variant("MIME", STD_BASE64_ALPHABET, true, '=', 76); } /** * Slightly non-standard modification of {@link #MIME} which does not * use linefeeds (max line length set to infinite). Useful when linefeeds * wouldn't work well (possibly in attributes), or for minor space savings * (save 1 linefeed per 76 data chars, ie. ~1.4% savings). */ public final static Base64Variant MIME_NO_LINEFEEDS; static { MIME_NO_LINEFEEDS = new Base64Variant(MIME, "MIME-NO-LINEFEEDS", Integer.MAX_VALUE); } /** * This variant is the one that predates {@link #MIME}: it is otherwise * identical, except that it mandates shorter line length. */ public final static Base64Variant PEM = new Base64Variant(MIME, "PEM", true, '=', 64); /** * This non-standard variant is usually used when encoded data needs to be * passed via URLs (such as part of GET request). It differs from the * base {@link #MIME} variant in multiple ways. * First, no padding is used: this also means that it generally can not * be written in multiple separate but adjacent chunks (which would not * be the usual use case in any case). Also, no linefeeds are used (max * line length set to infinite). And finally, two characters (plus and * slash) that would need quoting in URLs are replaced with more * optimal alternatives (hyphen and underscore, respectively). */ public final static Base64Variant MODIFIED_FOR_URL; static { StringBuilder sb = new StringBuilder(STD_BASE64_ALPHABET); // Replace plus with hyphen, slash with underscore (and no padding) sb.setCharAt(sb.indexOf("+"), '-'); sb.setCharAt(sb.indexOf("/"), '_'); // And finally, let's not split lines either, wouldn't work too well with URLs MODIFIED_FOR_URL = new Base64Variant("MODIFIED-FOR-URL", sb.toString(), false, Base64Variant.PADDING_CHAR_NONE, Integer.MAX_VALUE); } /** * Method used to get the default variant ("MIME_NO_LINEFEEDS") for cases * where caller does not explicitly specify the variant. * We will prefer no-linefeed version because linefeeds in JSON values * must be escaped, making linefeed-containing variants sub-optimal. */ public static Base64Variant getDefaultVariant() { return MIME_NO_LINEFEEDS; } /** * @since 2.1 */ public static Base64Variant valueOf(String name) throws IllegalArgumentException { if (MIME._name.equals(name)) { return MIME; } if (MIME_NO_LINEFEEDS._name.equals(name)) { return MIME_NO_LINEFEEDS; } if (PEM._name.equals(name)) { return PEM; } if (MODIFIED_FOR_URL._name.equals(name)) { return MODIFIED_FOR_URL; } if (name == null) { name = ""; } else { name = "'"+name+"'"; } throw new IllegalArgumentException("No Base64Variant with name "+name); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/FormatFeature.java000066400000000000000000000020661356164247300317520ustar00rootroot00000000000000package com.fasterxml.jackson.core; /** * Marker interface that is to be implemented by data format - specific features. * Interface used since Java Enums can not extend classes or other Enums, but * they can implement interfaces; and as such we may be able to use limited * amount of generic functionality. *

* Note that this type is only implemented by non-JSON formats: * types {@link JsonParser.Feature} and {@link JsonGenerator.Feature} do NOT * implement it. This is to make it easier to avoid ambiguity with method * calls. * * @since 2.6 (to be fully used in 2.7 and beyond) */ public interface FormatFeature { /** * Accessor for checking whether this feature is enabled by default. */ public boolean enabledByDefault(); /** * Returns bit mask for this feature instance; must be a single bit, * that is of form (1 << N) */ public int getMask(); /** * Convenience method for checking whether feature is enabled in given bitmask */ public boolean enabledIn(int flags); } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/FormatSchema.java000066400000000000000000000025251356164247300315570ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; /** * Simple tag interface used to mark schema objects that are used by some * {@link JsonParser} and {@link JsonGenerator} implementations to further * specify structure of expected format. * Basic JSON-based parsers and generators do not use schemas, but some data * formats (like many binary data formats like Thrift, protobuf) mandate * use of schemas. *

* Since there is little commonality between schemas for different data formats, * this interface does not define much meaningful functionality for accessing * schema details; rather, specific parser and generator implementations need * to cast to schema implementations they use. This marker interface is mostly * used for tagging "some kind of schema" -- instead of passing opaque * {@link java.lang.Object} -- for documentation purposes. */ public interface FormatSchema { /** * Method that can be used to get an identifier that can be used for diagnostics * purposes, to indicate what kind of data format this schema is used for: typically * it is a short name of format itself, but it can also contain additional information * in cases where data format supports multiple types of schemas. */ String getSchemaType(); } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonEncoding.java000066400000000000000000000032621356164247300315650ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; /** * Enumeration that defines legal encodings that can be used * for JSON content, based on list of allowed encodings from * JSON specification. *

* Note: if application want to explicitly disregard Encoding * limitations (to read in JSON encoded using an encoding not * listed as allowed), they can use {@link java.io.Reader} / * {@link java.io.Writer} instances as input */ public enum JsonEncoding { UTF8("UTF-8", false, 8), // N/A for big-endian, really UTF16_BE("UTF-16BE", true, 16), UTF16_LE("UTF-16LE", false, 16), UTF32_BE("UTF-32BE", true, 32), UTF32_LE("UTF-32LE", false, 32) ; private final String _javaName; private final boolean _bigEndian; private final int _bits; JsonEncoding(String javaName, boolean bigEndian, int bits) { _javaName = javaName; _bigEndian = bigEndian; _bits = bits; } /** * Method for accessing encoding name that JDK will support. * * @return Matching encoding name that JDK will support. */ public String getJavaName() { return _javaName; } /** * Whether encoding is big-endian (if encoding supports such * notion). If no such distinction is made (as is the case for * {@link #UTF8}), return value is undefined. * * @return True for big-endian encodings; false for little-endian * (or if not applicable) */ public boolean isBigEndian() { return _bigEndian; } public int bits() { return _bits; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonFactory.java000066400000000000000000001737471356164247300314660ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; import java.io.*; import java.lang.ref.SoftReference; import java.net.URL; import com.fasterxml.jackson.core.format.InputAccessor; import com.fasterxml.jackson.core.format.MatchStrength; import com.fasterxml.jackson.core.io.*; import com.fasterxml.jackson.core.json.*; import com.fasterxml.jackson.core.json.async.NonBlockingJsonParser; import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer; import com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer; import com.fasterxml.jackson.core.util.BufferRecycler; import com.fasterxml.jackson.core.util.BufferRecyclers; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; /** * The main factory class of Jackson package, used to configure and * construct reader (aka parser, {@link JsonParser}) * and writer (aka generator, {@link JsonGenerator}) * instances. *

* Factory instances are thread-safe and reusable after configuration * (if any). Typically applications and services use only a single * globally shared factory instance, unless they need differently * configured factories. Factory reuse is important if efficiency matters; * most recycling of expensive construct is done on per-factory basis. *

* Creation of a factory instance is a light-weight operation, * and since there is no need for pluggable alternative implementations * (as there is no "standard" JSON processor API to implement), * the default constructor is used for constructing factory * instances. * * @author Tatu Saloranta */ @SuppressWarnings("resource") public class JsonFactory extends TokenStreamFactory implements Versioned, java.io.Serializable // since 2.1 (for Android, mostly) { private static final long serialVersionUID = 2; /* /********************************************************** /* Helper types /********************************************************** */ /** * Enumeration that defines all on/off features that can only be * changed for {@link JsonFactory}. */ public enum Feature { // // // Symbol handling (interning etc) /** * Feature that determines whether JSON object field names are * to be canonicalized using {@link String#intern} or not: * if enabled, all field names will be intern()ed (and caller * can count on this being true for all such names); if disabled, * no intern()ing is done. There may still be basic * canonicalization (that is, same String will be used to represent * all identical object property names for a single document). *

* Note: this setting only has effect if * {@link #CANONICALIZE_FIELD_NAMES} is true -- otherwise no * canonicalization of any sort is done. *

* This setting is enabled by default. */ INTERN_FIELD_NAMES(true), /** * Feature that determines whether JSON object field names are * to be canonicalized (details of how canonicalization is done * then further specified by * {@link #INTERN_FIELD_NAMES}). *

* This setting is enabled by default. */ CANONICALIZE_FIELD_NAMES(true), /** * Feature that determines what happens if we encounter a case in symbol * handling where number of hash collisions exceeds a safety threshold * -- which almost certainly means a denial-of-service attack via generated * duplicate hash codes. * If feature is enabled, an {@link IllegalStateException} is * thrown to indicate the suspected denial-of-service attack; if disabled, processing continues but * canonicalization (and thereby intern()ing) is disabled) as protective * measure. *

* This setting is enabled by default. * * @since 2.4 */ FAIL_ON_SYMBOL_HASH_OVERFLOW(true), /** * Feature that determines whether we will use {@link BufferRecycler} with * {@link ThreadLocal} and {@link SoftReference}, for efficient reuse of * underlying input/output buffers. * This usually makes sense on normal J2SE/J2EE server-side processing; * but may not make sense on platforms where {@link SoftReference} handling * is broken (like Android), or if there are retention issues due to * {@link ThreadLocal} (see * Issue #189 * for a possible case) *

* This setting is enabled by default. * * @since 2.6 */ USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING(true) ; /** * Whether feature is enabled or disabled by default. */ private final boolean _defaultState; /** * Method that calculates bit set (flags) of all features that * are enabled by default. */ public static int collectDefaults() { int flags = 0; for (Feature f : values()) { if (f.enabledByDefault()) { flags |= f.getMask(); } } return flags; } private Feature(boolean defaultState) { _defaultState = defaultState; } public boolean enabledByDefault() { return _defaultState; } public boolean enabledIn(int flags) { return (flags & getMask()) != 0; } public int getMask() { return (1 << ordinal()); } } /* /********************************************************** /* Constants /********************************************************** */ /** * Name used to identify JSON format * (and returned by {@link #getFormatName()} */ public final static String FORMAT_NAME_JSON = "JSON"; /** * Bitfield (set of flags) of all factory features that are enabled by default. */ protected final static int DEFAULT_FACTORY_FEATURE_FLAGS = JsonFactory.Feature.collectDefaults(); /** * Bitfield (set of flags) of all parser features that are enabled * by default. */ protected final static int DEFAULT_PARSER_FEATURE_FLAGS = JsonParser.Feature.collectDefaults(); /** * Bitfield (set of flags) of all generator features that are enabled * by default. */ protected final static int DEFAULT_GENERATOR_FEATURE_FLAGS = JsonGenerator.Feature.collectDefaults(); public final static SerializableString DEFAULT_ROOT_VALUE_SEPARATOR = DefaultPrettyPrinter.DEFAULT_ROOT_VALUE_SEPARATOR; /** * @since 2.10 */ public final static char DEFAULT_QUOTE_CHAR = '"'; /* /********************************************************** /* Buffer, symbol table management /********************************************************** */ /** * Each factory comes equipped with a shared root symbol table. * It should not be linked back to the original blueprint, to * avoid contents from leaking between factories. */ protected final transient CharsToNameCanonicalizer _rootCharSymbols = CharsToNameCanonicalizer.createRoot(); /** * Alternative to the basic symbol table, some stream-based * parsers use different name canonicalization method. *

* TODO: should clean up this; looks messy having 2 alternatives * with not very clear differences. * * @since 2.6 */ protected final transient ByteQuadsCanonicalizer _byteSymbolCanonicalizer = ByteQuadsCanonicalizer.createRoot(); /* /********************************************************** /* Configuration, simple feature flags /********************************************************** */ /** * Currently enabled factory features. */ protected int _factoryFeatures = DEFAULT_FACTORY_FEATURE_FLAGS; /** * Currently enabled parser features. */ protected int _parserFeatures = DEFAULT_PARSER_FEATURE_FLAGS; /** * Currently enabled generator features. */ protected int _generatorFeatures = DEFAULT_GENERATOR_FEATURE_FLAGS; /* /********************************************************** /* Configuration, helper objects /********************************************************** */ /** * Object that implements conversion functionality between * Java objects and JSON content. For base JsonFactory implementation * usually not set by default, but can be explicitly set. * Sub-classes (like @link org.codehaus.jackson.map.MappingJsonFactory} * usually provide an implementation. */ protected ObjectCodec _objectCodec; /** * Definition of custom character escapes to use for generators created * by this factory, if any. If null, standard data format specific * escapes are used. */ protected CharacterEscapes _characterEscapes; /** * Optional helper object that may decorate input sources, to do * additional processing on input during parsing. */ protected InputDecorator _inputDecorator; /** * Optional helper object that may decorate output object, to do * additional processing on output during content generation. */ protected OutputDecorator _outputDecorator; /** * Separator used between root-level values, if any; null indicates * "do not add separator". * Default separator is a single space character. * * @since 2.1 */ protected SerializableString _rootValueSeparator = DEFAULT_ROOT_VALUE_SEPARATOR; /** * Optional threshold used for automatically escaping character above certain character * code value: either {@code 0} to indicate that no threshold is specified, or value * at or above 127 to indicate last character code that is NOT automatically escaped * (but depends on other configuration rules for checking). * * @since 2.10 */ protected int _maximumNonEscapedChar; /** * Character used for quoting field names (if field name quoting has not * been disabled with {@link JsonWriteFeature#QUOTE_FIELD_NAMES}) * and JSON String values. */ protected final char _quoteChar; /* /********************************************************** /* Construction /********************************************************** */ /** * Default constructor used to create factory instances. * Creation of a factory instance is a light-weight operation, * but it is still a good idea to reuse limited number of * factory instances (and quite often just a single instance): * factories are used as context for storing some reused * processing objects (such as symbol tables parsers use) * and this reuse only works within context of a single * factory instance. */ public JsonFactory() { this((ObjectCodec) null); } public JsonFactory(ObjectCodec oc) { _objectCodec = oc; _quoteChar = DEFAULT_QUOTE_CHAR; } /** * Constructor used when copy()ing a factory instance. * * @since 2.2.1 */ protected JsonFactory(JsonFactory src, ObjectCodec codec) { _objectCodec = codec; // General _factoryFeatures = src._factoryFeatures; _parserFeatures = src._parserFeatures; _generatorFeatures = src._generatorFeatures; _inputDecorator = src._inputDecorator; _outputDecorator = src._outputDecorator; // JSON-specific _characterEscapes = src._characterEscapes; _rootValueSeparator = src._rootValueSeparator; _maximumNonEscapedChar = src._maximumNonEscapedChar; _quoteChar = src._quoteChar; } /** * Constructor used by {@link JsonFactoryBuilder} for instantiation. * * @since 2.10 */ public JsonFactory(JsonFactoryBuilder b) { _objectCodec = null; // General _factoryFeatures = b._factoryFeatures; _parserFeatures = b._streamReadFeatures; _generatorFeatures = b._streamWriteFeatures; _inputDecorator = b._inputDecorator; _outputDecorator = b._outputDecorator; // JSON-specific _characterEscapes = b._characterEscapes; _rootValueSeparator = b._rootValueSeparator; _maximumNonEscapedChar = b._maximumNonEscapedChar; _quoteChar = b._quoteChar; } /** * Constructor for subtypes; needed to work around the fact that before 3.0, * this factory has cumbersome dual role as generic type as well as actual * implementation for json. * * @param b Builder that contains information * @param bogus Argument only needed to separate constructor signature; ignored */ protected JsonFactory(TSFBuilder b, boolean bogus) { _objectCodec = null; _factoryFeatures = b._factoryFeatures; _parserFeatures = b._streamReadFeatures; _generatorFeatures = b._streamWriteFeatures; _inputDecorator = b._inputDecorator; _outputDecorator = b._outputDecorator; // JSON-specific: need to assign even if not really used _characterEscapes = null; _rootValueSeparator = null; _maximumNonEscapedChar = 0; _quoteChar = DEFAULT_QUOTE_CHAR; } /** * Method that allows construction of differently configured factory, starting * with settings of this factory. * * @since 2.10 */ public TSFBuilder rebuild() { // 13-Jun-2018, tatu: Verify sub-classing to prevent strange bugs in format impls _requireJSONFactory("Factory implementation for format (%s) MUST override `rebuild()` method"); return new JsonFactoryBuilder(this); } /** * Main factory method to use for constructing {@link JsonFactory} instances with * different configuration: creates and returns a builder for collecting configuration * settings; instance created by calling {@code build()} after all configuration * set. *

* NOTE: signature unfortunately does not expose true implementation type; this * will be fixed in 3.0. */ public static TSFBuilder builder() { return new JsonFactoryBuilder(); } /** * Method for constructing a new {@link JsonFactory} that has * the same settings as this instance, but is otherwise * independent (i.e. nothing is actually shared, symbol tables * are separate). * Note that {@link ObjectCodec} reference is not copied but is * set to null; caller typically needs to set it after calling * this method. Reason for this is that the codec is used for * callbacks, and assumption is that there is strict 1-to-1 * mapping between codec, factory. Caller has to, then, explicitly * set codec after making the copy. * * @since 2.1 */ public JsonFactory copy() { _checkInvalidCopy(JsonFactory.class); // as per above, do clear ObjectCodec return new JsonFactory(this, null); } /** * @since 2.1 */ protected void _checkInvalidCopy(Class exp) { if (getClass() != exp) { throw new IllegalStateException("Failed copy(): "+getClass().getName() +" (version: "+version()+") does not override copy(); it has to"); } } /* /********************************************************** /* Serializable overrides /********************************************************** */ /** * Method that we need to override to actually make restoration go * through constructors etc. * Also: must be overridden by sub-classes as well. */ protected Object readResolve() { return new JsonFactory(this, _objectCodec); } /* /********************************************************** /* Capability introspection /********************************************************** */ /** * Introspection method that higher-level functionality may call * to see whether underlying data format requires a stable ordering * of object properties or not. * This is usually used for determining * whether to force a stable ordering (like alphabetic ordering by name) * if no ordering if explicitly specified. *

* Default implementation returns false as JSON does NOT * require stable ordering. Formats that require ordering include positional * textual formats like CSV, and schema-based binary formats * like Avro. * * @since 2.3 */ @Override public boolean requiresPropertyOrdering() { return false; } /** * Introspection method that higher-level functionality may call * to see whether underlying data format can read and write binary * data natively; that is, embeded it as-is without using encodings * such as Base64. *

* Default implementation returns false as JSON does not * support native access: all binary content must use Base64 encoding. * Most binary formats (like Smile and Avro) support native binary content. * * @since 2.3 */ @Override public boolean canHandleBinaryNatively() { return false; } /** * Introspection method that can be used by base factory to check * whether access using char[] is something that actual * parser implementations can take advantage of, over having to * use {@link java.io.Reader}. Sub-types are expected to override * definition; default implementation (suitable for JSON) alleges * that optimization are possible; and thereby is likely to try * to access {@link java.lang.String} content by first copying it into * recyclable intermediate buffer. * * @since 2.4 */ public boolean canUseCharArrays() { return true; } /** * Introspection method that can be used to check whether this * factory can create non-blocking parsers: parsers that do not * use blocking I/O abstractions but instead use a * {@link com.fasterxml.jackson.core.async.NonBlockingInputFeeder}. * * @since 2.9 */ @Override public boolean canParseAsync() { // 31-May-2017, tatu: Jackson 2.9 does support async parsing for JSON, // but not all other formats, so need to do this: return _isJSONFactory(); } @Override public Class getFormatReadFeatureType() { return null; } @Override public Class getFormatWriteFeatureType() { return null; } /* /********************************************************** /* Format detection functionality /********************************************************** */ /** * Method that can be used to quickly check whether given schema * is something that parsers and/or generators constructed by this * factory could use. Note that this means possible use, at the level * of data format (i.e. schema is for same data format as parsers and * generators this factory constructs); individual schema instances * may have further usage restrictions. * * @since 2.1 */ @Override public boolean canUseSchema(FormatSchema schema) { if (schema == null){ return false; } String ourFormat = getFormatName(); return (ourFormat != null) && ourFormat.equals(schema.getSchemaType()); } /** * Method that returns short textual id identifying format * this factory supports. *

* Note: sub-classes should override this method; default * implementation will return null for all sub-classes */ @Override public String getFormatName() { /* Somewhat nasty check: since we can't make this abstract * (due to backwards compatibility concerns), need to prevent * format name "leakage" */ if (getClass() == JsonFactory.class) { return FORMAT_NAME_JSON; } return null; } /** * Convenience method for trying to determine whether input via given accessor * is of format type supported by this factory. */ public MatchStrength hasFormat(InputAccessor acc) throws IOException { // since we can't keep this abstract, only implement for "vanilla" instance if (getClass() == JsonFactory.class) { return hasJSONFormat(acc); } return null; } /** * Method that can be called to determine if a custom * {@link ObjectCodec} is needed for binding data parsed * using {@link JsonParser} constructed by this factory * (which typically also implies the same for serialization * with {@link JsonGenerator}). * * @return True if custom codec is needed with parsers and * generators created by this factory; false if a general * {@link ObjectCodec} is enough * * @since 2.1 */ public boolean requiresCustomCodec() { return false; } /** * Helper method that can be called to determine if content accessed * using given accessor seems to be JSON content. */ protected MatchStrength hasJSONFormat(InputAccessor acc) throws IOException { return ByteSourceJsonBootstrapper.hasJSONFormat(acc); } /* /********************************************************** /* Versioned /********************************************************** */ @Override public Version version() { return PackageVersion.VERSION; } /* /********************************************************** /* Configuration, factory features /********************************************************** */ /** * Method for enabling or disabling specified parser feature * (check {@link JsonParser.Feature} for list of features) * * @deprecated since 2.10 use {@link JsonFactoryBuilder#configure(JsonFactory.Feature, boolean)} instead */ @Deprecated public final JsonFactory configure(JsonFactory.Feature f, boolean state) { return state ? enable(f) : disable(f); } /** * Method for enabling specified parser feature * (check {@link JsonFactory.Feature} for list of features) * * @deprecated since 2.10 use {@link JsonFactoryBuilder#configure(JsonFactory.Feature, boolean)} instead */ @Deprecated public JsonFactory enable(JsonFactory.Feature f) { _factoryFeatures |= f.getMask(); return this; } /** * Method for disabling specified parser features * (check {@link JsonFactory.Feature} for list of features) * * @deprecated since 2.10 use {@link JsonFactoryBuilder#configure(JsonFactory.Feature, boolean)} instead */ @Deprecated public JsonFactory disable(JsonFactory.Feature f) { _factoryFeatures &= ~f.getMask(); return this; } /** * Checked whether specified parser feature is enabled. */ public final boolean isEnabled(JsonFactory.Feature f) { return (_factoryFeatures & f.getMask()) != 0; } @Override public final int getParserFeatures() { return _parserFeatures; } @Override public final int getGeneratorFeatures() { return _generatorFeatures; } // MUST be overridden by sub-classes that support format-specific parser features @Override public int getFormatParserFeatures() { return 0; } // MUST be overridden by sub-classes that support format-specific generator features @Override public int getFormatGeneratorFeatures() { return 0; } /* /********************************************************** /* Configuration, parser configuration /********************************************************** */ /** * Method for enabling or disabling specified parser feature * (check {@link JsonParser.Feature} for list of features) */ public final JsonFactory configure(JsonParser.Feature f, boolean state) { return state ? enable(f) : disable(f); } /** * Method for enabling specified parser feature * (check {@link JsonParser.Feature} for list of features) */ public JsonFactory enable(JsonParser.Feature f) { _parserFeatures |= f.getMask(); return this; } /** * Method for disabling specified parser features * (check {@link JsonParser.Feature} for list of features) */ public JsonFactory disable(JsonParser.Feature f) { _parserFeatures &= ~f.getMask(); return this; } /** * Checked whether specified parser feature is enabled. */ @Override public final boolean isEnabled(JsonParser.Feature f) { return (_parserFeatures & f.getMask()) != 0; } /** * @since 2.10 */ public final boolean isEnabled(StreamReadFeature f) { return (_parserFeatures & f.mappedFeature().getMask()) != 0; } /** * Method for getting currently configured input decorator (if any; * there is no default decorator). */ public InputDecorator getInputDecorator() { return _inputDecorator; } /** * Method for overriding currently configured input decorator * * @deprecated Since 2.10 use {@link JsonFactoryBuilder#inputDecorator(InputDecorator)} instead */ @Deprecated public JsonFactory setInputDecorator(InputDecorator d) { _inputDecorator = d; return this; } /* /********************************************************** /* Configuration, generator settings /********************************************************** */ /** * Method for enabling or disabling specified generator feature * (check {@link JsonGenerator.Feature} for list of features) */ public final JsonFactory configure(JsonGenerator.Feature f, boolean state) { return state ? enable(f) : disable(f); } /** * Method for enabling specified generator features * (check {@link JsonGenerator.Feature} for list of features) */ public JsonFactory enable(JsonGenerator.Feature f) { _generatorFeatures |= f.getMask(); return this; } /** * Method for disabling specified generator feature * (check {@link JsonGenerator.Feature} for list of features) */ public JsonFactory disable(JsonGenerator.Feature f) { _generatorFeatures &= ~f.getMask(); return this; } /** * Check whether specified generator feature is enabled. */ @Override public final boolean isEnabled(JsonGenerator.Feature f) { return (_generatorFeatures & f.getMask()) != 0; } /** * @since 2.10 */ public final boolean isEnabled(StreamWriteFeature f) { return (_generatorFeatures & f.mappedFeature().getMask()) != 0; } /** * Method for accessing custom escapes factory uses for {@link JsonGenerator}s * it creates. */ public CharacterEscapes getCharacterEscapes() { return _characterEscapes; } /** * Method for defining custom escapes factory uses for {@link JsonGenerator}s * it creates. */ public JsonFactory setCharacterEscapes(CharacterEscapes esc) { _characterEscapes = esc; return this; } /** * Method for getting currently configured output decorator (if any; * there is no default decorator). */ public OutputDecorator getOutputDecorator() { return _outputDecorator; } /** * Method for overriding currently configured output decorator * * @deprecated Since 2.10 use {@link JsonFactoryBuilder#inputDecorator(InputDecorator)} instead */ @Deprecated public JsonFactory setOutputDecorator(OutputDecorator d) { _outputDecorator = d; return this; } /** * Method that allows overriding String used for separating root-level * JSON values (default is single space character) * * @param sep Separator to use, if any; null means that no separator is * automatically added * * @since 2.1 */ public JsonFactory setRootValueSeparator(String sep) { _rootValueSeparator = (sep == null) ? null : new SerializedString(sep); return this; } /** * @since 2.1 */ public String getRootValueSeparator() { return (_rootValueSeparator == null) ? null : _rootValueSeparator.getValue(); } /* /********************************************************** /* Configuration, other /********************************************************** */ /** * Method for associating a {@link ObjectCodec} (typically * a com.fasterxml.jackson.databind.ObjectMapper) * with this factory (and more importantly, parsers and generators * it constructs). This is needed to use data-binding methods * of {@link JsonParser} and {@link JsonGenerator} instances. */ public JsonFactory setCodec(ObjectCodec oc) { _objectCodec = oc; return this; } public ObjectCodec getCodec() { return _objectCodec; } /* /********************************************************** /* Parser factories, traditional (blocking) I/O sources /********************************************************** */ /** * Method for constructing JSON parser instance to parse * contents of specified file. * *

* Encoding is auto-detected from contents according to JSON * specification recommended mechanism. Json specification * supports only UTF-8, UTF-16 and UTF-32 as valid encodings, * so auto-detection implemented only for this charsets. * For other charsets use {@link #createParser(java.io.Reader)}. * *

* Underlying input stream (needed for reading contents) * will be owned (and managed, i.e. closed as need be) by * the parser, since caller has no access to it. * * @param f File that contains JSON content to parse * * @since 2.1 */ @Override public JsonParser createParser(File f) throws IOException, JsonParseException { // true, since we create InputStream from File IOContext ctxt = _createContext(f, true); InputStream in = new FileInputStream(f); return _createParser(_decorate(in, ctxt), ctxt); } /** * Method for constructing JSON parser instance to parse * contents of resource reference by given URL. * *

* Encoding is auto-detected from contents according to JSON * specification recommended mechanism. Json specification * supports only UTF-8, UTF-16 and UTF-32 as valid encodings, * so auto-detection implemented only for this charsets. * For other charsets use {@link #createParser(java.io.Reader)}. * *

* Underlying input stream (needed for reading contents) * will be owned (and managed, i.e. closed as need be) by * the parser, since caller has no access to it. * * @param url URL pointing to resource that contains JSON content to parse * * @since 2.1 */ @Override public JsonParser createParser(URL url) throws IOException, JsonParseException { // true, since we create InputStream from URL IOContext ctxt = _createContext(url, true); InputStream in = _optimizedStreamFromURL(url); return _createParser(_decorate(in, ctxt), ctxt); } /** * Method for constructing JSON parser instance to parse * the contents accessed via specified input stream. *

* The input stream will not be owned by * the parser, it will still be managed (i.e. closed if * end-of-stream is reacher, or parser close method called) * if (and only if) {@link com.fasterxml.jackson.core.StreamReadFeature#AUTO_CLOSE_SOURCE} * is enabled. *

* * Note: no encoding argument is taken since it can always be * auto-detected as suggested by JSON RFC. Json specification * supports only UTF-8, UTF-16 and UTF-32 as valid encodings, * so auto-detection implemented only for this charsets. * For other charsets use {@link #createParser(java.io.Reader)}. * * @param in InputStream to use for reading JSON content to parse * * @since 2.1 */ @Override public JsonParser createParser(InputStream in) throws IOException, JsonParseException { IOContext ctxt = _createContext(in, false); return _createParser(_decorate(in, ctxt), ctxt); } /** * Method for constructing parser for parsing * the contents accessed via specified Reader.

* The read stream will not be owned by * the parser, it will still be managed (i.e. closed if * end-of-stream is reacher, or parser close method called) * if (and only if) {@link com.fasterxml.jackson.core.StreamReadFeature#AUTO_CLOSE_SOURCE} * is enabled. * * @param r Reader to use for reading JSON content to parse * * @since 2.1 */ @Override public JsonParser createParser(Reader r) throws IOException, JsonParseException { // false -> we do NOT own Reader (did not create it) IOContext ctxt = _createContext(r, false); return _createParser(_decorate(r, ctxt), ctxt); } /** * Method for constructing parser for parsing * the contents of given byte array. * * @since 2.1 */ @Override public JsonParser createParser(byte[] data) throws IOException, JsonParseException { IOContext ctxt = _createContext(data, true); if (_inputDecorator != null) { InputStream in = _inputDecorator.decorate(ctxt, data, 0, data.length); if (in != null) { return _createParser(in, ctxt); } } return _createParser(data, 0, data.length, ctxt); } /** * Method for constructing parser for parsing * the contents of given byte array. * * @param data Buffer that contains data to parse * @param offset Offset of the first data byte within buffer * @param len Length of contents to parse within buffer * * @since 2.1 */ @Override public JsonParser createParser(byte[] data, int offset, int len) throws IOException, JsonParseException { IOContext ctxt = _createContext(data, true); // [JACKSON-512]: allow wrapping with InputDecorator if (_inputDecorator != null) { InputStream in = _inputDecorator.decorate(ctxt, data, offset, len); if (in != null) { return _createParser(in, ctxt); } } return _createParser(data, offset, len, ctxt); } /** * Method for constructing parser for parsing * contents of given String. * * @since 2.1 */ @Override 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()) { // 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)); } IOContext ctxt = _createContext(content, true); char[] buf = ctxt.allocTokenBuffer(strLen); content.getChars(0, strLen, buf, 0); return _createParser(buf, 0, strLen, ctxt, true); } /** * Method for constructing parser for parsing * contents of given char array. * * @since 2.4 */ @Override public JsonParser createParser(char[] content) throws IOException { return createParser(content, 0, content.length); } /** * Method for constructing parser for parsing contents of given char array. * * @since 2.4 */ @Override public JsonParser createParser(char[] content, int offset, int len) throws IOException { if (_inputDecorator != null) { // easier to just wrap in a Reader than extend InputDecorator return createParser(new CharArrayReader(content, offset, len)); } return _createParser(content, offset, len, _createContext(content, true), // important: buffer is NOT recyclable, as it's from caller false); } /** * Optional method for constructing parser for reading contents from specified {@link DataInput} * instance. *

* If this factory does not support {@link DataInput} as source, * will throw {@link UnsupportedOperationException} * * @since 2.8 */ @Override public JsonParser createParser(DataInput in) throws IOException { IOContext ctxt = _createContext(in, false); return _createParser(_decorate(in, ctxt), ctxt); } /* /********************************************************** /* Parser factories, non-blocking (async) sources /********************************************************** */ /** * Optional method for constructing parser for non-blocking parsing * via {@link com.fasterxml.jackson.core.async.ByteArrayFeeder} * interface (accessed using {@link JsonParser#getNonBlockingInputFeeder()} * from constructed instance). *

* If this factory does not support non-blocking parsing (either at all, * or from byte array), * will throw {@link UnsupportedOperationException} * * @since 2.9 */ @Override public JsonParser createNonBlockingByteArrayParser() throws IOException { // 17-May-2017, tatu: Need to take care not to accidentally create JSON parser // for non-JSON input: _requireJSONFactory("Non-blocking source not (yet?) supported for this format (%s)"); IOContext ctxt = _createNonBlockingContext(null); ByteQuadsCanonicalizer can = _byteSymbolCanonicalizer.makeChild(_factoryFeatures); return new NonBlockingJsonParser(ctxt, _parserFeatures, can); } /* /********************************************************** /* Generator factories /********************************************************** */ /** * Method for constructing JSON generator for writing JSON content * using specified output stream. * Encoding to use must be specified, and needs to be one of available * types (as per JSON specification). *

* Underlying stream is NOT owned by the generator constructed, * so that generator will NOT close the output stream when * {@link JsonGenerator#close} is called (unless auto-closing * feature, * {@link com.fasterxml.jackson.core.JsonGenerator.Feature#AUTO_CLOSE_TARGET} * is enabled). * Using application needs to close it explicitly if this is the case. *

* Note: there are formats that use fixed encoding (like most binary data formats) * and that ignore passed in encoding. * * @param out OutputStream to use for writing JSON content * @param enc Character encoding to use * * @since 2.1 */ @Override public JsonGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException { // false -> we won't manage the stream unless explicitly directed to IOContext ctxt = _createContext(out, false); ctxt.setEncoding(enc); if (enc == JsonEncoding.UTF8) { return _createUTF8Generator(_decorate(out, ctxt), ctxt); } Writer w = _createWriter(out, enc, ctxt); return _createGenerator(_decorate(w, ctxt), ctxt); } /** * Convenience method for constructing generator that uses default * encoding of the format (UTF-8 for JSON and most other data formats). *

* Note: there are formats that use fixed encoding (like most binary data formats). * * @since 2.1 */ @Override public JsonGenerator createGenerator(OutputStream out) throws IOException { return createGenerator(out, JsonEncoding.UTF8); } /** * Method for constructing JSON generator for writing JSON content * using specified Writer. *

* Underlying stream is NOT owned by the generator constructed, * so that generator will NOT close the Reader when * {@link JsonGenerator#close} is called (unless auto-closing * feature, * {@link com.fasterxml.jackson.core.JsonGenerator.Feature#AUTO_CLOSE_TARGET} is enabled). * Using application needs to close it explicitly. * * @since 2.1 * * @param w Writer to use for writing JSON content */ @Override public JsonGenerator createGenerator(Writer w) throws IOException { IOContext ctxt = _createContext(w, false); return _createGenerator(_decorate(w, ctxt), ctxt); } /** * Method for constructing JSON generator for writing JSON content * to specified file, overwriting contents it might have (or creating * it if such file does not yet exist). * Encoding to use must be specified, and needs to be one of available * types (as per JSON specification). *

* Underlying stream is owned by the generator constructed, * i.e. generator will handle closing of file when * {@link JsonGenerator#close} is called. * * @param f File to write contents to * @param enc Character encoding to use * * @since 2.1 */ @Override public JsonGenerator createGenerator(File f, JsonEncoding enc) throws IOException { OutputStream out = new FileOutputStream(f); // true -> yes, we have to manage the stream since we created it IOContext ctxt = _createContext(out, true); ctxt.setEncoding(enc); if (enc == JsonEncoding.UTF8) { return _createUTF8Generator(_decorate(out, ctxt), ctxt); } Writer w = _createWriter(out, enc, ctxt); return _createGenerator(_decorate(w, ctxt), ctxt); } /** * Method for constructing generator for writing content using specified * {@link DataOutput} instance. * * @since 2.8 */ @Override 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). *

* Note: there are formats that use fixed encoding (like most binary data formats). * * @since 2.8 */ @Override public JsonGenerator createGenerator(DataOutput out) throws IOException { return createGenerator(_createDataOutputWrapper(out), JsonEncoding.UTF8); } /* /********************************************************** /* Deprecated parser factory methods: to be removed from 3.x /********************************************************** */ /** * Method for constructing JSON parser instance to parse * contents of specified file. *

* Encoding is auto-detected from contents according to JSON * specification recommended mechanism. Json specification * supports only UTF-8, UTF-16 and UTF-32 as valid encodings, * so auto-detection implemented only for this charsets. * For other charsets use {@link #createParser(java.io.Reader)}. * *

* Underlying input stream (needed for reading contents) * will be owned (and managed, i.e. closed as need be) by * the parser, since caller has no access to it. * * @param f File that contains JSON content to parse * * @deprecated Since 2.2, use {@link #createParser(File)} instead. */ @Deprecated public JsonParser createJsonParser(File f) throws IOException, JsonParseException { return createParser(f); } /** * Method for constructing JSON parser instance to parse * contents of resource reference by given URL. * *

* Encoding is auto-detected from contents according to JSON * specification recommended mechanism. Json specification * supports only UTF-8, UTF-16 and UTF-32 as valid encodings, * so auto-detection implemented only for this charsets. * For other charsets use {@link #createParser(java.io.Reader)}. * *

* Underlying input stream (needed for reading contents) * will be owned (and managed, i.e. closed as need be) by * the parser, since caller has no access to it. * * @param url URL pointing to resource that contains JSON content to parse * * @deprecated Since 2.2, use {@link #createParser(URL)} instead. */ @Deprecated public JsonParser createJsonParser(URL url) throws IOException, JsonParseException { return createParser(url); } /** * Method for constructing JSON parser instance to parse * the contents accessed via specified input stream. *

* The input stream will not be owned by * the parser, it will still be managed (i.e. closed if * end-of-stream is reacher, or parser close method called) * if (and only if) {@link com.fasterxml.jackson.core.JsonParser.Feature#AUTO_CLOSE_SOURCE} * is enabled. *

* * Note: no encoding argument is taken since it can always be * auto-detected as suggested by JSON RFC. Json specification * supports only UTF-8, UTF-16 and UTF-32 as valid encodings, * so auto-detection implemented only for this charsets. * For other charsets use {@link #createParser(java.io.Reader)}. * * @param in InputStream to use for reading JSON content to parse * * @deprecated Since 2.2, use {@link #createParser(InputStream)} instead. */ @Deprecated public JsonParser createJsonParser(InputStream in) throws IOException, JsonParseException { return createParser(in); } /** * Method for constructing parser for parsing * the contents accessed via specified Reader.

* The read stream will not be owned by * the parser, it will still be managed (i.e. closed if * end-of-stream is reacher, or parser close method called) * if (and only if) {@link com.fasterxml.jackson.core.JsonParser.Feature#AUTO_CLOSE_SOURCE} * is enabled. * * @param r Reader to use for reading JSON content to parse * * @deprecated Since 2.2, use {@link #createParser(Reader)} instead. */ @Deprecated public JsonParser createJsonParser(Reader r) throws IOException, JsonParseException { return createParser(r); } /** * Method for constructing parser for parsing the contents of given byte array. * * @deprecated Since 2.2, use {@link #createParser(byte[])} instead. */ @Deprecated public JsonParser createJsonParser(byte[] data) throws IOException, JsonParseException { return createParser(data); } /** * Method for constructing parser for parsing * the contents of given byte array. * * @param data Buffer that contains data to parse * @param offset Offset of the first data byte within buffer * @param len Length of contents to parse within buffer * * @deprecated Since 2.2, use {@link #createParser(byte[],int,int)} instead. */ @Deprecated public JsonParser createJsonParser(byte[] data, int offset, int len) throws IOException, JsonParseException { return createParser(data, offset, len); } /** * Method for constructing parser for parsing * contents of given String. * * @deprecated Since 2.2, use {@link #createParser(String)} instead. */ @Deprecated public JsonParser createJsonParser(String content) throws IOException, JsonParseException { return createParser(content); } /* /********************************************************** /* Deprecated generator factory methods: to be removed from 3.x /********************************************************** */ /** * Method for constructing JSON generator for writing JSON content * using specified output stream. * Encoding to use must be specified, and needs to be one of available * types (as per JSON specification). *

* Underlying stream is NOT owned by the generator constructed, * so that generator will NOT close the output stream when * {@link JsonGenerator#close} is called (unless auto-closing * feature, * {@link com.fasterxml.jackson.core.JsonGenerator.Feature#AUTO_CLOSE_TARGET} * is enabled). * Using application needs to close it explicitly if this is the case. *

* Note: there are formats that use fixed encoding (like most binary data formats) * and that ignore passed in encoding. * * @param out OutputStream to use for writing JSON content * @param enc Character encoding to use * * @deprecated Since 2.2, use {@link #createGenerator(OutputStream, JsonEncoding)} instead. */ @Deprecated public JsonGenerator createJsonGenerator(OutputStream out, JsonEncoding enc) throws IOException { return createGenerator(out, enc); } /** * Method for constructing JSON generator for writing JSON content * using specified Writer. *

* Underlying stream is NOT owned by the generator constructed, * so that generator will NOT close the Reader when * {@link JsonGenerator#close} is called (unless auto-closing * feature, * {@link com.fasterxml.jackson.core.JsonGenerator.Feature#AUTO_CLOSE_TARGET} is enabled). * Using application needs to close it explicitly. * * @param out Writer to use for writing JSON content * * @deprecated Since 2.2, use {@link #createGenerator(Writer)} instead. */ @Deprecated public JsonGenerator createJsonGenerator(Writer out) throws IOException { return createGenerator(out); } /** * Convenience method for constructing generator that uses default * encoding of the format (UTF-8 for JSON and most other data formats). *

* Note: there are formats that use fixed encoding (like most binary data formats). * * @deprecated Since 2.2, use {@link #createGenerator(OutputStream)} instead. */ @Deprecated public JsonGenerator createJsonGenerator(OutputStream out) throws IOException { return createGenerator(out, JsonEncoding.UTF8); } /* /********************************************************** /* Factory methods used by factory for creating parser instances, /* overridable by sub-classes /********************************************************** */ /** * Overridable factory method that actually instantiates desired parser * given {@link InputStream} and context object. *

* This method is specifically designed to remain * compatible between minor versions so that sub-classes can count * on it being called as expected. That is, it is part of official * interface from sub-class perspective, although not a public * method available to users of factory implementations. * * @since 2.1 */ protected JsonParser _createParser(InputStream in, IOContext ctxt) throws IOException { // As per [JACKSON-259], may want to fully disable canonicalization: return new ByteSourceJsonBootstrapper(ctxt, in).constructParser(_parserFeatures, _objectCodec, _byteSymbolCanonicalizer, _rootCharSymbols, _factoryFeatures); } /** * Overridable factory method that actually instantiates parser * using given {@link Reader} object for reading content. *

* This method is specifically designed to remain * compatible between minor versions so that sub-classes can count * on it being called as expected. That is, it is part of official * interface from sub-class perspective, although not a public * method available to users of factory implementations. * * @since 2.1 */ protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException { return new ReaderBasedJsonParser(ctxt, _parserFeatures, r, _objectCodec, _rootCharSymbols.makeChild(_factoryFeatures)); } /** * Overridable factory method that actually instantiates parser * using given char[] object for accessing content. * * @since 2.4 */ protected JsonParser _createParser(char[] data, int offset, int len, IOContext ctxt, boolean recyclable) throws IOException { return new ReaderBasedJsonParser(ctxt, _parserFeatures, null, _objectCodec, _rootCharSymbols.makeChild(_factoryFeatures), data, offset, offset+len, recyclable); } /** * Overridable factory method that actually instantiates parser * using given {@link Reader} object for reading content * passed as raw byte array. *

* This method is specifically designed to remain * compatible between minor versions so that sub-classes can count * on it being called as expected. That is, it is part of official * interface from sub-class perspective, although not a public * method available to users of factory implementations. */ protected JsonParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException { return new ByteSourceJsonBootstrapper(ctxt, data, offset, len).constructParser(_parserFeatures, _objectCodec, _byteSymbolCanonicalizer, _rootCharSymbols, _factoryFeatures); } /** * Optional factory method, expected to be overridden * * @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. _requireJSONFactory("InputData source not (yet?) supported for this format (%s)"); // 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, /* overridable by sub-classes /********************************************************** */ /** * Overridable factory method that actually instantiates generator for * given {@link Writer} and context object. *

* This method is specifically designed to remain * compatible between minor versions so that sub-classes can count * on it being called as expected. That is, it is part of official * interface from sub-class perspective, although not a public * method available to users of factory implementations. */ protected JsonGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException { WriterBasedJsonGenerator gen = new WriterBasedJsonGenerator(ctxt, _generatorFeatures, _objectCodec, out, _quoteChar); if (_maximumNonEscapedChar > 0) { gen.setHighestNonEscapedChar(_maximumNonEscapedChar); } if (_characterEscapes != null) { gen.setCharacterEscapes(_characterEscapes); } SerializableString rootSep = _rootValueSeparator; if (rootSep != DEFAULT_ROOT_VALUE_SEPARATOR) { gen.setRootValueSeparator(rootSep); } return gen; } /** * Overridable factory method that actually instantiates generator for * given {@link OutputStream} and context object, using UTF-8 encoding. *

* This method is specifically designed to remain * compatible between minor versions so that sub-classes can count * on it being called as expected. That is, it is part of official * interface from sub-class perspective, although not a public * method available to users of factory implementations. */ protected JsonGenerator _createUTF8Generator(OutputStream out, IOContext ctxt) throws IOException { UTF8JsonGenerator gen = new UTF8JsonGenerator(ctxt, _generatorFeatures, _objectCodec, out, _quoteChar); if (_maximumNonEscapedChar > 0) { gen.setHighestNonEscapedChar(_maximumNonEscapedChar); } if (_characterEscapes != null) { gen.setCharacterEscapes(_characterEscapes); } SerializableString rootSep = _rootValueSeparator; if (rootSep != DEFAULT_ROOT_VALUE_SEPARATOR) { gen.setRootValueSeparator(rootSep); } return gen; } protected Writer _createWriter(OutputStream out, JsonEncoding enc, IOContext ctxt) throws IOException { // note: this should not get called any more (caller checks, dispatches) if (enc == JsonEncoding.UTF8) { // We have optimized writer for UTF-8 return new UTF8Writer(ctxt, out); } // not optimal, but should do unless we really care about UTF-16/32 encoding speed return new OutputStreamWriter(out, enc.getJavaName()); } /* /********************************************************** /* Internal factory methods, decorator handling /********************************************************** */ /** * @since 2.4 */ protected final InputStream _decorate(InputStream in, IOContext ctxt) throws IOException { if (_inputDecorator != null) { InputStream in2 = _inputDecorator.decorate(ctxt, in); if (in2 != null) { return in2; } } return in; } /** * @since 2.4 */ protected final Reader _decorate(Reader in, IOContext ctxt) throws IOException { if (_inputDecorator != null) { Reader in2 = _inputDecorator.decorate(ctxt, in); if (in2 != null) { return in2; } } return in; } /** * @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 { if (_outputDecorator != null) { OutputStream out2 = _outputDecorator.decorate(ctxt, out); if (out2 != null) { return out2; } } return out; } /** * @since 2.4 */ protected final Writer _decorate(Writer out, IOContext ctxt) throws IOException { if (_outputDecorator != null) { Writer out2 = _outputDecorator.decorate(ctxt, out); if (out2 != null) { return out2; } } return out; } /* /********************************************************** /* Internal factory methods, other /********************************************************** */ /** * Method used by factory to create buffer recycler instances * for parsers and generators. *

* Note: only public to give access for ObjectMapper */ public BufferRecycler _getBufferRecycler() { /* 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 (Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING.enabledIn(_factoryFeatures)) { return BufferRecyclers.getBufferRecycler(); } return new BufferRecycler(); } /** * Overridable factory method that actually instantiates desired * context object. */ protected IOContext _createContext(Object srcRef, boolean resourceManaged) { return new IOContext(_getBufferRecycler(), srcRef, resourceManaged); } /** * Overridable factory method that actually instantiates desired * context object for async (non-blocking) parsing * * @since 2.9.7 */ protected IOContext _createNonBlockingContext(Object srcRef) { // [jackson-core#479]: allow recycling for non-blocking parser again // now that access is thread-safe return new IOContext(_getBufferRecycler(), srcRef, false); } /* /********************************************************** /* Internal helper methods /********************************************************** */ /** * Helper method called to work around the problem of this class both defining * general API for constructing parsers+generators AND implementing the API * for JSON handling. Problem here is that when adding new functionality * via factory methods, it is not possible to leave these methods abstract * (because we are implementing them for JSON); but there is risk that * sub-classes do not override them all (plus older version can not implement). * So a work-around is to add a check to ensure that factory is still one * used for JSON; and if not, make base implementation of a factory method fail. * * @since 2.9 */ private final void _requireJSONFactory(String msg) { if (!_isJSONFactory()) { throw new UnsupportedOperationException(String.format(msg, getFormatName())); } } private final boolean _isJSONFactory() { // NOTE: since we only really care about whether this is standard JSON-backed factory, // or its sub-class / delegated to one, no need to check for equality, identity is enough return getFormatName() == FORMAT_NAME_JSON; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonFactoryBuilder.java000066400000000000000000000172401356164247300327560ustar00rootroot00000000000000package com.fasterxml.jackson.core; import com.fasterxml.jackson.core.io.CharacterEscapes; import com.fasterxml.jackson.core.io.SerializedString; import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.json.JsonWriteFeature; /** * {@link com.fasterxml.jackson.core.TSFBuilder} * implementation for constructing vanilla {@link JsonFactory} * instances for reading/writing JSON encoded content. *

* NOTE: as of Jackson 2.x, use of JSON-specific builder is bit cumbersome * since {@link JsonFactory} serves dual duty of base class AND actual * implementation for JSON backend. This will be fixed in Jackson 3.0. * * @since 2.10 */ public class JsonFactoryBuilder extends TSFBuilder { protected CharacterEscapes _characterEscapes; protected SerializableString _rootValueSeparator; protected int _maximumNonEscapedChar; /** * Character used for quoting field names (if field name quoting has not * been disabled with {@link JsonWriteFeature#QUOTE_FIELD_NAMES}) * and JSON String values. */ protected char _quoteChar = JsonFactory.DEFAULT_QUOTE_CHAR; public JsonFactoryBuilder() { super(); _rootValueSeparator = JsonFactory.DEFAULT_ROOT_VALUE_SEPARATOR; _maximumNonEscapedChar = 0; } public JsonFactoryBuilder(JsonFactory base) { super(base); _characterEscapes = base.getCharacterEscapes(); _rootValueSeparator = base._rootValueSeparator; _maximumNonEscapedChar = base._maximumNonEscapedChar; } /* /********************************************************** /* Mutators /********************************************************** */ // // // JSON-parsing features @Override public JsonFactoryBuilder enable(JsonReadFeature f) { _legacyEnable(f.mappedFeature()); return this; } @Override public JsonFactoryBuilder enable(JsonReadFeature first, JsonReadFeature... other) { _legacyEnable(first.mappedFeature()); enable(first); for (JsonReadFeature f : other) { _legacyEnable(f.mappedFeature()); } return this; } @Override public JsonFactoryBuilder disable(JsonReadFeature f) { _legacyDisable(f.mappedFeature()); return this; } @Override public JsonFactoryBuilder disable(JsonReadFeature first, JsonReadFeature... other) { _legacyDisable(first.mappedFeature()); for (JsonReadFeature f : other) { _legacyEnable(f.mappedFeature()); } return this; } @Override public JsonFactoryBuilder configure(JsonReadFeature f, boolean state) { return state ? enable(f) : disable(f); } // // // JSON-generating features @Override public JsonFactoryBuilder enable(JsonWriteFeature f) { JsonGenerator.Feature old = f.mappedFeature(); if (old != null) { _legacyEnable(old); } return this; } @Override public JsonFactoryBuilder enable(JsonWriteFeature first, JsonWriteFeature... other) { _legacyEnable(first.mappedFeature()); for (JsonWriteFeature f : other) { _legacyEnable(f.mappedFeature()); } return this; } @Override public JsonFactoryBuilder disable(JsonWriteFeature f) { _legacyDisable(f.mappedFeature()); return this; } @Override public JsonFactoryBuilder disable(JsonWriteFeature first, JsonWriteFeature... other) { _legacyDisable(first.mappedFeature()); for (JsonWriteFeature f : other) { _legacyDisable(f.mappedFeature()); } return this; } @Override public JsonFactoryBuilder configure(JsonWriteFeature f, boolean state) { return state ? enable(f) : disable(f); } // // // JSON-specific helper objects, settings /** * Method for defining custom escapes factory uses for {@link JsonGenerator}s * it creates. */ public JsonFactoryBuilder characterEscapes(CharacterEscapes esc) { _characterEscapes = esc; return this; } /** * Method that allows overriding String used for separating root-level * JSON values (default is single space character) * * @param sep Separator to use, if any; null means that no separator is * automatically added */ public JsonFactoryBuilder rootValueSeparator(String sep) { _rootValueSeparator = (sep == null) ? null : new SerializedString(sep); return this; } /** * Method that allows overriding String used for separating root-level * JSON values (default is single space character) * * @param sep Separator to use, if any; null means that no separator is * automatically added */ public JsonFactoryBuilder rootValueSeparator(SerializableString sep) { _rootValueSeparator = sep; return this; } /** * Method that allows specifying threshold beyond which all characters are * automatically escaped (without checking possible custom escaping settings * a la {@link #characterEscapes}: for example, to force escaping of all non-ASCII * characters (set to 127), or all non-Latin-1 character (set to 255). * Default setting is "disabled", specified by passing value of {@code 0} (or * negative numbers). *

* NOTE! Lowest legal value (aside from marker 0) is 127: for ASCII range, other checks apply * and this threshold is ignored. If value between [1, 126] is specified, 127 will be * used instead. * * @param maxNonEscaped Highest character code that is NOT automatically escaped; if * positive value above 0, or 0 to indicate that no automatic escaping is applied * beside from what JSON specification requires (and possible custom escape settings). * Values between 1 and 127 are all taken to behave as if 127 is specified: that is, * no automatic escaping is applied in ASCII range. */ public JsonFactoryBuilder highestNonEscapedChar(int maxNonEscaped) { _maximumNonEscapedChar = (maxNonEscaped <= 0) ? 0 : Math.max(127, maxNonEscaped); return this; } /** * Method that allows specifying an alternate * character used for quoting field names (if field name quoting has not * been disabled with {@link JsonWriteFeature#QUOTE_FIELD_NAMES}) * and JSON String values. *

* Default value is double-quote ({@code "}); typical alternative is * single-quote/apostrophe ({@code '}). * * @param ch Character to use for quoting field names and JSON String values. */ public JsonFactoryBuilder quoteChar(char ch) { // 12-Aug-2019, tatu: Due to implementation details, escaping characters beyond // 7-bit ASCII set has deep overhead so let's limit set. If we absolutely // must it is possible of course, but leads to problems combining with // custom escaping aspects. if (ch > 0x7F) { throw new IllegalArgumentException("Can only use Unicode characters up to 0x7F as quote characters"); } _quoteChar = ch; return this; } // // // Accessors for JSON-specific settings public CharacterEscapes characterEscapes() { return _characterEscapes; } public SerializableString rootValueSeparator() { return _rootValueSeparator; } public int highestNonEscapedChar() { return _maximumNonEscapedChar; } public char quoteChar() { return _quoteChar; } @Override public JsonFactory build() { // 28-Dec-2017, tatu: No special settings beyond base class ones, so: return new JsonFactory(this); } } JsonGenerationException.java000066400000000000000000000034471356164247300337370ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; /** * Exception type for exceptions during JSON writing, such as trying * to output content in wrong context (non-matching end-array or end-object, * for example). */ public class JsonGenerationException extends JsonProcessingException { private final static long serialVersionUID = 123; // Stupid eclipse... // transient since 2.7.4 protected transient JsonGenerator _processor; @Deprecated // since 2.7 public JsonGenerationException(Throwable rootCause) { super(rootCause); } @Deprecated // since 2.7 public JsonGenerationException(String msg) { super(msg, (JsonLocation)null); } @Deprecated // since 2.7 public JsonGenerationException(String msg, Throwable rootCause) { super(msg, null, rootCause); } /** * @since 2.7 */ public JsonGenerationException(Throwable rootCause, JsonGenerator g) { super(rootCause); _processor = g; } /** * @since 2.7 */ public JsonGenerationException(String msg, JsonGenerator g) { super(msg, (JsonLocation) null); _processor = g; } /** * @since 2.7 */ public JsonGenerationException(String msg, Throwable rootCause, JsonGenerator g) { super(msg, null, rootCause); _processor = g; } /** * Fluent method that may be used to assign originating {@link JsonGenerator}, * to be accessed using {@link #getProcessor()}. * * @since 2.7 */ public JsonGenerationException withGenerator(JsonGenerator g) { _processor = g; return this; } @Override public JsonGenerator getProcessor() { return _processor; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java000066400000000000000000002404031356164247300317650ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; import java.io.*; import java.math.BigDecimal; import java.math.BigInteger; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import com.fasterxml.jackson.core.JsonParser.NumberType; import com.fasterxml.jackson.core.io.CharacterEscapes; import com.fasterxml.jackson.core.type.WritableTypeId; import com.fasterxml.jackson.core.type.WritableTypeId.Inclusion; import com.fasterxml.jackson.core.util.VersionUtil; import static com.fasterxml.jackson.core.JsonTokenId.*; /** * Base class that defines public API for writing JSON content. * Instances are created using factory methods of * a {@link JsonFactory} instance. * * @author Tatu Saloranta */ public abstract class JsonGenerator implements Closeable, Flushable, Versioned { /** * Enumeration that defines all togglable features for generators. */ public enum Feature { // // Low-level I/O / content features /** * Feature that determines whether generator will automatically * close underlying output target that is NOT owned by the * generator. * If disabled, calling application has to separately * close the underlying {@link OutputStream} and {@link Writer} * instances used to create the generator. If enabled, generator * will handle closing, as long as generator itself gets closed: * this happens when end-of-input is encountered, or generator * is closed by a call to {@link JsonGenerator#close}. *

* Feature is enabled by default. */ AUTO_CLOSE_TARGET(true), /** * Feature that determines what happens when the generator is * closed while there are still unmatched * {@link JsonToken#START_ARRAY} or {@link JsonToken#START_OBJECT} * entries in output content. If enabled, such Array(s) and/or * Object(s) are automatically closed; if disabled, nothing * specific is done. *

* Feature is enabled by default. */ AUTO_CLOSE_JSON_CONTENT(true), /** * Feature that specifies that calls to {@link #flush} will cause * matching flush() to underlying {@link OutputStream} * or {@link Writer}; if disabled this will not be done. * Main reason to disable this feature is to prevent flushing at * generator level, if it is not possible to prevent method being * called by other code (like ObjectMapper or third * party libraries). *

* Feature is enabled by default. */ FLUSH_PASSED_TO_STREAM(true), // // Quoting-related features /** * Feature that determines whether JSON Object field names are * quoted using double-quotes, as specified by JSON specification * or not. Ability to disable quoting was added to support use * cases where they are not usually expected, which most commonly * occurs when used straight from Javascript. *

* Feature is enabled by default (since it is required by JSON specification). * * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#QUOTE_FIELD_NAMES} instead */ @Deprecated QUOTE_FIELD_NAMES(true), /** * Feature that determines whether "exceptional" (not real number) * float/double values are output as quoted strings. * The values checked are Double.Nan, * Double.POSITIVE_INFINITY and Double.NEGATIVE_INIFINTY (and * associated Float values). * If feature is disabled, these numbers are still output using * associated literal values, resulting in non-conformant * output. *

* Feature is enabled by default. * * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#WRITE_NAN_AS_STRINGS} instead */ @Deprecated QUOTE_NON_NUMERIC_NUMBERS(true), // // Character escaping features /** * Feature that specifies that all characters beyond 7-bit ASCII * range (i.e. code points of 128 and above) need to be output * using format-specific escapes (for JSON, backslash escapes), * if format uses escaping mechanisms (which is generally true * for textual formats but not for binary formats). *

* Note that this setting may not necessarily make sense for all * data formats (for example, binary formats typically do not use * any escaping mechanisms; and some textual formats do not have * general-purpose escaping); if so, settings is simply ignored. * Put another way, effects of this feature are data-format specific. *

* Feature is disabled by default. * * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#ESCAPE_NON_ASCII} instead */ @Deprecated ESCAPE_NON_ASCII(false), // // Datatype coercion features /** * Feature that forces all Java numbers to be written as Strings, * even if the underlying data format has non-textual representation * (which is the case for JSON as well as all binary formats). * Default state is 'false', meaning that Java numbers are to * be serialized using basic numeric serialization (as JSON * numbers, integral or floating point, for example). * If enabled, all such numeric values are instead written out as * textual values (which for JSON means quoted in double-quotes). *

* One use case is to avoid problems with Javascript limitations: * since Javascript standard specifies that all number handling * should be done using 64-bit IEEE 754 floating point values, * result being that some 64-bit integer values can not be * accurately represent (as mantissa is only 51 bit wide). *

* Feature is disabled by default. * * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#WRITE_NUMBERS_AS_STRINGS} instead */ @Deprecated WRITE_NUMBERS_AS_STRINGS(false), /** * Feature that determines whether {@link java.math.BigDecimal} entries are * serialized using {@link java.math.BigDecimal#toPlainString()} to prevent * values to be written using scientific notation. *

* NOTE: only affects generators that serialize {@link java.math.BigDecimal}s * using textual representation (textual formats but potentially some binary * formats). *

* Feature is disabled by default, so default output mode is used; this generally * depends on how {@link BigDecimal} has been created. * * @since 2.3 */ WRITE_BIGDECIMAL_AS_PLAIN(false), // // Schema/Validity support features /** * Feature that determines whether {@link JsonGenerator} will explicitly * check that no duplicate JSON Object field names are written. * If enabled, generator will check all names within context and report * duplicates by throwing a {@link JsonGenerationException}; if disabled, * no such checking will be done. Assumption in latter case is * that caller takes care of not trying to write duplicate names. *

* Note that enabling this feature will incur performance overhead * due to having to store and check additional information. *

* Feature is disabled by default. * * @since 2.3 */ STRICT_DUPLICATE_DETECTION(false), /** * Feature that determines what to do if the underlying data format requires knowledge * of all properties to output, and if no definition is found for a property that * caller tries to write. If enabled, such properties will be quietly ignored; * if disabled, a {@link JsonProcessingException} will be thrown to indicate the * problem. * Typically most textual data formats do NOT require schema information (although * some do, such as CSV), whereas many binary data formats do require definitions * (such as Avro, protobuf), although not all (Smile, CBOR, BSON and MessagePack do not). *

* Note that support for this feature is implemented by individual data format * module, if (and only if) it makes sense for the format in question. For JSON, * for example, this feature has no effect as properties need not be pre-defined. *

* Feature is disabled by default, meaning that if the underlying data format * requires knowledge of all properties to output, attempts to write an unknown * property will result in a {@link JsonProcessingException} * * @since 2.5 */ IGNORE_UNKNOWN(false), ; private final boolean _defaultState; private final int _mask; /** * Method that calculates bit set (flags) of all features that * are enabled by default. */ public static int collectDefaults() { int flags = 0; for (Feature f : values()) { if (f.enabledByDefault()) { flags |= f.getMask(); } } return flags; } private Feature(boolean defaultState) { _defaultState = defaultState; _mask = (1 << ordinal()); } public boolean enabledByDefault() { return _defaultState; } /** * @since 2.3 */ public boolean enabledIn(int flags) { return (flags & _mask) != 0; } public int getMask() { return _mask; } } /* /********************************************************** /* Configuration /********************************************************** */ /** * Object that handles pretty-printing (usually additional * white space to make results more human-readable) during * output. If null, no pretty-printing is done. */ protected PrettyPrinter _cfgPrettyPrinter; /* /********************************************************** /* Construction, initialization /********************************************************** */ protected JsonGenerator() { } /** * Method that can be called to set or reset the object to * use for writing Java objects as JsonContent * (using method {@link #writeObject}). * * @return Generator itself (this), to allow chaining */ public abstract JsonGenerator setCodec(ObjectCodec oc); /** * Method for accessing the object used for writing Java * object as JSON content * (using method {@link #writeObject}). */ public abstract ObjectCodec getCodec(); /** * Accessor for finding out version of the bundle that provided this generator instance. */ @Override public abstract Version version(); /* /********************************************************** /* Public API, Feature configuration /********************************************************** */ /** * Method for enabling specified parser features: * check {@link Feature} for list of available features. * * @return Generator itself (this), to allow chaining */ public abstract JsonGenerator enable(Feature f); /** * Method for disabling specified features * (check {@link Feature} for list of features) * * @return Generator itself (this), to allow chaining */ public abstract JsonGenerator disable(Feature f); /** * Method for enabling or disabling specified feature: * check {@link Feature} for list of available features. * * @return Generator itself (this), to allow chaining */ public final JsonGenerator configure(Feature f, boolean state) { if (state) enable(f); else disable(f); return this; } /** * Method for checking whether given feature is enabled. * Check {@link Feature} for list of available features. */ public abstract boolean isEnabled(Feature f); /** * @since 2.10 */ public boolean isEnabled(StreamWriteFeature f) { return isEnabled(f.mappedFeature()); } /** * Bulk access method for getting state of all standard (non-dataformat-specific) * {@link JsonGenerator.Feature}s. * * @return Bit mask that defines current states of all standard {@link JsonGenerator.Feature}s. * * @since 2.3 */ public abstract int getFeatureMask(); /** * Bulk set method for (re)setting states of all standard {@link Feature}s * * @since 2.3 * * @param values Bitmask that defines which {@link Feature}s are enabled * and which disabled * * @return This parser object, to allow chaining of calls * * @deprecated Since 2.7, use {@link #overrideStdFeatures(int, int)} instead -- remove from 2.9 */ @Deprecated public abstract JsonGenerator setFeatureMask(int values); /** * Bulk set method for (re)setting states of features specified by mask. * Functionally equivalent to * * int oldState = getFeatureMask(); * int newState = (oldState & ~mask) | (values & mask); * setFeatureMask(newState); * * but preferred as this lets caller more efficiently specify actual changes made. * * @param values Bit mask of set/clear state for features to change * @param mask Bit mask of features to change * * @since 2.6 */ public JsonGenerator overrideStdFeatures(int values, int mask) { int oldState = getFeatureMask(); int newState = (oldState & ~mask) | (values & mask); return setFeatureMask(newState); } /** * Bulk access method for getting state of all {@link FormatFeature}s, format-specific * on/off configuration settings. * * @return Bit mask that defines current states of all standard {@link FormatFeature}s. * * @since 2.6 */ public int getFormatFeatures() { return 0; } /** * Bulk set method for (re)setting states of {@link FormatFeature}s, * by specifying values (set / clear) along with a mask, to determine * which features to change, if any. *

* Default implementation will simply throw an exception to indicate that * the generator implementation does not support any {@link FormatFeature}s. * * @param values Bit mask of set/clear state for features to change * @param mask Bit mask of features to change * * @since 2.6 */ public JsonGenerator overrideFormatFeatures(int values, int mask) { // 08-Oct-2018, tatu: For 2.10 we actually do get `JsonWriteFeature`s, although they // are (for 2.x only, not for 3.x) mapper to legacy settings. So do not freak out: // throw new IllegalArgumentException("No FormatFeatures defined for generator of type "+getClass().getName()); return this; } /* /********************************************************** /* Public API, Schema configuration /********************************************************** */ /** * Method to call to make this generator use specified schema. * Method must be called before generating any content, right after instance * has been created. * Note that not all generators support schemas; and those that do usually only * accept specific types of schemas: ones defined for data format this generator * produces. *

* If generator does not support specified schema, {@link UnsupportedOperationException} * is thrown. * * @param schema Schema to use * * @throws UnsupportedOperationException if generator does not support schema */ public void setSchema(FormatSchema schema) { throw new UnsupportedOperationException("Generator of type "+getClass().getName()+" does not support schema of type '" +schema.getSchemaType()+"'"); } /** * Method for accessing Schema that this parser uses, if any. * Default implementation returns null. * * @since 2.1 */ public FormatSchema getSchema() { return null; } /* /********************************************************** /* Public API, other configuration /********************************************************** */ /** * Method for setting a custom pretty printer, which is usually * used to add indentation for improved human readability. * By default, generator does not do pretty printing. *

* To use the default pretty printer that comes with core * Jackson distribution, call {@link #useDefaultPrettyPrinter} * instead. * * @return Generator itself (this), to allow chaining */ public JsonGenerator setPrettyPrinter(PrettyPrinter pp) { _cfgPrettyPrinter = pp; return this; } /** * Accessor for checking whether this generator has a configured * {@link PrettyPrinter}; returns it if so, null if none configured. * * @since 2.1 */ public PrettyPrinter getPrettyPrinter() { return _cfgPrettyPrinter; } /** * Convenience method for enabling pretty-printing using * the default pretty printer * ({@link com.fasterxml.jackson.core.util.DefaultPrettyPrinter}). * * @return Generator itself (this), to allow chaining */ public abstract JsonGenerator useDefaultPrettyPrinter(); /** * Method that can be called to request that generator escapes * all character codes above specified code point (if positive value); * or, to not escape any characters except for ones that must be * escaped for the data format (if -1). * To force escaping of all non-ASCII characters, for example, * this method would be called with value of 127. *

* Note that generators are NOT required to support setting of value * higher than 127, because there are other ways to affect quoting * (or lack thereof) of character codes between 0 and 127. * Not all generators support concept of escaping, either; if so, * calling this method will have no effect. *

* Default implementation does nothing; sub-classes need to redefine * it according to rules of supported data format. * * @param charCode Either -1 to indicate that no additional escaping * is to be done; or highest code point not to escape (meaning higher * ones will be), if positive value. */ public JsonGenerator setHighestNonEscapedChar(int charCode) { return this; } /** * Accessor method for testing what is the highest unescaped character * configured for this generator. This may be either positive value * (when escaping configuration has been set and is in effect), or * 0 to indicate that no additional escaping is in effect. * Some generators may not support additional escaping: for example, * generators for binary formats that do not use escaping should * simply return 0. * * @return Currently active limitation for highest non-escaped character, * if defined; or 0 to indicate no additional escaping is performed. */ public int getHighestEscapedChar() { return 0; } /** * Method for accessing custom escapes factory uses for {@link JsonGenerator}s * it creates. */ public CharacterEscapes getCharacterEscapes() { return null; } /** * Method for defining custom escapes factory uses for {@link JsonGenerator}s * it creates. *

* Default implementation does nothing and simply returns this instance. */ public JsonGenerator setCharacterEscapes(CharacterEscapes esc) { return this; } /** * Method that allows overriding String used for separating root-level * JSON values (default is single space character) *

* Default implementation throws {@link UnsupportedOperationException}. * * @param sep Separator to use, if any; null means that no separator is * automatically added * * @since 2.1 */ public JsonGenerator setRootValueSeparator(SerializableString sep) { throw new UnsupportedOperationException(); } /* /********************************************************** /* Public API, output state access /********************************************************** */ /** * Method that can be used to get access to object that is used * as target for generated output; this is usually either * {@link OutputStream} or {@link Writer}, depending on what * generator was constructed with. * Note that returned value may be null in some cases; including * case where implementation does not want to exposed raw * source to caller. * In cases where output has been decorated, object returned here * is the decorated version; this allows some level of interaction * between users of generator and decorator object. *

* In general use of this accessor should be considered as * "last effort", i.e. only used if no other mechanism is applicable. */ public Object getOutputTarget() { return null; } /** * Method for verifying amount of content that is buffered by generator * but not yet flushed to the underlying target (stream, writer), * in units (byte, char) that the generator implementation uses for buffering; * or -1 if this information is not available. * Unit used is often the same as the unit of underlying target (that is, * `byte` for {@link java.io.OutputStream}, `char` for {@link java.io.Writer}), * but may differ if buffering is done before encoding. * Default JSON-backed implementations do use matching units. *

* Note: non-JSON implementations will be retrofitted for 2.6 and beyond; * please report if you see -1 (missing override) * * @return Amount of content buffered in internal units, if amount known and * accessible; -1 if not accessible. * * @since 2.6 */ public int getOutputBuffered() { return -1; } /** * Helper method, usually equivalent to: * * getOutputContext().getCurrentValue(); * *

* Note that "current value" is NOT populated (or used) by Streaming parser; * it is only used by higher-level data-binding functionality. * The reason it is included here is that it can be stored and accessed hierarchically, * and gets passed through data-binding. * * @since 2.5 */ public Object getCurrentValue() { JsonStreamContext ctxt = getOutputContext(); return (ctxt == null) ? null : ctxt.getCurrentValue(); } /** * Helper method, usually equivalent to: * * getOutputContext().setCurrentValue(v); * * * @since 2.5 */ public void setCurrentValue(Object v) { JsonStreamContext ctxt = getOutputContext(); if (ctxt != null) { ctxt.setCurrentValue(v); } } /* /********************************************************** /* Public API, capability introspection methods /********************************************************** */ /** * Method that can be used to verify that given schema can be used with * this generator (using {@link #setSchema}). * * @param schema Schema to check * * @return True if this generator can use given schema; false if not */ public boolean canUseSchema(FormatSchema schema) { return false; } /** * Introspection method that may be called to see if the underlying * data format supports some kind of Object Ids natively (many do not; * for example, JSON doesn't). * This method must be called prior to calling * {@link #writeObjectId} or {@link #writeObjectRef}. *

* Default implementation returns false; overridden by data formats * that do support native Object Ids. Caller is expected to either * use a non-native notation (explicit property or such), or fail, * in case it can not use native object ids. * * @since 2.3 */ public boolean canWriteObjectId() { return false; } /** * Introspection method that may be called to see if the underlying * data format supports some kind of Type Ids natively (many do not; * for example, JSON doesn't). * This method must be called prior to calling * {@link #writeTypeId}. *

* Default implementation returns false; overridden by data formats * that do support native Type Ids. Caller is expected to either * use a non-native notation (explicit property or such), or fail, * in case it can not use native type ids. * * @since 2.3 */ public boolean canWriteTypeId() { return false; } /** * Introspection method that may be called to see if the underlying * data format supports "native" binary data; that is, an efficient * output of binary content without encoding. *

* Default implementation returns false; overridden by data formats * that do support native binary content. * * @since 2.3 */ public boolean canWriteBinaryNatively() { return false; } /** * Introspection method to call to check whether it is ok to omit * writing of Object fields or not. Most formats do allow omission, * but certain positional formats (such as CSV) require output of * placeholders, even if no real values are to be emitted. * * @since 2.3 */ 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 /********************************************************** */ /** * Method for writing starting marker of a Array value * (for JSON this is character '['; plus possible white space decoration * if pretty-printing is enabled). *

* Array values can be written in any context where values * are allowed: meaning everywhere except for when * a field name is expected. */ public abstract void writeStartArray() throws IOException; // TODO: deprecate in 2.11 (remove from 3.0) /** * Method for writing start marker of an Array value, similar * to {@link #writeStartArray()}, but also specifying how many * elements will be written for the array before calling * {@link #writeEndArray()}. *

* Default implementation simply calls {@link #writeStartArray()}. * * @param size Number of elements this array will have: actual * number of values written (before matching call to * {@link #writeEndArray()} MUST match; generator MAY verify * this is the case. * * @since 2.4 */ public void writeStartArray(int size) throws IOException { writeStartArray(); } /** * @since 2.10 */ public void writeStartArray(Object forValue) throws IOException { writeStartArray(); setCurrentValue(forValue); } /** * @since 2.10 */ public void writeStartArray(Object forValue, int size) throws IOException { writeStartArray(size); setCurrentValue(forValue); } /** * Method for writing closing marker of a JSON Array value * (character ']'; plus possible white space decoration * if pretty-printing is enabled). *

* Marker can be written if the innermost structured type * is Array. */ public abstract void writeEndArray() throws IOException; /** * Method for writing starting marker of an Object value * (character '{'; plus possible white space decoration * if pretty-printing is enabled). *

* Object values can be written in any context where values * are allowed: meaning everywhere except for when * a field name is expected. */ public abstract void writeStartObject() throws IOException; /** * Method for writing starting marker of an Object value * to represent the given Java Object value. * 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. *

* 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 starting marker of an Object value * to represent the given Java Object value. * 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. * In addition, caller knows number of key/value pairs ("properties") * that will get written for the Object value: this is relevant for * some format backends (but not, as an example, for JSON). *

* Object values can be written in any context where values * are allowed: meaning everywhere except for when * a field name is expected. * * @since 2.10 */ public void writeStartObject(Object forValue, int size) throws IOException { writeStartObject(); setCurrentValue(forValue); } /** * Method for writing closing marker of an Object value * (character '}'; plus possible white space decoration * if pretty-printing is enabled). *

* Marker can be written if the innermost structured type * is Object, and the last written event was either a * complete value, or START-OBJECT marker (see JSON specification * for more details). */ public abstract void writeEndObject() throws IOException; /** * Method for writing a field name (JSON String surrounded by * double quotes: syntactically identical to a JSON String value), * possibly decorated by white space if pretty-printing is enabled. *

* Field names can only be written in Object context (check out * JSON specification for details), when field name is expected * (field names alternate with values). */ public abstract void writeFieldName(String name) throws IOException; /** * Method similar to {@link #writeFieldName(String)}, main difference * being that it may perform better as some of processing (such as * quoting of certain characters, or encoding into external encoding * if supported by generator) can be done just once and reused for * later calls. *

* Default implementation simple uses unprocessed name container in * serialized String; implementations are strongly encouraged to make * use of more efficient methods argument object has. */ 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 String * 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(array, length); 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(array, length); 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(array, length); for (int i = offset, end = offset+length; i < end; ++i) { writeNumber(array[i]); } writeEndArray(); } /* /********************************************************** /* Public API, write methods, text/String values /********************************************************** */ /** * Method for outputting a String value. Depending on context * this means either array element, (object) field value or * a stand alone String; but in all cases, String will be * surrounded in double quotes, and contents will be properly * escaped as required by JSON specification. */ public abstract void writeString(String text) throws IOException; /** * Method for outputting a String value. Depending on context * this means either array element, (object) field value or * a stand alone String; but in all cases, String will be * surrounded in double quotes, and contents will be properly * escaped as required by JSON specification. * If the reader is null, then write a null. * If len is < 0, then write all contents of the reader. * Otherwise, write only len characters. * * @since 2.9 */ public void writeString(Reader reader, int len) throws IOException { // Let's implement this as "unsupported" to make it easier to add new parser impls _reportUnsupportedOperation(); } /** * Method for outputting a String value. Depending on context * this means either array element, (object) field value or * a stand alone String; but in all cases, String will be * surrounded in double quotes, and contents will be properly * escaped as required by JSON specification. */ public abstract void writeString(char[] text, int offset, int len) throws IOException; /** * Method similar to {@link #writeString(String)}, but that takes * {@link SerializableString} which can make this potentially * more efficient to call as generator may be able to reuse * quoted and/or encoded representation. *

* Default implementation just calls {@link #writeString(String)}; * sub-classes should override it with more efficient implementation * if possible. */ public abstract void writeString(SerializableString text) throws IOException; /** * Method similar to {@link #writeString(String)} but that takes as * its input a UTF-8 encoded String that is to be output as-is, without additional * escaping (type of which depends on data format; backslashes for JSON). * However, quoting that data format requires (like double-quotes for JSON) will be added * around the value if and as necessary. *

* Note that some backends may choose not to support this method: for * example, if underlying destination is a {@link java.io.Writer} * using this method would require UTF-8 decoding. * If so, implementation may instead choose to throw a * {@link UnsupportedOperationException} due to ineffectiveness * of having to decode input. */ public abstract void writeRawUTF8String(byte[] text, int offset, int length) throws IOException; /** * Method similar to {@link #writeString(String)} but that takes as its input * a UTF-8 encoded String which has not been escaped using whatever * escaping scheme data format requires (for JSON that is backslash-escaping * for control characters and double-quotes; for other formats something else). * This means that textual JSON backends need to check if value needs * JSON escaping, but otherwise can just be copied as is to output. * Also, quoting that data format requires (like double-quotes for JSON) will be added * around the value if and as necessary. *

* Note that some backends may choose not to support this method: for * example, if underlying destination is a {@link java.io.Writer} * using this method would require UTF-8 decoding. * In this case * generator implementation may instead choose to throw a * {@link UnsupportedOperationException} due to ineffectiveness * of having to decode input. */ public abstract void writeUTF8String(byte[] text, int offset, int length) throws IOException; /* /********************************************************** /* Public API, write methods, binary/raw content /********************************************************** */ /** * Method that will force generator to copy * input text verbatim with no modifications (including * that no escaping is done and no separators are added even * if context [array, object] would otherwise require such). * If such separators are desired, use * {@link #writeRawValue(String)} instead. *

* Note that not all generator implementations necessarily support * such by-pass methods: those that do not will throw * {@link UnsupportedOperationException}. */ public abstract void writeRaw(String text) throws IOException; /** * Method that will force generator to copy * input text verbatim with no modifications (including * that no escaping is done and no separators are added even * if context [array, object] would otherwise require such). * If such separators are desired, use * {@link #writeRawValue(String)} instead. *

* Note that not all generator implementations necessarily support * such by-pass methods: those that do not will throw * {@link UnsupportedOperationException}. */ public abstract void writeRaw(String text, int offset, int len) throws IOException; /** * Method that will force generator to copy * input text verbatim with no modifications (including * that no escaping is done and no separators are added even * if context [array, object] would otherwise require such). * If such separators are desired, use * {@link #writeRawValue(String)} instead. *

* Note that not all generator implementations necessarily support * such by-pass methods: those that do not will throw * {@link UnsupportedOperationException}. */ public abstract void writeRaw(char[] text, int offset, int len) throws IOException; /** * Method that will force generator to copy * input text verbatim with no modifications (including * that no escaping is done and no separators are added even * if context [array, object] would otherwise require such). * If such separators are desired, use * {@link #writeRawValue(String)} instead. *

* Note that not all generator implementations necessarily support * such by-pass methods: those that do not will throw * {@link UnsupportedOperationException}. */ public abstract void writeRaw(char c) throws IOException; /** * Method that will force generator to copy * input text verbatim with no modifications (including * that no escaping is done and no separators are added even * if context [array, object] would otherwise require such). * If such separators are desired, use * {@link #writeRawValue(String)} instead. *

* Note that not all generator implementations necessarily support * such by-pass methods: those that do not will throw * {@link UnsupportedOperationException}. *

* The default implementation delegates to {@link #writeRaw(String)}; * other backends that support raw inclusion of text are encouraged * to implement it in more efficient manner (especially if they * use UTF-8 encoding). * * @since 2.1 */ // public abstract void writeRaw(SerializableString raw) throws IOException; public void writeRaw(SerializableString raw) throws IOException { writeRaw(raw.getValue()); } /** * Method that will force generator to copy * input text verbatim without any modifications, but assuming * it must constitute a single legal JSON value (number, string, * boolean, null, Array or List). Assuming this, proper separators * are added if and as needed (comma or colon), and generator * state updated to reflect this. */ public abstract void writeRawValue(String text) throws IOException; public abstract void writeRawValue(String text, int offset, int len) throws IOException; public abstract void writeRawValue(char[] text, int offset, int len) throws IOException; /** * Method similar to {@link #writeRawValue(String)}, but potentially more * efficient as it may be able to use pre-encoded content (similar to * {@link #writeRaw(SerializableString)}. * * @since 2.5 */ public void writeRawValue(SerializableString raw) throws IOException { writeRawValue(raw.getValue()); } /** * Method that will output given chunk of binary data as base64 * encoded, as a complete String value (surrounded by double quotes). * This method defaults *

* Note: because JSON Strings can not contain unescaped linefeeds, * if linefeeds are included (as per last argument), they must be * escaped. This adds overhead for decoding without improving * readability. * Alternatively if linefeeds are not included, * resulting String value may violate the requirement of base64 * RFC which mandates line-length of 76 characters and use of * linefeeds. However, all {@link JsonParser} implementations * are required to accept such "long line base64"; as do * typical production-level base64 decoders. * * @param bv Base64 variant to use: defines details such as * whether padding is used (and if so, using which character); * what is the maximum line length before adding linefeed, * and also the underlying alphabet to use. */ public abstract void writeBinary(Base64Variant bv, byte[] data, int offset, int len) throws IOException; /** * Similar to {@link #writeBinary(Base64Variant,byte[],int,int)}, * but default to using the Jackson default Base64 variant * (which is {@link Base64Variants#MIME_NO_LINEFEEDS}). */ public void writeBinary(byte[] data, int offset, int len) throws IOException { writeBinary(Base64Variants.getDefaultVariant(), data, offset, len); } /** * Similar to {@link #writeBinary(Base64Variant,byte[],int,int)}, * but assumes default to using the Jackson default Base64 variant * (which is {@link Base64Variants#MIME_NO_LINEFEEDS}). Also * assumes that whole byte array is to be output. */ public void writeBinary(byte[] data) throws IOException { writeBinary(Base64Variants.getDefaultVariant(), data, 0, data.length); } /** * Similar to {@link #writeBinary(Base64Variant,InputStream,int)}, * but assumes default to using the Jackson default Base64 variant * (which is {@link Base64Variants#MIME_NO_LINEFEEDS}). * * @param data InputStream to use for reading binary data to write. * Will not be closed after successful write operation * @param dataLength (optional) number of bytes that will be available; * or -1 to be indicate it is not known. Note that implementations * need not support cases where length is not known in advance; this * depends on underlying data format: JSON output does NOT require length, * other formats may */ public int writeBinary(InputStream data, int dataLength) throws IOException { return writeBinary(Base64Variants.getDefaultVariant(), data, dataLength); } /** * Method similar to {@link #writeBinary(Base64Variant,byte[],int,int)}, * but where input is provided through a stream, allowing for incremental * writes without holding the whole input in memory. * * @param bv Base64 variant to use * @param data InputStream to use for reading binary data to write. * Will not be closed after successful write operation * @param dataLength (optional) number of bytes that will be available; * or -1 to be indicate it is not known. * If a positive length is given, data MUST provide at least * that many bytes: if not, an exception will be thrown. * Note that implementations * need not support cases where length is not known in advance; this * depends on underlying data format: JSON output does NOT require length, * other formats may. * * @return Number of bytes read from data and written as binary payload * * @since 2.1 */ public abstract int writeBinary(Base64Variant bv, InputStream data, int dataLength) throws IOException; /* /********************************************************** /* Public API, write methods, numeric /********************************************************** */ /** * Method for outputting given value as JSON number. * Can be called in any context where a value is expected * (Array value, Object field value, root-level value). * Additional white space may be added around the value * if pretty-printing is enabled. * * @param v Number value to write * * @since 2.2 */ public void writeNumber(short v) throws IOException { writeNumber((int) v); } /** * Method for outputting given value as JSON number. * Can be called in any context where a value is expected * (Array value, Object field value, root-level value). * Additional white space may be added around the value * if pretty-printing is enabled. * * @param v Number value to write */ public abstract void writeNumber(int v) throws IOException; /** * Method for outputting given value as JSON number. * Can be called in any context where a value is expected * (Array value, Object field value, root-level value). * Additional white space may be added around the value * if pretty-printing is enabled. * * @param v Number value to write */ public abstract void writeNumber(long v) throws IOException; /** * Method for outputting given value as JSON number. * Can be called in any context where a value is expected * (Array value, Object field value, root-level value). * Additional white space may be added around the value * if pretty-printing is enabled. * * @param v Number value to write */ public abstract void writeNumber(BigInteger v) throws IOException; /** * Method for outputting indicate JSON numeric value. * Can be called in any context where a value is expected * (Array value, Object field value, root-level value). * Additional white space may be added around the value * if pretty-printing is enabled. * * @param v Number value to write */ public abstract void writeNumber(double v) throws IOException; /** * Method for outputting indicate JSON numeric value. * Can be called in any context where a value is expected * (Array value, Object field value, root-level value). * Additional white space may be added around the value * if pretty-printing is enabled. * * @param v Number value to write */ public abstract void writeNumber(float v) throws IOException; /** * Method for outputting indicate JSON numeric value. * Can be called in any context where a value is expected * (Array value, Object field value, root-level value). * Additional white space may be added around the value * if pretty-printing is enabled. * * @param v Number value to write */ public abstract void writeNumber(BigDecimal v) throws IOException; /** * Write method that can be used for custom numeric types that can * not be (easily?) converted to "standard" Java number types. * Because numbers are not surrounded by double quotes, regular * {@link #writeString} method can not be used; nor * {@link #writeRaw} because that does not properly handle * value separators needed in Array or Object contexts. *

* Note: because of lack of type safety, some generator * implementations may not be able to implement this * method. For example, if a binary JSON format is used, * it may require type information for encoding; similarly * for generator-wrappers around Java objects or JSON nodes. * If implementation does not implement this method, * it needs to throw {@link UnsupportedOperationException}. * * @throws UnsupportedOperationException If underlying data format does not * support numbers serialized textually AND if generator is not allowed * to just output a String instead (Schema-based formats may require actual * number, for example) */ 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'). * Can be called in any context where a value is expected * (Array value, Object field value, root-level value). * Additional white space may be added around the value * if pretty-printing is enabled. */ public abstract void writeBoolean(boolean state) throws IOException; /** * Method for outputting literal JSON null value. * Can be called in any context where a value is expected * (Array value, Object field value, root-level value). * Additional white space may be added around the value * if pretty-printing is enabled. */ 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 { // 01-Sep-2016, tatu: As per [core#318], handle small number of cases if (object == null) { writeNull(); return; } if (object instanceof byte[]) { writeBinary((byte[]) object); return; } throw new JsonGenerationException("No native support for writing embedded objects of type " +object.getClass().getName(), this); } /* /********************************************************** /* Public API, write methods, Native Ids (type, object) /********************************************************** */ /** * Method that can be called to output so-called native Object Id. * Note that it may only be called after ensuring this is legal * (with {@link #canWriteObjectId()}), as not all data formats * have native type id support; and some may only allow them in * certain positions or locations. * If output is not allowed by the data format in this position, * a {@link JsonGenerationException} will be thrown. * * @since 2.3 */ public void writeObjectId(Object id) throws IOException { throw new JsonGenerationException("No native support for writing Object Ids", this); } /** * Method that can be called to output references to native Object Ids. * Note that it may only be called after ensuring this is legal * (with {@link #canWriteObjectId()}), as not all data formats * have native type id support; and some may only allow them in * certain positions or locations. * If output is not allowed by the data format in this position, * a {@link JsonGenerationException} will be thrown. */ public void writeObjectRef(Object id) throws IOException { throw new JsonGenerationException("No native support for writing Object Ids", this); } /** * Method that can be called to output so-called native Type Id. * Note that it may only be called after ensuring this is legal * (with {@link #canWriteTypeId()}), as not all data formats * have native type id support; and some may only allow them in * certain positions or locations. * If output is not allowed by the data format in this position, * a {@link JsonGenerationException} will be thrown. * * @since 2.3 */ public void writeTypeId(Object id) throws IOException { throw new JsonGenerationException("No native support for writing Type Ids", this); } /* * 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. *

* NOTE: databind may choose to skip calling this method for some special cases * (and instead included type id via regular write methods and/or {@link #writeTypeId} * -- this is discouraged, but not illegal, and may be necessary as a work-around * in some cases. * * @since 2.9 */ public WritableTypeId writeTypePrefix(WritableTypeId typeIdDef) throws IOException { Object id = typeIdDef.id; final JsonToken valueShape = typeIdDef.valueShape; if (canWriteTypeId()) { typeIdDef.wrapperWritten = false; // just rely on native type output method (sub-classes likely to override) writeTypeId(id); } else { // No native type id; write wrappers // Normally we only support String type ids (non-String reserved for native type ids) String idStr = (id instanceof String) ? (String) id : String.valueOf(id); typeIdDef.wrapperWritten = true; Inclusion incl = typeIdDef.include; // first: can not output "as property" if value not Object; if so, must do "as array" if ((valueShape != JsonToken.START_OBJECT) && incl.requiresObjectContext()) { typeIdDef.include = incl = WritableTypeId.Inclusion.WRAPPER_ARRAY; } switch (incl) { case PARENT_PROPERTY: // nothing to do here, as it has to be written in suffix... break; case PAYLOAD_PROPERTY: // only output as native type id; otherwise caller must handle using some // other mechanism, so... break; case METADATA_PROPERTY: // must have Object context by now, so simply write as field name // Note, too, that it's bit tricky, since we must print START_OBJECT that is part // of value first -- and then NOT output it later on: hence return "early" writeStartObject(typeIdDef.forValue); writeStringField(typeIdDef.asProperty, idStr); return typeIdDef; case WRAPPER_OBJECT: // NOTE: this is wrapper, not directly related to value to output, so don't pass writeStartObject(); writeFieldName(idStr); break; case WRAPPER_ARRAY: default: // should never occur but translate as "as-array" writeStartArray(); // wrapper, not actual array object to write writeString(idStr); } } // and finally possible start marker for value itself: if (valueShape == JsonToken.START_OBJECT) { writeStartObject(typeIdDef.forValue); } else if (valueShape == JsonToken.START_ARRAY) { // should we now set the current object? writeStartArray(); } return typeIdDef; } /* * @since 2.9 */ public WritableTypeId writeTypeSuffix(WritableTypeId typeIdDef) throws IOException { final JsonToken valueShape = typeIdDef.valueShape; // First: does value need closing? if (valueShape == JsonToken.START_OBJECT) { writeEndObject(); } else if (valueShape == JsonToken.START_ARRAY) { writeEndArray(); } if (typeIdDef.wrapperWritten) { switch (typeIdDef.include) { case WRAPPER_ARRAY: writeEndArray(); break; case PARENT_PROPERTY: // unusually, need to output AFTER value. And no real wrapper... { Object id = typeIdDef.id; String idStr = (id instanceof String) ? (String) id : String.valueOf(id); writeStringField(typeIdDef.asProperty, idStr); } break; case METADATA_PROPERTY: case PAYLOAD_PROPERTY: // no actual wrapper; included within Object itself break; case WRAPPER_OBJECT: default: // should never occur but... writeEndObject(); break; } } return typeIdDef; } /* /********************************************************** /* Public API, write methods, serializing Java objects /********************************************************** */ /** * Method for writing given Java object (POJO) as Json. * Exactly how the object gets written depends on object * in question (ad on codec, its configuration); for most * beans it will result in JSON Object, but for others JSON * Array, or String or numeric value (and for nulls, JSON * null literal. * NOTE: generator must have its object codec * set to non-null value; for generators created by a mapping * factory this is the case, for others not. */ public abstract void writeObject(Object pojo) throws IOException; /** * Method for writing given JSON tree (expressed as a tree * where given JsonNode is the root) using this generator. * This will generally just call * {@link #writeObject} with given node, but is added * for convenience and to make code more explicit in cases * where it deals specifically with trees. */ public abstract void writeTree(TreeNode rootNode) throws IOException; /* /********************************************************** /* Public API, convenience field write methods /********************************************************** */ /** * Convenience method for outputting a field entry ("member") * that has a String value. Equivalent to: *

     *  writeFieldName(fieldName);
     *  writeString(value);
     *
*

* Note: many performance-sensitive implementations override this method */ public void writeStringField(String fieldName, String value) throws IOException { writeFieldName(fieldName); writeString(value); } /** * Convenience method for outputting a field entry ("member") * that has a boolean value. Equivalent to: *

     *  writeFieldName(fieldName);
     *  writeBoolean(value);
     *
*/ public final void writeBooleanField(String fieldName, boolean value) throws IOException { writeFieldName(fieldName); writeBoolean(value); } /** * Convenience method for outputting a field entry ("member") * that has JSON literal value null. Equivalent to: *
     *  writeFieldName(fieldName);
     *  writeNull();
     *
*/ public final void writeNullField(String fieldName) throws IOException { writeFieldName(fieldName); writeNull(); } /** * Convenience method for outputting a field entry ("member") * that has the specified numeric value. Equivalent to: *
     *  writeFieldName(fieldName);
     *  writeNumber(value);
     *
*/ public final void writeNumberField(String fieldName, int value) throws IOException { writeFieldName(fieldName); writeNumber(value); } /** * Convenience method for outputting a field entry ("member") * that has the specified numeric value. Equivalent to: *
     *  writeFieldName(fieldName);
     *  writeNumber(value);
     *
*/ public final void writeNumberField(String fieldName, long value) throws IOException { writeFieldName(fieldName); writeNumber(value); } /** * Convenience method for outputting a field entry ("member") * that has the specified numeric value. Equivalent to: *
     *  writeFieldName(fieldName);
     *  writeNumber(value);
     *
*/ public final void writeNumberField(String fieldName, double value) throws IOException { writeFieldName(fieldName); writeNumber(value); } /** * Convenience method for outputting a field entry ("member") * that has the specified numeric value. Equivalent to: *
     *  writeFieldName(fieldName);
     *  writeNumber(value);
     *
*/ public final void writeNumberField(String fieldName, float value) throws IOException { writeFieldName(fieldName); writeNumber(value); } /** * Convenience method for outputting a field entry ("member") * that has the specified numeric value. * Equivalent to: *
     *  writeFieldName(fieldName);
     *  writeNumber(value);
     *
*/ public final void writeNumberField(String fieldName, BigDecimal value) throws IOException { writeFieldName(fieldName); writeNumber(value); } /** * Convenience method for outputting a field entry ("member") * that contains specified data in base64-encoded form. * Equivalent to: *
     *  writeFieldName(fieldName);
     *  writeBinary(value);
     *
*/ public final void writeBinaryField(String fieldName, byte[] data) throws IOException { writeFieldName(fieldName); writeBinary(data); } /** * Convenience method for outputting a field entry ("member") * (that will contain a JSON Array value), and the START_ARRAY marker. * Equivalent to: *
     *  writeFieldName(fieldName);
     *  writeStartArray();
     *
*

* Note: caller still has to take care to close the array * (by calling {#link #writeEndArray}) after writing all values * of the value Array. */ public final void writeArrayFieldStart(String fieldName) throws IOException { writeFieldName(fieldName); writeStartArray(); } /** * Convenience method for outputting a field entry ("member") * (that will contain an Object value), and the START_OBJECT marker. * Equivalent to: *

     *  writeFieldName(fieldName);
     *  writeStartObject();
     *
*

* Note: caller still has to take care to close the Object * (by calling {#link #writeEndObject}) after writing all * entries of the value Object. */ public final void writeObjectFieldStart(String fieldName) throws IOException { writeFieldName(fieldName); writeStartObject(); } /** * Convenience method for outputting a field entry ("member") * that has contents of specific Java object as its value. * Equivalent to: *

     *  writeFieldName(fieldName);
     *  writeObject(pojo);
     *
*/ public final void writeObjectField(String fieldName, Object pojo) throws IOException { writeFieldName(fieldName); writeObject(pojo); } /** * Method called to indicate that a property in this position was * skipped. It is usually only called for generators that return * false from {@link #canOmitFields()}. *

* Default implementation does nothing. * * @since 2.3 */ public void writeOmittedField(String fieldName) throws IOException { } /* /********************************************************** /* Public API, copy-through methods /********************************************************** */ /** * Method for copying contents of the current event that * the given parser instance points to. * Note that the method will not copy any other events, * such as events contained within JSON Array or Object structures. *

* Calling this method will not advance the given * parser, although it may cause parser to internally process * more data (if it lazy loads contents of value events, for example) */ public void copyCurrentEvent(JsonParser p) throws IOException { JsonToken t = p.currentToken(); final int token = (t == null) ? ID_NOT_AVAILABLE : t.id(); switch (token) { case ID_NOT_AVAILABLE: _reportError("No current event to copy"); break; // never gets here case ID_START_OBJECT: writeStartObject(); break; case ID_END_OBJECT: writeEndObject(); break; case ID_START_ARRAY: writeStartArray(); break; case ID_END_ARRAY: writeEndArray(); break; case ID_FIELD_NAME: writeFieldName(p.getCurrentName()); break; case ID_STRING: if (p.hasTextCharacters()) { writeString(p.getTextCharacters(), p.getTextOffset(), p.getTextLength()); } else { writeString(p.getText()); } break; case ID_NUMBER_INT: { NumberType n = p.getNumberType(); if (n == NumberType.INT) { writeNumber(p.getIntValue()); } else if (n == NumberType.BIG_INTEGER) { writeNumber(p.getBigIntegerValue()); } else { writeNumber(p.getLongValue()); } break; } case ID_NUMBER_FLOAT: { NumberType n = p.getNumberType(); if (n == NumberType.BIG_DECIMAL) { writeNumber(p.getDecimalValue()); } else if (n == NumberType.FLOAT) { writeNumber(p.getFloatValue()); } else { writeNumber(p.getDoubleValue()); } break; } case ID_TRUE: writeBoolean(true); break; case ID_FALSE: writeBoolean(false); break; case ID_NULL: writeNull(); break; case ID_EMBEDDED_OBJECT: writeObject(p.getEmbeddedObject()); break; default: throw new IllegalStateException("Internal error: unknown current token, "+t); } } /** * Method for copying contents of the current event * and following events that it encloses * the given parser instance points to. *

* So what constitutes enclosing? Here is the list of * events that have associated enclosed events that will * get copied: *

*

* After calling this method, parser will point to the * last event that was copied. This will either be * the event parser already pointed to (if there were no * enclosed events), or the last enclosed event copied. */ public void copyCurrentStructure(JsonParser p) throws IOException { JsonToken t = p.currentToken(); // Let's handle field-name separately first int id = (t == null) ? ID_NOT_AVAILABLE : t.id(); if (id == ID_FIELD_NAME) { writeFieldName(p.getCurrentName()); t = p.nextToken(); id = (t == null) ? ID_NOT_AVAILABLE : t.id(); // fall-through to copy the associated value } switch (id) { case ID_START_OBJECT: writeStartObject(); _copyCurrentContents(p); return; case ID_START_ARRAY: writeStartArray(); _copyCurrentContents(p); return; default: copyCurrentEvent(p); } } /** * @since 2.10 */ protected void _copyCurrentContents(JsonParser p) throws IOException { int depth = 1; JsonToken t; // Mostly copied from `copyCurrentEvent()`, but with added nesting counts while ((t = p.nextToken()) != null) { switch (t.id()) { case ID_FIELD_NAME: writeFieldName(p.getCurrentName()); break; case ID_START_ARRAY: writeStartArray(); ++depth; break; case ID_START_OBJECT: writeStartObject(); ++depth; break; case ID_END_ARRAY: writeEndArray(); if (--depth == 0) { return; } break; case ID_END_OBJECT: writeEndObject(); if (--depth == 0) { return; } break; case ID_STRING: if (p.hasTextCharacters()) { writeString(p.getTextCharacters(), p.getTextOffset(), p.getTextLength()); } else { writeString(p.getText()); } break; case ID_NUMBER_INT: { NumberType n = p.getNumberType(); if (n == NumberType.INT) { writeNumber(p.getIntValue()); } else if (n == NumberType.BIG_INTEGER) { writeNumber(p.getBigIntegerValue()); } else { writeNumber(p.getLongValue()); } break; } case ID_NUMBER_FLOAT: { NumberType n = p.getNumberType(); if (n == NumberType.BIG_DECIMAL) { writeNumber(p.getDecimalValue()); } else if (n == NumberType.FLOAT) { writeNumber(p.getFloatValue()); } else { writeNumber(p.getDoubleValue()); } break; } case ID_TRUE: writeBoolean(true); break; case ID_FALSE: writeBoolean(false); break; case ID_NULL: writeNull(); break; case ID_EMBEDDED_OBJECT: writeObject(p.getEmbeddedObject()); break; default: throw new IllegalStateException("Internal error: unknown current token, "+t); } } } /* /********************************************************** /* Public API, context access /********************************************************** */ /** * @return Context object that can give information about logical * position within generated json content. */ public abstract JsonStreamContext getOutputContext(); /* /********************************************************** /* Public API, buffer handling /********************************************************** */ /** * Method called to flush any buffered content to the underlying * target (output stream, writer), and to flush the target itself * as well. */ @Override public abstract void flush() throws IOException; /** * Method that can be called to determine whether this generator * is closed or not. If it is closed, no more output can be done. */ public abstract boolean isClosed(); /* /********************************************************** /* Closeable implementation /********************************************************** */ /** * Method called to close this generator, so that no more content * can be written. *

* Whether the underlying target (stream, writer) gets closed depends * on whether this generator either manages the target (i.e. is the * only one with access to the target -- case if caller passes a * reference to the resource such as File, but not stream); or * has feature {@link Feature#AUTO_CLOSE_TARGET} enabled. * If either of above is true, the target is also closed. Otherwise * (not managing, feature not enabled), target is not closed. */ @Override public abstract void close() throws IOException; /* /********************************************************** /* Helper methods for sub-classes /********************************************************** */ /** * Helper method used for constructing and throwing * {@link JsonGenerationException} with given base message. *

* Note that sub-classes may override this method to add more detail * or use a {@link JsonGenerationException} sub-class. */ protected void _reportError(String msg) throws JsonGenerationException { throw new JsonGenerationException(msg, this); } protected final void _throwInternal() { VersionUtil.throwInternal(); } protected void _reportUnsupportedOperation() { throw new UnsupportedOperationException("Operation not supported by generator of type "+getClass().getName()); } /** * @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. * * @param value Non-null value to write */ protected void _writeSimpleObject(Object value) throws IOException { /* 31-Dec-2009, tatu: Actually, we could just handle some basic * types even without codec. This can improve interoperability, * and specifically help with TokenBuffer. */ if (value == null) { writeNull(); return; } if (value instanceof String) { writeString((String) value); return; } if (value instanceof Number) { Number n = (Number) value; if (n instanceof Integer) { writeNumber(n.intValue()); return; } else if (n instanceof Long) { writeNumber(n.longValue()); return; } else if (n instanceof Double) { writeNumber(n.doubleValue()); return; } else if (n instanceof Float) { writeNumber(n.floatValue()); return; } else if (n instanceof Short) { writeNumber(n.shortValue()); return; } else if (n instanceof Byte) { writeNumber(n.byteValue()); return; } else if (n instanceof BigInteger) { writeNumber((BigInteger) n); return; } else if (n instanceof BigDecimal) { writeNumber((BigDecimal) n); return; // then Atomic types } else if (n instanceof AtomicInteger) { writeNumber(((AtomicInteger) n).get()); return; } else if (n instanceof AtomicLong) { writeNumber(((AtomicLong) n).get()); return; } } else if (value instanceof byte[]) { writeBinary((byte[]) value); return; } else if (value instanceof Boolean) { writeBoolean((Boolean) value); return; } else if (value instanceof AtomicBoolean) { writeBoolean(((AtomicBoolean) value).get()); return; } throw new IllegalStateException("No ObjectCodec defined for the generator, can only serialize simple wrapper types (type passed " +value.getClass().getName()+")"); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonLocation.java000066400000000000000000000156501356164247300316130ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; import java.nio.charset.Charset; /** * Object that encapsulates Location information used for reporting * parsing (or potentially generation) errors, as well as current location * within input streams. */ public class JsonLocation implements java.io.Serializable { private static final long serialVersionUID = 1L; /** * Include at most first 500 characters/bytes from contents; should be enough * to give context, but not cause unfortunate side effects in things like * logs. * * @since 2.9 */ public static final int MAX_CONTENT_SNIPPET = 500; /** * Shared immutable "N/A location" that can be returned to indicate * that no location information is available. *

* NOTE: before 2.9, Location was given as String "N/A"; with 2.9 it was * removed so that source should be indicated as "UNKNOWN". */ public final static JsonLocation NA = new JsonLocation(null, -1L, -1L, -1, -1); protected final long _totalBytes; protected final long _totalChars; protected final int _lineNr; protected final int _columnNr; /** * Displayable description for input source: file path, URL. *

* NOTE: transient since 2.2 so that Location itself is Serializable. */ final transient Object _sourceRef; public JsonLocation(Object srcRef, long totalChars, int lineNr, int colNr) { /* Unfortunately, none of legal encodings are straight single-byte * encodings. Could determine offset for UTF-16/UTF-32, but the * most important one is UTF-8... * so for now, we'll just not report any real byte count */ this(srcRef, -1L, totalChars, lineNr, colNr); } public JsonLocation(Object sourceRef, long totalBytes, long totalChars, int lineNr, int columnNr) { _sourceRef = sourceRef; _totalBytes = totalBytes; _totalChars = totalChars; _lineNr = lineNr; _columnNr = columnNr; } /** * Reference to the original resource being read, if one available. * For example, when a parser has been constructed by passing * a {@link java.io.File} instance, this method would return * that File. Will return null if no such reference is available, * for example when {@link java.io.InputStream} was used to * construct the parser instance. */ public Object getSourceRef() { return _sourceRef; } /** * @return Line number of the location (1-based) */ public int getLineNr() { return _lineNr; } /** * @return Column number of the location (1-based) */ public int getColumnNr() { return _columnNr; } /** * @return Character offset within underlying stream, reader or writer, * if available; -1 if not. */ public long getCharOffset() { return _totalChars; } /** * @return Byte offset within underlying stream, reader or writer, * if available; -1 if not. */ public long getByteOffset() { return _totalBytes; } /** * Accessor for getting a textual description of source reference * (Object returned by {@link #getSourceRef()}), as included in * description returned by {@link #toString()}. *

* NOTE: not added as a "getter" to prevent it from getting serialized. * * @since 2.9 */ public String sourceDescription() { return _appendSourceDesc(new StringBuilder(100)).toString(); } /* /********************************************************** /* Std method overrides /********************************************************** */ @Override public int hashCode() { int hash = (_sourceRef == null) ? 1 : _sourceRef.hashCode(); hash ^= _lineNr; hash += _columnNr; hash ^= (int) _totalChars; hash += (int) _totalBytes; return hash; } @Override public boolean equals(Object other) { if (other == this) return true; if (other == null) return false; if (!(other instanceof JsonLocation)) return false; JsonLocation otherLoc = (JsonLocation) other; if (_sourceRef == null) { if (otherLoc._sourceRef != null) return false; } else if (!_sourceRef.equals(otherLoc._sourceRef)) return false; return (_lineNr == otherLoc._lineNr) && (_columnNr == otherLoc._columnNr) && (_totalChars == otherLoc._totalChars) && (getByteOffset() == otherLoc.getByteOffset()) ; } @Override public String toString() { StringBuilder sb = new StringBuilder(80); sb.append("[Source: "); _appendSourceDesc(sb); sb.append("; line: "); sb.append(_lineNr); sb.append(", column: "); sb.append(_columnNr); sb.append(']'); return sb.toString(); } protected StringBuilder _appendSourceDesc(StringBuilder sb) { final Object srcRef = _sourceRef; if (srcRef == null) { sb.append("UNKNOWN"); return sb; } // First, figure out what name to use as source type Class srcType = (srcRef instanceof Class) ? ((Class) srcRef) : srcRef.getClass(); String tn = srcType.getName(); // standard JDK types without package if (tn.startsWith("java.")) { tn = srcType.getSimpleName(); } else if (srcRef instanceof byte[]) { // then some other special cases tn = "byte[]"; } else if (srcRef instanceof char[]) { tn = "char[]"; } sb.append('(').append(tn).append(')'); // and then, include (part of) contents for selected types: int len; String charStr = " chars"; if (srcRef instanceof CharSequence) { CharSequence cs = (CharSequence) srcRef; len = cs.length(); len -= _append(sb, cs.subSequence(0, Math.min(len, MAX_CONTENT_SNIPPET)).toString()); } else if (srcRef instanceof char[]) { char[] ch = (char[]) srcRef; len = ch.length; len -= _append(sb, new String(ch, 0, Math.min(len, MAX_CONTENT_SNIPPET))); } else if (srcRef instanceof byte[]) { byte[] b = (byte[]) srcRef; int maxLen = Math.min(b.length, MAX_CONTENT_SNIPPET); _append(sb, new String(b, 0, maxLen, Charset.forName("UTF-8"))); len = b.length - maxLen; charStr = " bytes"; } else { len = 0; } if (len > 0) { sb.append("[truncated ").append(len).append(charStr).append(']'); } return sb; } private int _append(StringBuilder sb, String content) { sb.append('"').append(content).append('"'); return content.length(); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonParseException.java000066400000000000000000000061161356164247300327710ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; import com.fasterxml.jackson.core.exc.StreamReadException; 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) * is encountered. */ public class JsonParseException extends StreamReadException { private static final long serialVersionUID = 2L; // 2.7 @Deprecated // since 2.7 public JsonParseException(String msg, JsonLocation loc) { super(msg, loc, null); } @Deprecated // since 2.7 public JsonParseException(String msg, JsonLocation loc, Throwable root) { super(msg, loc, root); } /** * Constructor that uses current parsing location as location, and * sets processor (accessible via {@link #getProcessor()}) to * specified parser. * * @since 2.7 */ public JsonParseException(JsonParser p, String msg) { super(p, msg); } /** * @since 2.7 */ public JsonParseException(JsonParser p, String msg, Throwable root) { super(p, msg, root); } /** * @since 2.7 */ public JsonParseException(JsonParser p, String msg, JsonLocation loc) { super(p, msg, loc); } /** * @since 2.7 */ public JsonParseException(JsonParser p, String msg, JsonLocation loc, Throwable root) { super(msg, loc, root); } /** * Fluent method that may be used to assign originating {@link JsonParser}, * to be accessed using {@link #getProcessor()}. *

* NOTE: `this` instance is modified and no new instance is constructed. * * @since 2.7 */ @Override public JsonParseException withParser(JsonParser p) { _processor = p; return this; } /** * Fluent method that may be used to assign payload to this exception, * to let recipient access it for diagnostics purposes. *

* NOTE: `this` instance is modified and no new instance is constructed. * * @since 2.8 */ @Override public JsonParseException withRequestPayload(RequestPayload p) { _requestPayload = p; return this; } // NOTE: overloaded in 2.10 just to retain binary compatibility with 2.9 (remove from 3.0) @Override public JsonParser getProcessor() { return super.getProcessor(); } // NOTE: overloaded in 2.10 just to retain binary compatibility with 2.9 (remove from 3.0) @Override public RequestPayload getRequestPayload() { return super.getRequestPayload(); } // NOTE: overloaded in 2.10 just to retain binary compatibility with 2.9 (remove from 3.0) @Override public String getRequestPayloadAsString() { return super.getRequestPayloadAsString(); } // NOTE: overloaded in 2.10 just to retain binary compatibility with 2.9 (remove from 3.0) @Override public String getMessage() { return super.getMessage(); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonParser.java000066400000000000000000002172111356164247300312740ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; import java.io.*; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Iterator; import com.fasterxml.jackson.core.async.NonBlockingInputFeeder; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.util.RequestPayload; /** * Base class that defines public API for reading JSON content. * Instances are created using factory methods of * a {@link JsonFactory} instance. * * @author Tatu Saloranta */ public abstract class JsonParser implements Closeable, Versioned { private final static int MIN_BYTE_I = (int) Byte.MIN_VALUE; // as per [JACKSON-804], allow range up to and including 255 private final static int MAX_BYTE_I = (int) 255; private final static int MIN_SHORT_I = (int) Short.MIN_VALUE; private final static int MAX_SHORT_I = (int) Short.MAX_VALUE; /** * Enumeration of possible "native" (optimal) types that can be * used for numbers. */ public enum NumberType { INT, LONG, BIG_INTEGER, FLOAT, DOUBLE, BIG_DECIMAL }; /** * Enumeration that defines all on/off features for parsers. */ public enum Feature { // // // Low-level I/O handling features: /** * Feature that determines whether parser will automatically * close underlying input source that is NOT owned by the * parser. If disabled, calling application has to separately * close the underlying {@link InputStream} and {@link Reader} * instances used to create the parser. If enabled, parser * will handle closing, as long as parser itself gets closed: * this happens when end-of-input is encountered, or parser * is closed by a call to {@link JsonParser#close}. *

* Feature is enabled by default. */ AUTO_CLOSE_SOURCE(true), // // // Support for non-standard data format constructs /** * Feature that determines whether parser will allow use * of Java/C++ style comments (both '/'+'*' and * '//' varieties) within parsed content or not. *

* Since JSON specification does not mention comments as legal * construct, * this is a non-standard feature; however, in the wild * this is extensively used. As such, feature is * disabled by default for parsers and must be * explicitly enabled. *

* NOTE: while not technically deprecated, since 2.10 recommended to use * {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_JAVA_COMMENTS} instead. */ ALLOW_COMMENTS(false), /** * Feature that determines whether parser will allow use * of YAML comments, ones starting with '#' and continuing * until the end of the line. This commenting style is common * with scripting languages as well. *

* Since JSON specification does not mention comments as legal * construct, * this is a non-standard feature. As such, feature is * disabled by default for parsers and must be * explicitly enabled. *

* NOTE: while not technically deprecated, since 2.10 recommended to use * {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_YAML_COMMENTS} instead. */ ALLOW_YAML_COMMENTS(false), /** * Feature that determines whether parser will allow use * of unquoted field names (which is allowed by Javascript, * but not by JSON specification). *

* Since JSON specification requires use of double quotes for * field names, * this is a non-standard feature, and as such disabled by default. *

* NOTE: while not technically deprecated, since 2.10 recommended to use * {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_UNQUOTED_FIELD_NAMES} instead. */ ALLOW_UNQUOTED_FIELD_NAMES(false), /** * Feature that determines whether parser will allow use * of single quotes (apostrophe, character '\'') for * quoting Strings (names and String values). If so, * this is in addition to other acceptable markers. * but not by JSON specification). *

* Since JSON specification requires use of double quotes for * field names, * this is a non-standard feature, and as such disabled by default. *

* NOTE: while not technically deprecated, since 2.10 recommended to use * {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_SINGLE_QUOTES} instead. */ ALLOW_SINGLE_QUOTES(false), /** * Feature that determines whether parser will allow * JSON Strings to contain unquoted control characters * (ASCII characters with value less than 32, including * tab and line feed characters) or not. * If feature is set false, an exception is thrown if such a * character is encountered. *

* Since JSON specification requires quoting for all control characters, * this is a non-standard feature, and as such disabled by default. * * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_UNESCAPED_CONTROL_CHARS} instead */ @Deprecated ALLOW_UNQUOTED_CONTROL_CHARS(false), /** * Feature that can be enabled to accept quoting of all character * using backslash quoting mechanism: if not enabled, only characters * that are explicitly listed by JSON specification can be thus * escaped (see JSON spec for small list of these characters) *

* Since JSON specification requires quoting for all control characters, * this is a non-standard feature, and as such disabled by default. * * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER} instead */ @Deprecated ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(false), /** * Feature that determines whether parser will allow * JSON integral numbers to start with additional (ignorable) * zeroes (like: 000001). If enabled, no exception is thrown, and extra * nulls are silently ignored (and not included in textual representation * exposed via {@link JsonParser#getText}). *

* Since JSON specification does not allow leading zeroes, * this is a non-standard feature, and as such disabled by default. * * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_LEADING_ZEROS_FOR_NUMBERS} instead */ @Deprecated ALLOW_NUMERIC_LEADING_ZEROS(false), /** * Feature that allows parser to recognize set of * "Not-a-Number" (NaN) tokens as legal floating number * values (similar to how many other data formats and * programming language source code allows it). * Specific subset contains values that * XML Schema * (see section 3.2.4.1, Lexical Representation) * allows (tokens are quoted contents, not including quotes): *

*

* Since JSON specification does not allow use of such values, * this is a non-standard feature, and as such disabled by default. * * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_NON_NUMERIC_NUMBERS} instead */ @Deprecated ALLOW_NON_NUMERIC_NUMBERS(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. *

* For example, enabling this feature will represent a JSON array ["value1",,"value3",] * as ["value1", null, "value3", null] *

* Since the JSON specification does not allow missing values this is a non-compliant JSON * feature and is disabled by default. * * @since 2.8 * * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_MISSING_VALUES} instead */ @Deprecated ALLOW_MISSING_VALUES(false), /** * Feature that determines whether {@link JsonParser} will allow for a single trailing * comma following the final value (in an Array) or member (in an Object). These commas * will simply be ignored. *

* For example, when this feature is enabled, [true,true,] is equivalent to * [true, true] and {"a": true,} is equivalent to * {"a": true}. *

* When combined with ALLOW_MISSING_VALUES, this feature takes priority, and * the final trailing comma in an array declaration does not imply a missing * (null) value. For example, when both ALLOW_MISSING_VALUES * and ALLOW_TRAILING_COMMA are enabled, [true,true,] is * equivalent to [true, true], and [true,true,,] is equivalent to * [true, true, null]. *

* Since the JSON specification does not permit trailing commas, this is a non-standard * feature, and as such disabled by default. * * @since 2.9 * * @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_TRAILING_COMMA} instead */ @Deprecated ALLOW_TRAILING_COMMA(false), // // // Validity checks /** * Feature that determines whether {@link JsonParser} will explicitly * check that no duplicate JSON Object field names are encountered. * If enabled, parser will check all names within context and report * duplicates by throwing a {@link JsonParseException}; if disabled, * parser will not do such checking. Assumption in latter case is * that caller takes care of handling duplicates at a higher level: * data-binding, for example, has features to specify detection to * be done there. *

* Note that enabling this feature will incur performance overhead * due to having to store and check additional information: this typically * adds 20-30% to execution time for basic parsing. * * @since 2.3 */ STRICT_DUPLICATE_DETECTION(false), /** * Feature that determines what to do if the underlying data format requires knowledge * of all properties to decode (usually via a Schema), and if no definition is * found for a property that input content contains. * Typically most textual data formats do NOT require schema information (although * some do, such as CSV), whereas many binary data formats do require definitions * (such as Avro, protobuf), although not all (Smile, CBOR, BSON and MessagePack do not). * Further note that some formats that do require schema information will not be able * to ignore undefined properties: for example, Avro is fully positional and there is * no possibility of undefined data. This leaves formats like Protobuf that have identifiers * that may or may not map; and as such Protobuf format does make use of this feature. *

* Note that support for this feature is implemented by individual data format * module, if (and only if) it makes sense for the format in question. For JSON, * for example, this feature has no effect as properties need not be pre-defined. *

* Feature is disabled by default, meaning that if the underlying data format * requires knowledge of all properties to output, attempts to read an unknown * property will result in a {@link JsonProcessingException} * * @since 2.6 */ IGNORE_UNDEFINED(false), // // // Other /** * Feature that determines whether {@link JsonLocation} instances should be constructed * with reference to source or not. If source reference is included, its type and contents * are included when `toString()` method is called (most notably when printing out parse * exception with that location information). If feature is disabled, no source reference * is passed and source is only indicated as "UNKNOWN". *

* Most common reason for disabling this feature is to avoid leaking information about * internal information; this may be done for security reasons. * Note that even if source reference is included, only parts of contents are usually * printed, and not the whole contents. Further, many source reference types can not * necessarily access contents (like streams), so only type is indicated, not contents. *

* Feature is enabled by default, meaning that "source reference" information is passed * and some or all of the source content may be included in {@link JsonLocation} information * constructed either when requested explicitly, or when needed for an exception. * * @since 2.9 */ INCLUDE_SOURCE_IN_LOCATION(true), ; /** * Whether feature is enabled or disabled by default. */ private final boolean _defaultState; private final int _mask; /** * Method that calculates bit set (flags) of all features that * are enabled by default. */ public static int collectDefaults() { int flags = 0; for (Feature f : values()) { if (f.enabledByDefault()) { flags |= f.getMask(); } } return flags; } private Feature(boolean defaultState) { _mask = (1 << ordinal()); _defaultState = defaultState; } public boolean enabledByDefault() { return _defaultState; } /** * @since 2.3 */ public boolean enabledIn(int flags) { return (flags & _mask) != 0; } public int getMask() { return _mask; } } /* /********************************************************** /* Minimal configuration state /********************************************************** */ /** * Bit flag composed of bits that indicate which * {@link com.fasterxml.jackson.core.JsonParser.Feature}s * 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; /* /********************************************************** /* Construction, configuration, initialization /********************************************************** */ protected JsonParser() { } protected JsonParser(int features) { _features = features; } /** * Accessor for {@link ObjectCodec} associated with this * parser, if any. Codec is used by {@link #readValueAs(Class)} * method (and its variants). */ public abstract ObjectCodec getCodec(); /** * Setter that allows defining {@link ObjectCodec} associated with this * parser, if any. Codec is used by {@link #readValueAs(Class)} * method (and its variants). */ public abstract void setCodec(ObjectCodec c); /** * Method that can be used to get access to object that is used * to access input being parsed; this is usually either * {@link InputStream} or {@link Reader}, depending on what * parser was constructed with. * Note that returned value may be null in some cases; including * case where parser implementation does not want to exposed raw * source to caller. * In cases where input has been decorated, object returned here * is the decorated version; this allows some level of interaction * between users of parser and decorator object. *

* In general use of this accessor should be considered as * "last effort", i.e. only used if no other mechanism is applicable. */ public Object getInputSource() { return null; } /** * Helper method, usually equivalent to: * * getParsingContext().getCurrentValue(); * *

* Note that "current value" is NOT populated (or used) by Streaming parser; * it is only used by higher-level data-binding functionality. * The reason it is included here is that it can be stored and accessed hierarchically, * and gets passed through data-binding. * * @since 2.5 */ public Object getCurrentValue() { JsonStreamContext ctxt = getParsingContext(); return (ctxt == null) ? null : ctxt.getCurrentValue(); } /** * Helper method, usually equivalent to: * * getParsingContext().setCurrentValue(v); * * * @since 2.5 */ public void setCurrentValue(Object v) { JsonStreamContext ctxt = getParsingContext(); if (ctxt != null) { ctxt.setCurrentValue(v); } } /** * 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 * has been created. * Note that not all parsers support schemas; and those that do usually only * accept specific types of schemas: ones defined for data format parser can read. *

* If parser does not support specified schema, {@link UnsupportedOperationException} * is thrown. * * @param schema Schema to use * * @throws UnsupportedOperationException if parser does not support schema */ public void setSchema(FormatSchema schema) { throw new UnsupportedOperationException("Parser of type "+getClass().getName()+" does not support schema of type '" +schema.getSchemaType()+"'"); } /** * Method for accessing Schema that this parser uses, if any. * Default implementation returns null. * * @since 2.1 */ public FormatSchema getSchema() { return null; } /** * Method that can be used to verify that given schema can be used with * this parser (using {@link #setSchema}). * * @param schema Schema to check * * @return True if this parser can use given schema; false if not */ public boolean canUseSchema(FormatSchema schema) { return false; } /* /********************************************************** /* Capability introspection /********************************************************** */ /** * Method that can be called to determine if a custom * {@link ObjectCodec} is needed for binding data parsed * using {@link JsonParser} constructed by this factory * (which typically also implies the same for serialization * with {@link JsonGenerator}). * * @return True if custom codec is needed with parsers and * generators created by this factory; false if a general * {@link ObjectCodec} is enough * * @since 2.1 */ public boolean requiresCustomCodec() { return false;} /** * Method that can be called to determine if this parser instance * uses non-blocking ("asynchronous") input access for decoding or not. * Access mode is determined by earlier calls via {@link JsonFactory}; * it may not be changed after construction. *

* If non-blocking decoding is u (@code true}, it is possible to call * {@link #getNonBlockingInputFeeder()} to obtain object to use * for feeding input; otherwise (false returned) * input is read by blocking * * @since 2.9 */ public boolean canParseAsync() { return false; } /** * Method that will either return a feeder instance (if parser uses * non-blocking, aka asynchronous access); or null for * parsers that use blocking I/O. * * @since 2.9 */ public NonBlockingInputFeeder getNonBlockingInputFeeder() { return null; } /* /********************************************************** /* Versioned /********************************************************** */ /** * Accessor for getting version of the core package, given a parser instance. * Left for sub-classes to implement. */ @Override public abstract Version version(); /* /********************************************************** /* Closeable implementation /********************************************************** */ /** * Closes the parser so that no further iteration or data access * can be made; will also close the underlying input source * if parser either owns the input source, or feature * {@link Feature#AUTO_CLOSE_SOURCE} is enabled. * Whether parser owns the input source depends on factory * method that was used to construct instance (so check * {@link com.fasterxml.jackson.core.JsonFactory} for details, * but the general * idea is that if caller passes in closable resource (such * as {@link InputStream} or {@link Reader}) parser does NOT * own the source; but if it passes a reference (such as * {@link java.io.File} or {@link java.net.URL} and creates * stream or reader it does own them. */ @Override public abstract void close() throws IOException; /** * Method that can be called to determine whether this parser * is closed or not. If it is closed, no new tokens can be * retrieved by calling {@link #nextToken} (and the underlying * stream may be closed). Closing may be due to an explicit * call to {@link #close} or because parser has encountered * end of input. */ public abstract boolean isClosed(); /* /********************************************************** /* Public API, simple location, context accessors /********************************************************** */ /** * Method that can be used to access current parsing context reader * is in. There are 3 different types: root, array and object contexts, * with slightly different available information. Contexts are * hierarchically nested, and can be used for example for figuring * out part of the input document that correspond to specific * array or object (for highlighting purposes, or error reporting). * Contexts can also be used for simple xpath-like matching of * input, if so desired. */ public abstract JsonStreamContext getParsingContext(); /** * Method that return the starting location of the current * token; that is, position of the first character from input * that starts the current token. */ public abstract JsonLocation getTokenLocation(); /** * Method that returns location of the last processed character; * usually for error reporting purposes. */ public abstract JsonLocation getCurrentLocation(); /* /********************************************************** /* Buffer handling /********************************************************** */ /** * Method that can be called to push back any content that * has been read but not consumed by the parser. This is usually * done after reading all content of interest using parser. * Content is released by writing it to given stream if possible; * if underlying input is byte-based it can released, if not (char-based) * it can not. * * @return -1 if the underlying content source is not byte based * (that is, input can not be sent to {@link OutputStream}; * otherwise number of bytes released (0 if there was nothing to release) * * @throws IOException if write to stream threw exception */ public int releaseBuffered(OutputStream out) throws IOException { return -1; } /** * Method that can be called to push back any content that * has been read but not consumed by the parser. * This is usually * done after reading all content of interest using parser. * Content is released by writing it to given writer if possible; * if underlying input is char-based it can released, if not (byte-based) * it can not. * * @return -1 if the underlying content source is not char-based * (that is, input can not be sent to {@link Writer}; * otherwise number of chars released (0 if there was nothing to release) * * @throws IOException if write using Writer threw exception */ public int releaseBuffered(Writer w) throws IOException { return -1; } /* /*************************************************** /* Public API, configuration /*************************************************** */ /** * Method for enabling specified parser feature * (check {@link Feature} for list of features) */ public JsonParser enable(Feature f) { _features |= f.getMask(); return this; } /** * Method for disabling specified feature * (check {@link Feature} for list of features) */ public JsonParser disable(Feature f) { _features &= ~f.getMask(); return this; } /** * Method for enabling or disabling specified feature * (check {@link Feature} for list of features) */ public JsonParser configure(Feature f, boolean state) { if (state) enable(f); else disable(f); return this; } /** * Method for checking whether specified {@link Feature} is enabled. */ public boolean isEnabled(Feature f) { return f.enabledIn(_features); } /** * Method for checking whether specified {@link Feature} is enabled. * * @since 2.10 */ public boolean isEnabled(StreamReadFeature f) { return f.mappedFeature().enabledIn(_features); } /** * Bulk access method for getting state of all standard {@link Feature}s. * * @return Bit mask that defines current states of all standard {@link Feature}s. * * @since 2.3 */ public int getFeatureMask() { return _features; } /** * Bulk set method for (re)setting states of all standard {@link Feature}s * * @return This parser object, to allow chaining of calls * * @since 2.3 * * @deprecated Since 2.7, use {@link #overrideStdFeatures(int, int)} instead */ @Deprecated public JsonParser setFeatureMask(int mask) { _features = mask; return this; } /** * Bulk set method for (re)setting states of features specified by mask. * Functionally equivalent to * * int oldState = getFeatureMask(); * int newState = (oldState & ~mask) | (values & mask); * setFeatureMask(newState); * * but preferred as this lets caller more efficiently specify actual changes made. * * @param values Bit mask of set/clear state for features to change * @param mask Bit mask of features to change * * @since 2.6 */ public JsonParser overrideStdFeatures(int values, int mask) { int newState = (_features & ~mask) | (values & mask); return setFeatureMask(newState); } /** * Bulk access method for getting state of all {@link FormatFeature}s, format-specific * on/off configuration settings. * * @return Bit mask that defines current states of all standard {@link FormatFeature}s. * * @since 2.6 */ public int getFormatFeatures() { return 0; } /** * Bulk set method for (re)setting states of {@link FormatFeature}s, * by specifying values (set / clear) along with a mask, to determine * which features to change, if any. *

* Default implementation will simply throw an exception to indicate that * the generator implementation does not support any {@link FormatFeature}s. * * @param values Bit mask of set/clear state for features to change * @param mask Bit mask of features to change * * @since 2.6 */ public JsonParser overrideFormatFeatures(int values, int mask) { // 08-Oct-2018, tatu: For 2.10 we actually do get `JsonReadFeature`s, although they // are (for 2.x only, not for 3.x) mapper to legacy settings. So do not freak out: // throw new IllegalArgumentException("No FormatFeatures defined for parser of type "+getClass().getName()); return this; } /* /********************************************************** /* Public API, traversal /********************************************************** */ /** * Main iteration method, which will advance stream enough * to determine type of the next token, if any. If none * remaining (stream has no content other than possible * white space before ending), null will be returned. * * @return Next token from the stream, if any found, or null * to indicate end-of-input */ public abstract JsonToken nextToken() throws IOException; /** * Iteration method that will advance stream enough * to determine type of the next token that is a value type * (including JSON Array and Object start/end markers). * Or put another way, nextToken() will be called once, * and if {@link JsonToken#FIELD_NAME} is returned, another * time to get the value for the field. * Method is most useful for iterating over value entries * of JSON objects; field name will still be available * by calling {@link #getCurrentName} when parser points to * the value. * * @return Next non-field-name token from the stream, if any found, * or null to indicate end-of-input (or, for non-blocking * parsers, {@link JsonToken#NOT_AVAILABLE} if no tokens were * available yet) */ public abstract JsonToken nextValue() throws IOException; /** * Method that fetches next token (as if calling {@link #nextToken}) and * verifies whether it is {@link JsonToken#FIELD_NAME} with specified name * and returns result of that comparison. * It is functionally equivalent to: *

     *  return (nextToken() == JsonToken.FIELD_NAME) && str.getValue().equals(getCurrentName());
     *
* but may be faster for parser to verify, and can therefore be used if caller * expects to get such a property name from input next. * * @param str Property name to compare next token to (if next token is * JsonToken.FIELD_NAME) */ public boolean nextFieldName(SerializableString str) throws IOException { return (nextToken() == JsonToken.FIELD_NAME) && str.getValue().equals(getCurrentName()); } /** * Method that fetches next token (as if calling {@link #nextToken}) and * verifies whether it is {@link JsonToken#FIELD_NAME}; if it is, * returns same as {@link #getCurrentName()}, otherwise null. * * @since 2.5 */ public String nextFieldName() throws IOException { return (nextToken() == JsonToken.FIELD_NAME) ? getCurrentName() : null; } /** * Method that fetches next token (as if calling {@link #nextToken}) and * if it is {@link JsonToken#VALUE_STRING} returns contained String value; * otherwise returns null. * It is functionally equivalent to: *
     *  return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
     *
* 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 { return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null; } /** * Method that fetches next token (as if calling {@link #nextToken}) and * if it is {@link JsonToken#VALUE_NUMBER_INT} returns 32-bit int value; * otherwise returns specified default value * It is functionally equivalent to: *
     *  return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
     *
* but may be faster for parser to process, and can therefore be used if caller * expects to get an int value next from input. */ public int nextIntValue(int defaultValue) throws IOException { return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue; } /** * Method that fetches next token (as if calling {@link #nextToken}) and * if it is {@link JsonToken#VALUE_NUMBER_INT} returns 64-bit long value; * otherwise returns specified default value * It is functionally equivalent to: *
     *  return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
     *
* but may be faster for parser to process, and can therefore be used if caller * expects to get a long value next from input. */ public long nextLongValue(long defaultValue) throws IOException { return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue; } /** * Method that fetches next token (as if calling {@link #nextToken}) and * if it is {@link JsonToken#VALUE_TRUE} or {@link JsonToken#VALUE_FALSE} * returns matching Boolean value; otherwise return null. * It is functionally equivalent to: *
     *  JsonToken t = nextToken();
     *  if (t == JsonToken.VALUE_TRUE) return Boolean.TRUE;
     *  if (t == JsonToken.VALUE_FALSE) return Boolean.FALSE;
     *  return null;
     *
* but may be faster for parser to process, and can therefore be used if caller * expects to get a Boolean value next from input. */ public Boolean nextBooleanValue() throws IOException { JsonToken t = nextToken(); if (t == JsonToken.VALUE_TRUE) { return Boolean.TRUE; } if (t == JsonToken.VALUE_FALSE) { return Boolean.FALSE; } return null; } /** * Method that will skip all child tokens of an array or * object token that the parser currently points to, * iff stream points to * {@link JsonToken#START_OBJECT} or {@link JsonToken#START_ARRAY}. * If not, it will do nothing. * After skipping, stream will point to matching * {@link JsonToken#END_OBJECT} or {@link JsonToken#END_ARRAY} * (possibly skipping nested pairs of START/END OBJECT/ARRAY tokens * as well as value tokens). * The idea is that after calling this method, application * will call {@link #nextToken} to point to the next * available token, if any. */ 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). *

* 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 } /* /********************************************************** /* Public API, simple token id/type access /********************************************************** */ /** * Accessor to find which token parser currently points to, if any; * null will be returned if none. * If return value is non-null, data associated with the token * is available via other accessor methods. * * @return Type of the token this parser currently points to, * 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 JsonToken currentToken() { return getCurrentToken(); } /** * Method similar to {@link #getCurrentToken()} but that returns an * int instead of {@link JsonToken} (enum value). *

* Use of int directly is typically more efficient on switch statements, * so this method may be useful when building low-overhead codecs. * Note, however, that effect may not be big enough to matter: make sure * to profile performance before deciding to use this method. * * @since 2.8 * * @return int 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(); /** * Method for checking whether parser currently points to * a token (and data for that token is available). * Equivalent to check for parser.getCurrentToken() != null. * * @return True if the parser just returned a valid * token via {@link #nextToken}; false otherwise (parser * was just constructed, encountered end-of-input * and returned null from {@link #nextToken}, or the token * has been consumed) */ public abstract boolean hasCurrentToken(); /** * Method that is functionally equivalent to: * * return currentTokenId() == id * * but may be more efficiently implemented. *

* Note that no traversal or conversion is performed; so in some * cases calling method like {@link #isExpectedStartArrayToken()} * is necessary instead. * * @since 2.5 */ public abstract boolean hasTokenId(int id); /** * Method that is functionally equivalent to: * * return currentToken() == t * * but may be more efficiently implemented. *

* Note that no traversal or conversion is performed; so in some * cases calling method like {@link #isExpectedStartArrayToken()} * is necessary instead. * * @since 2.6 */ public abstract boolean hasToken(JsonToken t); /** * Specialized accessor that can be used to verify that the current * token indicates start array (usually meaning that current token * is {@link JsonToken#START_ARRAY}) when start array is expected. * For some specialized parsers this can return true for other cases * as well; this is usually done to emulate arrays in cases underlying * format is ambiguous (XML, for example, has no format-level difference * between Objects and Arrays; it just has elements). *

* Default implementation is equivalent to: *

     *   currentToken() == JsonToken.START_ARRAY
     *
* but may be overridden by custom parser implementations. * * @return True if the current token can be considered as a * start-array marker (such {@link JsonToken#START_ARRAY}); * false if not. */ 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 currentToken() == JsonToken.START_OBJECT; } /** * Access for checking whether current token is a numeric value token, but * one that is of "not-a-number" (NaN) variety (including both "NaN" AND * positive/negative infinity!): not supported by all formats, * but often supported for {@link JsonToken#VALUE_NUMBER_FLOAT}. * NOTE: roughly equivalent to calling !Double.isFinite() * on value you would get from calling {@link #getDoubleValue()}. * * @since 2.9 */ public boolean isNaN() throws IOException { return false; } /* /********************************************************** /* Public API, token state overrides /********************************************************** */ /** * Method called to "consume" the current token by effectively * removing it so that {@link #hasCurrentToken} returns false, and * {@link #getCurrentToken} null). * Cleared token value can still be accessed by calling * {@link #getLastClearedToken} (if absolutely needed), but * usually isn't. *

* Method was added to be used by the optional data binder, since * it has to be able to consume last token used for binding (so that * it will not be used again). */ public abstract void clearCurrentToken(); /** * Method that can be called to get the last token that was * cleared using {@link #clearCurrentToken}. This is not necessarily * the latest token read. * Will return null if no tokens have been cleared, * or if parser has been closed. */ public abstract JsonToken getLastClearedToken(); /** * Method that can be used to change what is considered to be * the current (field) name. * May be needed to support non-JSON data formats or unusual binding * conventions; not needed for typical processing. *

* Note that use of this method should only be done as sort of last * resort, as it is a work-around for regular operation. * * @param name Name to use as the current name; may be null. */ public abstract void overrideCurrentName(String name); /* /********************************************************** /* Public API, access to token information, text /********************************************************** */ /** * Method that can be called to get the name associated with * the current token: for {@link JsonToken#FIELD_NAME}s it will * be the same as what {@link #getText} returns; * for field values it will be preceding field name; * and for others (array values, root-level values) null. */ public abstract String getCurrentName() throws IOException; // 15-Dec-2017, tatu: Forward-looking, added in 2.9.4 (and officially in 3.0) // to smooth upgrading public String currentName() throws IOException { return getCurrentName(); } /** * Method for accessing textual representation of the current token; * if no current token (before first call to {@link #nextToken}, or * after encountering end-of-input), returns null. * Method can be called for any token type. */ 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: *

     *  writer.write(parser.getText());
     *
* but should typically be more efficient as longer content does need to * be combined into a single String 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 * to contain this information. * Note, however, that: * *

* Note that caller MUST NOT modify the returned * character array in any way -- doing so may corrupt * current parser state and render parser instance useless. *

* The only reason to call this method (over {@link #getText}) * is to avoid construction of a String object (which * will make a copy of contents). */ public abstract char[] getTextCharacters() throws IOException; /** * Accessor used with {@link #getTextCharacters}, to know length * of String stored in returned buffer. * * @return Number of characters within buffer returned * by {@link #getTextCharacters} that are part of * textual content of the current token. */ public abstract int getTextLength() throws IOException; /** * Accessor used with {@link #getTextCharacters}, to know offset * of the first text content character within buffer. * * @return Offset of the first character within buffer returned * by {@link #getTextCharacters} that is part of * textual content of the current token. */ public abstract int getTextOffset() throws IOException; /** * Method that can be used to determine whether calling of * {@link #getTextCharacters} would be the most efficient * way to access textual content for the event parser currently * points to. *

* Default implementation simply returns false since only actual * implementation class has knowledge of its internal buffering * state. * Implementations are strongly encouraged to properly override * this method, to allow efficient copying of content by other * code. * * @return True if parser currently has character array that can * be efficiently returned via {@link #getTextCharacters}; false * means that it may or may not exist */ public abstract boolean hasTextCharacters(); /* /********************************************************** /* Public API, access to token information, numeric /********************************************************** */ /** * Generic number value accessor method that will work for * all kinds of numeric values. It will return the optimal * (simplest/smallest possible) wrapper object that can * express the numeric value just parsed. */ public abstract Number getNumberValue() throws IOException; /** * If current token is of type * {@link JsonToken#VALUE_NUMBER_INT} or * {@link JsonToken#VALUE_NUMBER_FLOAT}, returns * one of {@link NumberType} constants; otherwise returns null. */ public abstract NumberType getNumberType() throws IOException; /** * Numeric accessor that can be called when the current * token is of type {@link JsonToken#VALUE_NUMBER_INT} and * it can be expressed as a value of Java byte primitive type. * It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT}; * if so, it is equivalent to calling {@link #getDoubleValue} * and then casting; except for possible overflow/underflow * exception. *

* Note: if the resulting integer value falls outside range of * Java byte, a {@link JsonParseException} * will be thrown to indicate numeric overflow/underflow. */ public byte getByteValue() throws IOException { int value = getIntValue(); // So far so good: but does it fit? // [JACKSON-804]: Let's actually allow range of [-128, 255], as those are uniquely mapped // (instead of just signed range of [-128, 127]) if (value < MIN_BYTE_I || value > MAX_BYTE_I) { throw _constructError("Numeric value ("+getText()+") out of range of Java byte"); } return (byte) value; } /** * Numeric accessor that can be called when the current * token is of type {@link JsonToken#VALUE_NUMBER_INT} and * it can be expressed as a value of Java short primitive type. * It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT}; * if so, it is equivalent to calling {@link #getDoubleValue} * and then casting; except for possible overflow/underflow * exception. *

* Note: if the resulting integer value falls outside range of * Java short, a {@link JsonParseException} * will be thrown to indicate numeric overflow/underflow. */ public short getShortValue() throws IOException { int value = getIntValue(); if (value < MIN_SHORT_I || value > MAX_SHORT_I) { throw _constructError("Numeric value ("+getText()+") out of range of Java short"); } return (short) value; } /** * Numeric accessor that can be called when the current * token is of type {@link JsonToken#VALUE_NUMBER_INT} and * it can be expressed as a value of Java int primitive type. * It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT}; * if so, it is equivalent to calling {@link #getDoubleValue} * and then casting; except for possible overflow/underflow * exception. *

* Note: if the resulting integer value falls outside range of * Java int, a {@link JsonParseException} * may be thrown to indicate numeric overflow/underflow. */ public abstract int getIntValue() throws IOException; /** * Numeric accessor that can be called when the current * token is of type {@link JsonToken#VALUE_NUMBER_INT} and * it can be expressed as a Java long primitive type. * It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT}; * if so, it is equivalent to calling {@link #getDoubleValue} * and then casting to int; except for possible overflow/underflow * exception. *

* Note: if the token is an integer, but its value falls * outside of range of Java long, a {@link JsonParseException} * may be thrown to indicate numeric overflow/underflow. */ public abstract long getLongValue() throws IOException; /** * Numeric accessor that can be called when the current * token is of type {@link JsonToken#VALUE_NUMBER_INT} and * it can not be used as a Java long primitive type due to its * magnitude. * It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT}; * if so, it is equivalent to calling {@link #getDecimalValue} * and then constructing a {@link BigInteger} from that value. */ public abstract BigInteger getBigIntegerValue() throws IOException; /** * Numeric accessor that can be called when the current * token is of type {@link JsonToken#VALUE_NUMBER_FLOAT} and * it can be expressed as a Java float primitive type. * It can also be called for {@link JsonToken#VALUE_NUMBER_INT}; * if so, it is equivalent to calling {@link #getLongValue} * and then casting; except for possible overflow/underflow * exception. *

* Note: if the value falls * outside of range of Java float, a {@link JsonParseException} * will be thrown to indicate numeric overflow/underflow. */ public abstract float getFloatValue() throws IOException; /** * Numeric accessor that can be called when the current * token is of type {@link JsonToken#VALUE_NUMBER_FLOAT} and * it can be expressed as a Java double primitive type. * It can also be called for {@link JsonToken#VALUE_NUMBER_INT}; * if so, it is equivalent to calling {@link #getLongValue} * and then casting; except for possible overflow/underflow * exception. *

* Note: if the value falls * outside of range of Java double, a {@link JsonParseException} * will be thrown to indicate numeric overflow/underflow. */ public abstract double getDoubleValue() throws IOException; /** * Numeric accessor that can be called when the current * token is of type {@link JsonToken#VALUE_NUMBER_FLOAT} or * {@link JsonToken#VALUE_NUMBER_INT}. No under/overflow exceptions * are ever thrown. */ public abstract BigDecimal getDecimalValue() throws IOException; /* /********************************************************** /* Public API, access to token information, other /********************************************************** */ /** * Convenience accessor that can be called when the current * token is {@link JsonToken#VALUE_TRUE} or * {@link JsonToken#VALUE_FALSE}. *

* Note: if the token is not of above-mentioned boolean types, an integer, but its value falls * outside of range of Java long, a {@link JsonParseException} * may be thrown to indicate numeric overflow/underflow. */ public boolean getBooleanValue() throws IOException { 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)) .withRequestPayload(_requestPayload); } /** * Accessor that can be called if (and only if) the current token * is {@link JsonToken#VALUE_EMBEDDED_OBJECT}. For other token types, * null is returned. *

* 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). 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 Object getEmbeddedObject() throws IOException { return null; } /* /********************************************************** /* Public API, access to token information, binary /********************************************************** */ /** * Method that can be used to read (and consume -- results * may not be accessible using other methods after the call) * base64-encoded binary data * included in the current textual JSON value. * It works similar to getting String value via {@link #getText} * and decoding result (except for decoding part), * but should be significantly more performant. *

* Note that non-decoded textual contents of the current token * are not guaranteed to be accessible after this method * is called. Current implementation, for example, clears up * textual content during decoding. * Decoded binary content, however, will be retained until * parser is advanced to the next event. * * @param bv Expected variant of base64 encoded * content (see {@link Base64Variants} for definitions * of "standard" variants). * * @return Decoded binary data */ public abstract byte[] getBinaryValue(Base64Variant bv) throws IOException; /** * Convenience alternative to {@link #getBinaryValue(Base64Variant)} * that defaults to using * {@link Base64Variants#getDefaultVariant} as the default encoding. */ public byte[] getBinaryValue() throws IOException { return getBinaryValue(Base64Variants.getDefaultVariant()); } /** * Method that can be used as an alternative to {@link #getBigIntegerValue()}, * especially when value can be large. The main difference (beyond method * of returning content using {@link OutputStream} instead of as byte array) * is that content will NOT remain accessible after method returns: any content * processed will be consumed and is not buffered in any way. If caller needs * buffering, it has to implement it. * * @param out Output stream to use for passing decoded binary data * * @return Number of bytes that were decoded and written via {@link OutputStream} * * @since 2.1 */ public int readBinaryValue(OutputStream out) throws IOException { return readBinaryValue(Base64Variants.getDefaultVariant(), out); } /** * Similar to {@link #readBinaryValue(OutputStream)} but allows explicitly * specifying base64 variant to use. * * @param bv base64 variant to use * @param out Output stream to use for passing decoded binary data * * @return Number of bytes that were decoded and written via {@link OutputStream} * * @since 2.1 */ public int readBinaryValue(Base64Variant bv, OutputStream out) throws IOException { _reportUnsupportedOperation(); return 0; // never gets here } /* /********************************************************** /* Public API, access to token information, coercion/conversion /********************************************************** */ /** * Method that will try to convert value of current token to a * int. * Numbers are coerced using default Java rules; booleans convert to 0 (false) * and 1 (true), and Strings are parsed using default Java language integer * parsing rules. *

* If representation can not be converted to an int (including structured type * markers like start/end Object/Array) * default value of 0 will be returned; no exceptions are thrown. */ public int getValueAsInt() throws IOException { return getValueAsInt(0); } /** * Method that will try to convert value of current token to a * int. * Numbers are coerced using default Java rules; booleans convert to 0 (false) * and 1 (true), and Strings are parsed using default Java language integer * parsing rules. *

* If representation can not be converted to an int (including structured type * markers like start/end Object/Array) * specified def will be returned; no exceptions are thrown. */ public int getValueAsInt(int def) throws IOException { return def; } /** * Method that will try to convert value of current token to a * long. * Numbers are coerced using default Java rules; booleans convert to 0 (false) * and 1 (true), and Strings are parsed using default Java language integer * parsing rules. *

* If representation can not be converted to a long (including structured type * markers like start/end Object/Array) * default value of 0L will be returned; no exceptions are thrown. */ public long getValueAsLong() throws IOException { return getValueAsLong(0); } /** * Method that will try to convert value of current token to a * long. * Numbers are coerced using default Java rules; booleans convert to 0 (false) * and 1 (true), and Strings are parsed using default Java language integer * parsing rules. *

* If representation can not be converted to a long (including structured type * markers like start/end Object/Array) * specified def will be returned; no exceptions are thrown. */ public long getValueAsLong(long def) throws IOException { return def; } /** * Method that will try to convert value of current token to a Java * double. * Numbers are coerced using default Java rules; booleans convert to 0.0 (false) * and 1.0 (true), and Strings are parsed using default Java language floating * point parsing rules. *

* If representation can not be converted to a double (including structured types * like Objects and Arrays), * default value of 0.0 will be returned; no exceptions are thrown. */ public double getValueAsDouble() throws IOException { return getValueAsDouble(0.0); } /** * Method that will try to convert value of current token to a * Java double. * Numbers are coerced using default Java rules; booleans convert to 0.0 (false) * and 1.0 (true), and Strings are parsed using default Java language floating * point parsing rules. *

* If representation can not be converted to a double (including structured types * like Objects and Arrays), * specified def will be returned; no exceptions are thrown. */ public double getValueAsDouble(double def) throws IOException { return def; } /** * Method that will try to convert value of current token to a * boolean. * JSON booleans map naturally; integer numbers other than 0 map to true, and * 0 maps to false * and Strings 'true' and 'false' map to corresponding values. *

* If representation can not be converted to a boolean value (including structured types * like Objects and Arrays), * default value of false will be returned; no exceptions are thrown. */ public boolean getValueAsBoolean() throws IOException { return getValueAsBoolean(false); } /** * Method that will try to convert value of current token to a * boolean. * JSON booleans map naturally; integer numbers other than 0 map to true, and * 0 maps to false * and Strings 'true' and 'false' map to corresponding values. *

* If representation can not be converted to a boolean value (including structured types * like Objects and Arrays), * specified def will be returned; no exceptions are thrown. */ public boolean getValueAsBoolean(boolean def) throws IOException { return def; } /** * Method that will try to convert value of current token to a * {@link java.lang.String}. * JSON Strings map naturally; scalar values get converted to * their textual representation. * If representation can not be converted to a String value (including structured types * like Objects and Arrays and null token), default value of * null will be returned; no exceptions are thrown. * * @since 2.1 */ public String getValueAsString() throws IOException { return getValueAsString(null); } /** * Method that will try to convert value of current token to a * {@link java.lang.String}. * JSON Strings map naturally; scalar values get converted to * their textual representation. * If representation can not be converted to a String value (including structured types * like Objects and Arrays and null token), specified default value * will be returned; no exceptions are thrown. * * @since 2.1 */ public abstract String getValueAsString(String def) throws IOException; /* /********************************************************** /* Public API, Native Ids (type, object) /********************************************************** */ /** * Introspection method that may be called to see if the underlying * data format supports some kind of Object Ids natively (many do not; * for example, JSON doesn't). *

* Default implementation returns true; overridden by data formats * that do support native Object Ids. Caller is expected to either * use a non-native notation (explicit property or such), or fail, * in case it can not use native object ids. * * @since 2.3 */ public boolean canReadObjectId() { return false; } /** * Introspection method that may be called to see if the underlying * data format supports some kind of Type Ids natively (many do not; * for example, JSON doesn't). *

* Default implementation returns true; overridden by data formats * that do support native Type Ids. Caller is expected to either * use a non-native notation (explicit property or such), or fail, * in case it can not use native type ids. * * @since 2.3 */ public boolean canReadTypeId() { return false; } /** * Method that can be called to check whether current token * (one that was just read) has an associated Object id, and if * so, return it. * Note that while typically caller should check with {@link #canReadObjectId} * first, it is not illegal to call this method even if that method returns * true; but if so, it will return null. This may be used to simplify calling * code. *

* Default implementation will simply return null. * * @since 2.3 */ public Object getObjectId() throws IOException { return null; } /** * Method that can be called to check whether current token * (one that was just read) has an associated type id, and if * so, return it. * Note that while typically caller should check with {@link #canReadTypeId} * first, it is not illegal to call this method even if that method returns * true; but if so, it will return null. This may be used to simplify calling * code. *

* Default implementation will simply return null. * * @since 2.3 */ public Object getTypeId() throws IOException { return null; } /* /********************************************************** /* Public API, optional data binding functionality /********************************************************** */ /** * Method to deserialize JSON content into a non-container * type (it can be an array type, however): typically a bean, array * or a wrapper type (like {@link java.lang.Boolean}). * Note: method can only be called if the parser has * an object codec assigned; this is true for parsers constructed * by MappingJsonFactory (from "jackson-databind" jar) * but not for {@link JsonFactory} (unless its setCodec * method has been explicitly called). *

* This method may advance the event stream, for structured types * the current token will be the closing end marker (END_ARRAY, * END_OBJECT) of the bound structure. For non-structured Json types * (and for {@link JsonToken#VALUE_EMBEDDED_OBJECT}) * stream is not advanced. *

* Note: this method should NOT be used if the result type is a * container ({@link java.util.Collection} or {@link java.util.Map}. * The reason is that due to type erasure, key and value types * can not be introspected when using this method. */ public T readValueAs(Class valueType) throws IOException { return _codec().readValue(this, valueType); } /** * Method to deserialize JSON content into a Java type, reference * to which is passed as argument. Type is passed using so-called * "super type token" * and specifically needs to be used if the root type is a * parameterized (generic) container type. * Note: method can only be called if the parser has * an object codec assigned; this is true for parsers constructed * by MappingJsonFactory (defined in 'jackson-databind' bundle) * but not for {@link JsonFactory} (unless its setCodec * method has been explicitly called). *

* This method may advance the event stream, for structured types * the current token will be the closing end marker (END_ARRAY, * END_OBJECT) of the bound structure. For non-structured Json types * (and for {@link JsonToken#VALUE_EMBEDDED_OBJECT}) * stream is not advanced. */ @SuppressWarnings("unchecked") public T readValueAs(TypeReference valueTypeRef) throws IOException { return (T) _codec().readValue(this, valueTypeRef); } /** * Method for reading sequence of Objects from parser stream, * all with same specified value type. */ public Iterator readValuesAs(Class valueType) throws IOException { return _codec().readValues(this, valueType); } /** * Method for reading sequence of Objects from parser stream, * all with same specified value type. */ public Iterator readValuesAs(TypeReference valueTypeRef) throws IOException { return _codec().readValues(this, valueTypeRef); } /** * Method to deserialize JSON content into equivalent "tree model", * represented by root {@link TreeNode} of resulting model. * For JSON Arrays it will an array node (with child nodes), * for objects object node (with child nodes), and for other types * matching leaf node type. Empty or whitespace documents are null. * * @return root of the document, or null if empty or whitespace. */ @SuppressWarnings("unchecked") public T readValueAsTree() throws IOException { return (T) _codec().readTree(this); } protected ObjectCodec _codec() { ObjectCodec c = getCodec(); if (c == null) { throw new IllegalStateException("No ObjectCodec defined for parser, needed for deserialization"); } return c; } /* /********************************************************** /* Internal methods /********************************************************** */ /** * Helper method for constructing {@link JsonParseException}s * based on current state of the parser */ protected JsonParseException _constructError(String msg) { return new JsonParseException(this, msg) .withRequestPayload(_requestPayload); } /** * Helper method to call for operations that are not supported by * parser implementation. * * @since 2.1 */ protected void _reportUnsupportedOperation() { throw new UnsupportedOperationException("Operation not supported by parser of type "+getClass().getName()); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonPointer.java000066400000000000000000000417471356164247300314710ustar00rootroot00000000000000package com.fasterxml.jackson.core; import com.fasterxml.jackson.core.io.NumberInput; /** * Implementation of * JSON Pointer * specification. * Pointer instances can be used to locate logical JSON nodes for things like * tree traversal (see {@link TreeNode#at}). * It may be used in future for filtering of streaming JSON content * as well (not implemented yet for 2.3). *

* Instances are fully immutable and can be cached, shared between threads. * * @author Tatu Saloranta * * @since 2.3 */ public class JsonPointer { /** * Character used to separate segments. * * @since 2.9 */ public final static char SEPARATOR = '/'; /** * Marker instance used to represent segment that matches current * node or position (that is, returns true for * {@link #matches()}). */ protected final static JsonPointer EMPTY = new JsonPointer(); /** * Reference to rest of the pointer beyond currently matching * segment (if any); null if this pointer refers to the matching * segment. */ protected final JsonPointer _nextSegment; /** * Reference from currently matching segment (if any) to node * before leaf. * Lazily constructed if/as needed. *

* NOTE: we'll use `volatile` here assuming that this is unlikely to * become a performance bottleneck. If it becomes one we can probably * just drop it and things still should work (despite warnings as per JMM * regarding visibility (and lack thereof) of unguarded changes). * * @since 2.5 */ protected volatile JsonPointer _head; /** * We will retain representation of the pointer, as a String, * so that {@link #toString} should be as efficient as possible. */ protected final String _asString; protected final String _matchingPropertyName; protected final int _matchingElementIndex; /* /********************************************************** /* Construction /********************************************************** */ /** * Constructor used for creating "empty" instance, used to represent * state that matches current node. */ protected JsonPointer() { _nextSegment = null; _matchingPropertyName = ""; _matchingElementIndex = -1; _asString = ""; } /** * Constructor used for creating non-empty Segments */ protected JsonPointer(String fullString, String segment, JsonPointer next) { _asString = fullString; _nextSegment = next; // Ok; may always be a property _matchingPropertyName = segment; // but could be an index, if parsable _matchingElementIndex = _parseIndex(segment); } /** * @since 2.5 */ protected JsonPointer(String fullString, String segment, int matchIndex, JsonPointer next) { _asString = fullString; _nextSegment = next; _matchingPropertyName = segment; _matchingElementIndex = matchIndex; } /* /********************************************************** /* Factory methods /********************************************************** */ /** * Factory method that parses given input and construct matching pointer * instance, if it represents a valid JSON Pointer: if not, a * {@link IllegalArgumentException} is thrown. * * @throws IllegalArgumentException Thrown if the input does not present a valid JSON Pointer * expression: currently the only such expression is one that does NOT start with * a slash ('/'). */ public static JsonPointer compile(String input) throws IllegalArgumentException { // First quick checks for well-known 'empty' pointer if ((input == null) || input.length() == 0) { return EMPTY; } // And then quick validity check: if (input.charAt(0) != '/') { throw new IllegalArgumentException("Invalid input: JSON Pointer expression must start with '/': "+"\""+input+"\""); } return _parseTail(input); } /** * Alias for {@link #compile}; added to make instances automatically * deserializable by Jackson databind. */ public static JsonPointer valueOf(String input) { return compile(input); } /** * Accessor for an "empty" expression, that is, one you can get by * calling {@link #compile} with "" (empty String). *

* NOTE: this is different from expression for {@code "/"} which would * instead match Object node property with empty String ("") as name. * * @since 2.10 */ public static JsonPointer empty() { return EMPTY; } /** * Factory method that will construct a pointer instance that describes * path to location given {@link JsonStreamContext} points to. * * @param context Context to build pointer expression fot * @param includeRoot Whether to include number offset for virtual "root context" * or not. * * @since 2.9 */ public static JsonPointer forPath(JsonStreamContext context, boolean includeRoot) { // First things first: last segment may be for START_ARRAY/START_OBJECT, // in which case it does not yet point to anything, and should be skipped if (context == null) { return EMPTY; } if (!context.hasPathSegment()) { // one special case; do not prune root if we need it if (!(includeRoot && context.inRoot() && context.hasCurrentIndex())) { context = context.getParent(); } } JsonPointer tail = null; for (; context != null; context = context.getParent()) { if (context.inObject()) { String seg = context.getCurrentName(); if (seg == null) { // is this legal? seg = ""; } tail = new JsonPointer(_fullPath(tail, seg), seg, tail); } else if (context.inArray() || includeRoot) { int ix = context.getCurrentIndex(); String ixStr = String.valueOf(ix); tail = new JsonPointer(_fullPath(tail, ixStr), ixStr, ix, tail); } // NOTE: this effectively drops ROOT node(s); should have 1 such node, // as the last one, but we don't have to care (probably some paths have // no root, for example) } if (tail == null) { return EMPTY; } return tail; } private static String _fullPath(JsonPointer tail, String segment) { if (tail == null) { StringBuilder sb = new StringBuilder(segment.length()+1); sb.append('/'); _appendEscaped(sb, segment); return sb.toString(); } String tailDesc = tail._asString; StringBuilder sb = new StringBuilder(segment.length() + 1 + tailDesc.length()); sb.append('/'); _appendEscaped(sb, segment); sb.append(tailDesc); return sb.toString(); } private static void _appendEscaped(StringBuilder sb, String segment) { for (int i = 0, end = segment.length(); i < end; ++i) { char c = segment.charAt(i); if (c == '/') { sb.append("~1"); continue; } if (c == '~') { sb.append("~0"); continue; } sb.append(c); } } /* Factory method that composes a pointer instance, given a set * of 'raw' segments: raw meaning that no processing will be done, * no escaping may is present. * * @param segments * * @return Constructed path instance */ /* TODO! public static JsonPointer fromSegment(String... segments) { if (segments.length == 0) { return EMPTY; } JsonPointer prev = null; for (String segment : segments) { JsonPointer next = new JsonPointer() } } */ /* /********************************************************** /* Public API /********************************************************** */ public boolean matches() { return _nextSegment == null; } public String getMatchingProperty() { return _matchingPropertyName; } public int getMatchingIndex() { return _matchingElementIndex; } /** * @return True if the root selector matches property name (that is, could * match field value of JSON Object node) */ public boolean mayMatchProperty() { return _matchingPropertyName != null; } /** * @return True if the root selector matches element index (that is, could * match an element of JSON Array node) */ public boolean mayMatchElement() { return _matchingElementIndex >= 0; } /** * Returns the leaf of current JSON Pointer expression. * Leaf is the last non-null segment of current JSON Pointer. * * @since 2.5 */ public JsonPointer last() { JsonPointer current = this; if (current == EMPTY) { return null; } JsonPointer next; while ((next = current._nextSegment) != JsonPointer.EMPTY) { current = next; } return current; } /** * Mutant factory method that will return *

* * @param tail {@link JsonPointer} instance to append to this one, to create a new pointer instance * * @return Either `this` instance, `tail`, or a newly created combination, as per description above. */ public JsonPointer append(JsonPointer tail) { if (this == EMPTY) { return tail; } if (tail == EMPTY) { return this; } // 21-Mar-2017, tatu: Not superbly efficient; could probably improve by not concatenating, // re-decoding -- by stitching together segments -- but for now should be fine. String currentJsonPointer = _asString; if (currentJsonPointer.endsWith("/")) { //removes final slash currentJsonPointer = currentJsonPointer.substring(0, currentJsonPointer.length()-1); } return compile(currentJsonPointer + tail._asString); } /** * Method that may be called to see if the pointer would match property * (of a JSON Object) with given name. * * @since 2.5 */ public boolean matchesProperty(String name) { return (_nextSegment != null) && _matchingPropertyName.equals(name); } public JsonPointer matchProperty(String name) { if ((_nextSegment != null) && _matchingPropertyName.equals(name)) { return _nextSegment; } return null; } /** * Method that may be called to see if the pointer would match * array element (of a JSON Array) with given index. * * @since 2.5 */ public boolean matchesElement(int index) { return (index == _matchingElementIndex) && (index >= 0); } /** * @since 2.6 */ public JsonPointer matchElement(int index) { if ((index != _matchingElementIndex) || (index < 0)) { return null; } return _nextSegment; } /** * Accessor for getting a "sub-pointer", instance where current segment * has been removed and pointer includes rest of segments. * For matching state, will return null. */ public JsonPointer tail() { return _nextSegment; } /** * Accessor for getting a pointer instance that is identical to this * instance except that the last segment has been dropped. * For example, for JSON Point "/root/branch/leaf", this method would * return pointer "/root/branch" (compared to {@link #tail()} that * would return "/branch/leaf"). * For leaf * * @since 2.5 */ public JsonPointer head() { JsonPointer h = _head; if (h == null) { if (this != EMPTY) { h = _constructHead(); } _head = h; } return h; } /* /********************************************************** /* Standard method overrides /********************************************************** */ @Override public String toString() { return _asString; } @Override public int hashCode() { return _asString.hashCode(); } @Override public boolean equals(Object o) { if (o == this) return true; if (o == null) return false; if (!(o instanceof JsonPointer)) return false; return _asString.equals(((JsonPointer) o)._asString); } /* /********************************************************** /* Internal methods /********************************************************** */ private final static int _parseIndex(String str) { final int len = str.length(); // [core#133]: beware of super long indexes; assume we never // have arrays over 2 billion entries so ints are fine. if (len == 0 || len > 10) { return -1; } // [core#176]: no leading zeroes allowed char c = str.charAt(0); if (c <= '0') { return (len == 1 && c == '0') ? 0 : -1; } if (c > '9') { return -1; } for (int i = 1; i < len; ++i) { c = str.charAt(i); if (c > '9' || c < '0') { return -1; } } if (len == 10) { long l = NumberInput.parseLong(str); if (l > Integer.MAX_VALUE) { return -1; } } return NumberInput.parseInt(str); } protected static JsonPointer _parseTail(String input) { final int end = input.length(); // first char is the contextual slash, skip for (int i = 1; i < end; ) { char c = input.charAt(i); if (c == '/') { // common case, got a segment return new JsonPointer(input, input.substring(1, i), _parseTail(input.substring(i))); } ++i; // quoting is different; offline this case if (c == '~' && i < end) { // possibly, quote return _parseQuotedTail(input, i); } // otherwise, loop on } // end of the road, no escapes return new JsonPointer(input, input.substring(1), EMPTY); } /** * Method called to parse tail of pointer path, when a potentially * escaped character has been seen. * * @param input Full input for the tail being parsed * @param i Offset to character after tilde */ protected static JsonPointer _parseQuotedTail(String input, int i) { final int end = input.length(); StringBuilder sb = new StringBuilder(Math.max(16, end)); if (i > 2) { sb.append(input, 1, i-1); } _appendEscape(sb, input.charAt(i++)); while (i < end) { char c = input.charAt(i); if (c == '/') { // end is nigh! return new JsonPointer(input, sb.toString(), _parseTail(input.substring(i))); } ++i; if (c == '~' && i < end) { _appendEscape(sb, input.charAt(i++)); continue; } sb.append(c); } // end of the road, last segment return new JsonPointer(input, sb.toString(), EMPTY); } protected JsonPointer _constructHead() { // ok; find out who we are to drop JsonPointer last = last(); if (last == this) { return EMPTY; } // and from that, length of suffix to drop int suffixLength = last._asString.length(); JsonPointer next = _nextSegment; return new JsonPointer(_asString.substring(0, _asString.length() - suffixLength), _matchingPropertyName, _matchingElementIndex, next._constructHead(suffixLength, last)); } protected JsonPointer _constructHead(int suffixLength, JsonPointer last) { if (this == last) { return EMPTY; } JsonPointer next = _nextSegment; String str = _asString; return new JsonPointer(str.substring(0, str.length() - suffixLength), _matchingPropertyName, _matchingElementIndex, next._constructHead(suffixLength, last)); } private static void _appendEscape(StringBuilder sb, char c) { if (c == '0') { c = '~'; } else if (c == '1') { c = '/'; } else { sb.append('~'); } sb.append(c); } } JsonProcessingException.java000066400000000000000000000103411356164247300337470ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; /** * Intermediate base class for all problems encountered when * processing (parsing, generating) JSON content * that are not pure I/O problems. * Regular {@link java.io.IOException}s will be passed through as is. * Sub-class of {@link java.io.IOException} for convenience. */ public class JsonProcessingException extends java.io.IOException { final static long serialVersionUID = 123; // Stupid eclipse... protected JsonLocation _location; protected JsonProcessingException(String msg, JsonLocation loc, Throwable rootCause) { super(msg); if (rootCause != null) { initCause(rootCause); } _location = loc; } protected JsonProcessingException(String msg) { super(msg); } protected JsonProcessingException(String msg, JsonLocation loc) { this(msg, loc, null); } protected JsonProcessingException(String msg, Throwable rootCause) { this(msg, null, rootCause); } protected JsonProcessingException(Throwable rootCause) { this(null, null, rootCause); } /* /********************************************************** /* Extended API /********************************************************** */ public JsonLocation getLocation() { return _location; } /** * Method that allows to remove context information from this exception's message. * Useful when you are parsing security-sensitive data and don't want original data excerpts * to be present in Jackson parser error messages. * * @since 2.9 */ public void clearLocation() { _location = null; } /** * Method that allows accessing the original "message" argument, * without additional decorations (like location information) * that overridden {@link #getMessage} adds. * * @since 2.1 */ public String getOriginalMessage() { return super.getMessage(); } /** * Method that allows accessing underlying processor that triggered * this exception; typically either {@link JsonParser} or {@link JsonGenerator} * for exceptions that originate from streaming API. * Note that it is possible that `null` may be returned if code throwing * exception either has no access to processor; or has not been retrofitted * to set it; this means that caller needs to take care to check for nulls. * Subtypes override this method with co-variant return type, for more * type-safe access. * * @return Originating processor, if available; null if not. * * @since 2.7 */ public Object getProcessor() { return null; } /* /********************************************************** /* Methods for sub-classes to use, override /********************************************************** */ /** * Accessor that sub-classes can override to append additional * information right after the main message, but before * source location information. */ protected String getMessageSuffix() { return null; } /* /********************************************************** /* Overrides of standard methods /********************************************************** */ /** * Default method overridden so that we can add location information */ @Override public String getMessage() { String msg = super.getMessage(); if (msg == null) { msg = "N/A"; } JsonLocation loc = getLocation(); String suffix = getMessageSuffix(); // mild optimization, if nothing extra is needed: if (loc != null || suffix != null) { StringBuilder sb = new StringBuilder(100); sb.append(msg); if (suffix != null) { sb.append(suffix); } if (loc != null) { sb.append('\n'); sb.append(" at "); sb.append(loc.toString()); } msg = sb.toString(); } return msg; } @Override public String toString() { return getClass().getName()+": "+getMessage(); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonStreamContext.java000066400000000000000000000216331356164247300326410ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; import com.fasterxml.jackson.core.io.CharTypes; /** * Shared base class for streaming processing contexts used during * reading and writing of Json content using Streaming API. * This context is also exposed to applications: * context object can be used by applications to get an idea of * relative position of the parser/generator within json content * being processed. This allows for some contextual processing: for * example, output within Array context can differ from that of * Object context. */ public abstract class JsonStreamContext { // // // Type constants used internally protected final static int TYPE_ROOT = 0; protected final static int TYPE_ARRAY = 1; protected final static int TYPE_OBJECT = 2; protected int _type; /** * Index of the currently processed entry. Starts with -1 to signal * that no entries have been started, and gets advanced each * time a new entry is started, either by encountering an expected * separator, or with new values if no separators are expected * (the case for root context). */ protected int _index; /* /********************************************************** /* Life-cycle /********************************************************** */ protected JsonStreamContext() { } /** * Copy constructor used by sub-classes for creating copies for * buffering. * * @since 2.9 */ protected JsonStreamContext(JsonStreamContext base) { _type = base._type; _index = base._index; } /** * @since 2.9 */ protected JsonStreamContext(int type, int index) { _type = type; _index = index; } /* /********************************************************** /* Public API, accessors /********************************************************** */ /** * Accessor for finding parent context of this context; will * return null for root context. */ public abstract JsonStreamContext getParent(); /** * Method that returns true if this context is an Array context; * that is, content is being read from or written to a Json Array. */ public final boolean inArray() { return _type == TYPE_ARRAY; } /** * Method that returns true if this context is a Root context; * that is, content is being read from or written to without * enclosing array or object structure. */ public final boolean inRoot() { return _type == TYPE_ROOT; } /** * Method that returns true if this context is an Object context; * that is, content is being read from or written to a Json Object. */ public final boolean inObject() { return _type == TYPE_OBJECT; } /** * 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"; case TYPE_ARRAY: return "ARRAY"; case TYPE_OBJECT: return "OBJECT"; } return "?"; } /** * @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; } /** * @return Index of the currently processed entry, if any */ public final int getCurrentIndex() { return (_index < 0) ? 0 : _index; } /** * Method that may be called to verify whether this context has valid index: * will return `false` before the first entry of Object context or before * first element of Array context; otherwise returns `true`. * * @since 2.9 */ public boolean hasCurrentIndex() { return _index >= 0; } /** * Method that may be called to check if this context is either: * * and if so, return `true`; otherwise return `false`. Latter case includes * Root context (always), and Object/Array contexts before any entries/elements * have been read or written. *

* Method is mostly used to determine whether this context should be used for * constructing {@link JsonPointer} * * @since 2.9 */ public boolean hasPathSegment() { if (_type == TYPE_OBJECT) { return hasCurrentName(); } else if (_type == TYPE_ARRAY) { return hasCurrentIndex(); } return false; } /** * Method for accessing name associated with the current location. * Non-null for FIELD_NAME and value events that directly * follow field names; null for root level and array values. */ public abstract String getCurrentName(); /** * @since 2.9 */ public boolean hasCurrentName() { return getCurrentName() != null; } /** * Method for accessing currently active value being used by data-binding * (as the source of streaming data to write, or destination of data being * read), at this level in hierarchy. *

* Note that "current value" is NOT populated (or used) by Streaming parser or generator; * it is only used by higher-level data-binding functionality. * The reason it is included here is that it can be stored and accessed hierarchically, * and gets passed through data-binding. * * @return Currently active value, if one has been assigned. * * @since 2.5 */ public Object getCurrentValue() { return null; } /** * Method to call to pass value to be returned via {@link #getCurrentValue}; typically * called indirectly through {@link JsonParser#setCurrentValue} * or {@link JsonGenerator#setCurrentValue}). * * @since 2.5 */ public void setCurrentValue(Object v) { } /** * Factory method for constructing a {@link JsonPointer} that points to the current * location within the stream that this context is for, excluding information about * "root context" (only relevant for multi-root-value cases) * * @since 2.9 */ public JsonPointer pathAsPointer() { return JsonPointer.forPath(this, false); } /** * Factory method for constructing a {@link JsonPointer} that points to the current * location within the stream that this context is for, optionally including * "root value index" * * @param includeRoot Whether root-value offset is included as the first segment or not; * * @since 2.9 */ public JsonPointer pathAsPointer(boolean includeRoot) { return JsonPointer.forPath(this, includeRoot); } /** * Optional method that may be used to access starting location of this context: * for example, in case of JSON `Object` context, offset at which `[` token was * read or written. Often used for error reporting purposes. * Implementations that do not keep track of such location are expected to return * {@link JsonLocation#NA}; this is what the default implementation does. * * @return Location pointing to the point where the context * start marker was found (or written); never `null`. *

* NOTE: demoted from JsonReadContext in 2.9, to allow use for * "non-standard" read contexts. * * @since 2.9 */ public JsonLocation getStartLocation(Object srcRef) { return JsonLocation.NA; } /** * Overridden to provide developer readable "JsonPath" representation * of the context. * * @since 2.9 */ @Override public String toString() { StringBuilder sb = new StringBuilder(64); switch (_type) { case TYPE_ROOT: sb.append("/"); break; case TYPE_ARRAY: sb.append('['); sb.append(getCurrentIndex()); sb.append(']'); break; case TYPE_OBJECT: default: sb.append('{'); String currentName = getCurrentName(); if (currentName != null) { sb.append('"'); CharTypes.appendQuoted(sb, currentName); sb.append('"'); } else { sb.append('?'); } sb.append('}'); break; } return sb.toString(); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonToken.java000066400000000000000000000147371356164247300311300ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; /** * Enumeration for basic token types used for returning results * of parsing JSON content. */ public enum JsonToken { /* Some notes on implementation: * * - Entries are to be ordered such that start/end array/object * markers come first, then field name marker (if any), and * finally scalar value tokens. This is assumed by some * typing checks. */ /** * NOT_AVAILABLE can be returned if {@link JsonParser} * implementation can not currently return the requested * token (usually next one), or even if any will be * available, but that may be able to determine this in * future. This is the case with non-blocking parsers -- * they can not block to wait for more data to parse and * must return something. */ NOT_AVAILABLE(null, JsonTokenId.ID_NOT_AVAILABLE), /** * START_OBJECT is returned when encountering '{' * which signals starting of an Object value. */ START_OBJECT("{", JsonTokenId.ID_START_OBJECT), /** * END_OBJECT is returned when encountering '}' * which signals ending of an Object value */ END_OBJECT("}", JsonTokenId.ID_END_OBJECT), /** * START_ARRAY is returned when encountering '[' * which signals starting of an Array value */ START_ARRAY("[", JsonTokenId.ID_START_ARRAY), /** * END_ARRAY is returned when encountering ']' * which signals ending of an Array value */ END_ARRAY("]", JsonTokenId.ID_END_ARRAY), /** * FIELD_NAME is returned when a String token is encountered * as a field name (same lexical value, different function) */ FIELD_NAME(null, JsonTokenId.ID_FIELD_NAME), /** * Placeholder token returned when the input source has a concept * of embedded Object that are not accessible as usual structure * (of starting with {@link #START_OBJECT}, having values, ending with * {@link #END_OBJECT}), but as "raw" objects. *

* Note: this token is never returned by regular JSON readers, but * only by readers that expose other kinds of source (like * JsonNode-based JSON trees, Maps, Lists and such). */ VALUE_EMBEDDED_OBJECT(null, JsonTokenId.ID_EMBEDDED_OBJECT), /** * VALUE_STRING is returned when a String token is encountered * in value context (array element, field value, or root-level * stand-alone value) */ VALUE_STRING(null, JsonTokenId.ID_STRING), /** * VALUE_NUMBER_INT is returned when an integer numeric token is * encountered in value context: that is, a number that does * not have floating point or exponent marker in it (consists * only of an optional sign, followed by one or more digits; * or, for binary formats, is indicated as integral number * by internal representation). */ VALUE_NUMBER_INT(null, JsonTokenId.ID_NUMBER_INT), /** * VALUE_NUMBER_FLOAT is returned when a numeric token other * than integer is encountered: that is, a number that does * have floating point or exponent marker in it, in addition * to one or more digits (or, for non-textual formats, * has internal floating-point representation). */ VALUE_NUMBER_FLOAT(null, JsonTokenId.ID_NUMBER_FLOAT), /** * VALUE_TRUE is returned when encountering literal "true" in * value context */ VALUE_TRUE("true", JsonTokenId.ID_TRUE), /** * VALUE_FALSE is returned when encountering literal "false" in * value context */ VALUE_FALSE("false", JsonTokenId.ID_FALSE), /** * VALUE_NULL is returned when encountering literal "null" in * value context */ VALUE_NULL("null", JsonTokenId.ID_NULL), ; final String _serialized; final char[] _serializedChars; final byte[] _serializedBytes; final int _id; final boolean _isStructStart, _isStructEnd; final boolean _isNumber; final boolean _isBoolean; final boolean _isScalar; /** * @param token representation for this token, if there is a * single static representation; null otherwise */ JsonToken(String token, int id) { if (token == null) { _serialized = null; _serializedChars = null; _serializedBytes = null; } else { _serialized = token; _serializedChars = token.toCharArray(); // It's all in ascii, can just case... int len = _serializedChars.length; _serializedBytes = new byte[len]; for (int i = 0; i < len; ++i) { _serializedBytes[i] = (byte) _serializedChars[i]; } } _id = id; _isBoolean = (id == JsonTokenId.ID_FALSE || id == JsonTokenId.ID_TRUE); _isNumber = (id == JsonTokenId.ID_NUMBER_INT || id == JsonTokenId.ID_NUMBER_FLOAT); _isStructStart = (id == JsonTokenId.ID_START_OBJECT || id == JsonTokenId.ID_START_ARRAY); _isStructEnd = (id == JsonTokenId.ID_END_OBJECT || id == JsonTokenId.ID_END_ARRAY); _isScalar = !_isStructStart && !_isStructEnd && (id != JsonTokenId.ID_FIELD_NAME) && (id != JsonTokenId.ID_NOT_AVAILABLE); } public final int id() { return _id; } public final String asString() { return _serialized; } public final char[] asCharArray() { return _serializedChars; } public final byte[] asByteArray() { return _serializedBytes; } public final boolean isNumeric() { return _isNumber; } /** * Accessor that is functionally equivalent to: * * this == JsonToken.START_OBJECT || this == JsonToken.START_ARRAY * * * @since 2.3 */ public final boolean isStructStart() { return _isStructStart; } /** * Accessor that is functionally equivalent to: * * this == JsonToken.END_OBJECT || this == JsonToken.END_ARRAY * * * @since 2.3 */ public final boolean isStructEnd() { return _isStructEnd; } /** * Method that can be used to check whether this token represents * a valid non-structured value. This means all tokens other than * Object/Array start/end markers all field names. */ public final boolean isScalarValue() { return _isScalar; } public final boolean isBoolean() { return _isBoolean; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonTokenId.java000066400000000000000000000045261356164247300314000ustar00rootroot00000000000000package com.fasterxml.jackson.core; /** * Interface defined to contain ids accessible with {@link JsonToken#id()}. * Needed because it is impossible to define these constants in * {@link JsonToken} itself, as static constants (oddity of how Enums * are implemented by JVM). * * @since 2.3 */ public interface JsonTokenId { /** * Id used to represent {@link JsonToken#NOT_AVAILABLE}, used in * cases where a token may become available when more input * is available: this occurs in non-blocking use cases. */ public final static int ID_NOT_AVAILABLE = -1; /** * Id used to represent the case where no {@link JsonToken} * is available: either because {@link JsonParser} has not been * advanced to first token, or because no more tokens will be * available (end-of-input or explicit closing of parser}. */ public final static int ID_NO_TOKEN = 0; /** * Id used to represent {@link JsonToken#START_OBJECT} */ public final static int ID_START_OBJECT = 1; /** * Id used to represent {@link JsonToken#END_OBJECT} */ public final static int ID_END_OBJECT = 2; /** * Id used to represent {@link JsonToken#START_ARRAY} */ public final static int ID_START_ARRAY = 3; /** * Id used to represent {@link JsonToken#END_ARRAY} */ public final static int ID_END_ARRAY = 4; /** * Id used to represent {@link JsonToken#FIELD_NAME} */ public final static int ID_FIELD_NAME = 5; /** * Id used to represent {@link JsonToken#VALUE_STRING} */ public final static int ID_STRING = 6; /** * Id used to represent {@link JsonToken#VALUE_NUMBER_INT} */ public final static int ID_NUMBER_INT = 7; /** * Id used to represent {@link JsonToken#VALUE_NUMBER_FLOAT} */ public final static int ID_NUMBER_FLOAT = 8; /** * Id used to represent {@link JsonToken#VALUE_TRUE} */ public final static int ID_TRUE = 9; /** * Id used to represent {@link JsonToken#VALUE_FALSE} */ public final static int ID_FALSE = 10; /** * Id used to represent {@link JsonToken#VALUE_NULL} */ public final static int ID_NULL = 11; /** * Id used to represent {@link JsonToken#VALUE_EMBEDDED_OBJECT} */ public final static int ID_EMBEDDED_OBJECT = 12; } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonpCharacterEscapes.java000066400000000000000000000026421356164247300334200ustar00rootroot00000000000000package 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; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/ObjectCodec.java000066400000000000000000000135151356164247300313530ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; import java.io.IOException; import java.util.Iterator; import com.fasterxml.jackson.core.type.ResolvedType; import com.fasterxml.jackson.core.type.TypeReference; /** * Abstract class that defines the interface that {@link JsonParser} and * {@link JsonGenerator} use to serialize and deserialize regular * Java objects (POJOs aka Beans). *

* The standard implementation of this class is * com.fasterxml.jackson.databind.ObjectMapper, * defined in the "jackson-databind". */ public abstract class ObjectCodec extends TreeCodec // since 2.3 implements Versioned // since 2.3 { protected ObjectCodec() { } // Since 2.3 @Override public abstract Version version(); /* /********************************************************** /* API for de-serialization (JSON-to-Object) /********************************************************** */ /** * Method to deserialize JSON content into a non-container * type (it can be an array type, however): typically a bean, array * or a wrapper type (like {@link java.lang.Boolean}). *

* Note: this method should NOT be used if the result type is a * container ({@link java.util.Collection} or {@link java.util.Map}. * The reason is that due to type erasure, key and value types * can not be introspected when using this method. */ public abstract T readValue(JsonParser p, Class valueType) throws IOException; /** * Method to deserialize JSON content into a Java type, reference * to which is passed as argument. Type is passed using so-called * "super type token" * and specifically needs to be used if the root type is a * parameterized (generic) container type. */ public abstract T readValue(JsonParser p, TypeReference valueTypeRef) throws IOException; /** * Method to deserialize JSON content into a POJO, type specified * with fully resolved type object (so it can be a generic type, * including containers like {@link java.util.Collection} and * {@link java.util.Map}). */ public abstract 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 Iterator readValues(JsonParser p, Class valueType) throws IOException; /** * Method for reading sequence of Objects from parser stream, * all with same specified value type. */ public abstract Iterator readValues(JsonParser p, TypeReference valueTypeRef) throws IOException; /** * Method for reading sequence of Objects from parser stream, * all with same specified value type. */ public abstract Iterator readValues(JsonParser p, ResolvedType valueType) throws IOException; /* /********************************************************** /* API for serialization (Object-to-JSON) /********************************************************** */ /** * Method to serialize given Java Object, using generator * provided. */ public abstract void writeValue(JsonGenerator gen, Object value) throws IOException; /* /********************************************************** /* TreeCodec pass-through methods /********************************************************** */ /** * Method to deserialize JSON content as tree expressed * using set of {@link TreeNode} instances. Returns * root of the resulting tree (where root can consist * of just a single node if the current event is a * value event, not container). Empty or whitespace * documents return null. * * @return next tree from p, or null if empty. */ @Override public abstract T readTree(JsonParser p) throws IOException; @Override public abstract void writeTree(JsonGenerator gen, TreeNode tree) throws IOException; /** * Method for construct root level Object nodes * for Tree Model instances. */ @Override public abstract TreeNode createObjectNode(); /** * Method for construct root level Array nodes * for Tree Model instances. */ @Override public abstract TreeNode createArrayNode(); /** * Method for constructing a {@link JsonParser} for reading * contents of a JSON tree, as if it was external serialized * JSON content. */ @Override public abstract JsonParser treeAsTokens(TreeNode n); /* /********************************************************** /* Extended tree conversions beyond TreeCodec /********************************************************** */ /** * Convenience method for converting given JSON tree into instance of specified * value type. This is equivalent to first constructing a {@link JsonParser} to * iterate over contents of the tree, and using that parser for data binding. */ public abstract T treeToValue(TreeNode n, Class valueType) throws JsonProcessingException; /* /********************************************************** /* Basic accessors /********************************************************** */ /** * @deprecated Since 2.1: Use {@link #getFactory} instead. */ @Deprecated public JsonFactory getJsonFactory() { return getFactory(); } /** * Accessor for finding underlying data format factory * ({@link JsonFactory}) codec will use for data binding. * * @since 2.1 */ public JsonFactory getFactory() { return getJsonFactory(); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/PrettyPrinter.java000066400000000000000000000144641356164247300320460ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; import java.io.IOException; import com.fasterxml.jackson.core.io.SerializedString; import com.fasterxml.jackson.core.util.Separators; /** * Interface for objects that implement pretty printer functionality, such * as indentation. * Pretty printers are used to add white space in output JSON content, * to make results more human readable. Usually this means things like adding * linefeeds and indentation. *

* Note: since Jackson 2.1, stateful implementations MUST implement * {@link com.fasterxml.jackson.core.util.Instantiatable} interface, * to allow for constructing per-generation instances and avoid * state corruption. * Stateless implementations need not do this; but those are less common. */ public interface PrettyPrinter { /** * @since 2.9 */ public final static Separators DEFAULT_SEPARATORS = Separators.createDefaultInstance(); /** * Default String used for separating root values is single space. * * @since 2.9 */ public final static SerializedString DEFAULT_ROOT_VALUE_SEPARATOR = new SerializedString(" "); /* /********************************************************** /* First methods that act both as events, and expect /* output for correct functioning (i.e something gets /* output even when not pretty-printing) /********************************************************** */ // // // Root-level handling: /** * Method called after a root-level value has been completely * output, and before another value is to be output. *

* Default * handling (without pretty-printing) will output a space, to * allow values to be parsed correctly. Pretty-printer is * to output some other suitable and nice-looking separator * (tab(s), space(s), linefeed(s) or any combination thereof). */ void writeRootValueSeparator(JsonGenerator gen) throws IOException; // // Object handling /** * Method called when an Object value is to be output, before * any fields are output. *

* Default handling (without pretty-printing) will output * the opening curly bracket. * Pretty-printer is * to output a curly bracket as well, but can surround that * with other (white-space) decoration. */ void writeStartObject(JsonGenerator gen) throws IOException; /** * Method called after an Object value has been completely output * (minus closing curly bracket). *

* Default handling (without pretty-printing) will output * the closing curly bracket. * Pretty-printer is * to output a curly bracket as well, but can surround that * with other (white-space) decoration. * * @param nrOfEntries Number of direct members of the array that * have been output */ void writeEndObject(JsonGenerator gen, int nrOfEntries) throws IOException; /** * Method called after an object entry (field:value) has been completely * output, and before another value is to be output. *

* Default handling (without pretty-printing) will output a single * comma to separate the two. Pretty-printer is * to output a comma as well, but can surround that with other * (white-space) decoration. */ void writeObjectEntrySeparator(JsonGenerator gen) throws IOException; /** * Method called after an object field has been output, but * before the value is output. *

* Default handling (without pretty-printing) will output a single * colon to separate the two. Pretty-printer is * to output a colon as well, but can surround that with other * (white-space) decoration. */ void writeObjectFieldValueSeparator(JsonGenerator gen) throws IOException; // // // Array handling /** * Method called when an Array value is to be output, before * any member/child values are output. *

* Default handling (without pretty-printing) will output * the opening bracket. * Pretty-printer is * to output a bracket as well, but can surround that * with other (white-space) decoration. */ void writeStartArray(JsonGenerator gen) throws IOException; /** * Method called after an Array value has been completely output * (minus closing bracket). *

* Default handling (without pretty-printing) will output * the closing bracket. * Pretty-printer is * to output a bracket as well, but can surround that * with other (white-space) decoration. * * @param nrOfValues Number of direct members of the array that * have been output */ void writeEndArray(JsonGenerator gen, int nrOfValues) throws IOException; /** * Method called after an array value has been completely * output, and before another value is to be output. *

* Default handling (without pretty-printing) will output a single * comma to separate the two. Pretty-printer is * to output a comma as well, but can surround that with other * (white-space) decoration. */ void writeArrayValueSeparator(JsonGenerator gen) throws IOException; /* /********************************************************** /* Then events that by default do not produce any output /* but that are often overridden to add white space /* in pretty-printing mode /********************************************************** */ /** * Method called after array start marker has been output, * and right before the first value is to be output. * It is not called for arrays with no values. *

* Default handling does not output anything, but pretty-printer * is free to add any white space decoration. */ void beforeArrayValues(JsonGenerator gen) throws IOException; /** * Method called after object start marker has been output, * and right before the field name of the first entry is * to be output. * It is not called for objects without entries. *

* Default handling does not output anything, but pretty-printer * is free to add any white space decoration. */ void beforeObjectEntries(JsonGenerator gen) throws IOException; } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/SerializableString.java000066400000000000000000000111111356164247300327720ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; /** * Interface that defines how Jackson package can interact with efficient * pre-serialized or lazily-serialized and reused String representations. * Typically implementations store possible serialized version(s) so that * serialization of String can be done more efficiently, especially when * used multiple times. *

* Note that "quoted" in methods means quoting of 'special' characters using * JSON backlash notation (and not use of actual double quotes). * * @see com.fasterxml.jackson.core.io.SerializedString */ public interface SerializableString { /** * Returns unquoted String that this object represents (and offers * serialized forms for) */ String getValue(); /** * Returns length of the (unquoted) String as characters. * Functionally equivalent to: *

     *   getValue().length();
     *
*/ int charLength(); /* /********************************************************** /* Accessors for byte sequences /********************************************************** */ /** * Returns JSON quoted form of the String, as character array. * Result can be embedded as-is in textual JSON as property name or JSON String. */ char[] asQuotedChars(); /** * Returns UTF-8 encoded version of unquoted String. * Functionally equivalent to (but more efficient than): *
     * getValue().getBytes("UTF-8");
     *
*/ byte[] asUnquotedUTF8(); /** * Returns UTF-8 encoded version of JSON-quoted String. * Functionally equivalent to (but more efficient than): *
     * new String(asQuotedChars()).getBytes("UTF-8");
     *
*/ byte[] asQuotedUTF8(); /* /********************************************************** /* Helper methods for appending byte/char sequences /********************************************************** */ /** * Method that will append quoted UTF-8 bytes of this String into given * buffer, if there is enough room; if not, returns -1. * Functionally equivalent to: *
     *  byte[] bytes = str.asQuotedUTF8();
     *  System.arraycopy(bytes, 0, buffer, offset, bytes.length);
     *  return bytes.length;
     *
* * @return Number of bytes appended, if successful, otherwise -1 */ int appendQuotedUTF8(byte[] buffer, int offset); /** * Method that will append quoted characters of this String into given * buffer. Functionally equivalent to: *
     *  char[] ch = str.asQuotedChars();
     *  System.arraycopy(ch, 0, buffer, offset, ch.length);
     *  return ch.length;
     *
* * @return Number of characters appended, if successful, otherwise -1 */ int appendQuoted(char[] buffer, int offset); /** * Method that will append unquoted ('raw') UTF-8 bytes of this String into given * buffer. Functionally equivalent to: *
     *  byte[] bytes = str.asUnquotedUTF8();
     *  System.arraycopy(bytes, 0, buffer, offset, bytes.length);
     *  return bytes.length;
     *
* * @return Number of bytes appended, if successful, otherwise -1 */ int appendUnquotedUTF8(byte[] buffer, int offset); /** * Method that will append unquoted characters of this String into given * buffer. Functionally equivalent to: *
     *  char[] ch = str.getValue().toCharArray();
     *  System.arraycopy(bytes, 0, buffer, offset, ch.length);
     *  return ch.length;
     *
* * @return Number of characters appended, if successful, otherwise -1 */ int appendUnquoted(char[] buffer, int offset); /* /********************************************************** /* Helper methods for writing out byte sequences /********************************************************** */ /** * @return Number of bytes written */ int writeQuotedUTF8(OutputStream out) throws IOException; /** * @return Number of bytes written */ int writeUnquotedUTF8(OutputStream out) throws IOException; /** * @return Number of bytes put, if successful, otherwise -1 */ int putQuotedUTF8(ByteBuffer buffer) throws IOException; /** * @return Number of bytes put, if successful, otherwise -1 */ int putUnquotedUTF8(ByteBuffer out) throws IOException; } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java000066400000000000000000000133261356164247300325520ustar00rootroot00000000000000package com.fasterxml.jackson.core; import java.io.InputStream; import java.io.Reader; /** * Token reader (parser) features not-specific to any particular format backend. * Eventual replacement for non-JSON-specific {@link com.fasterxml.jackson.core.JsonParser.Feature}s. * * @since 2.10 */ public enum StreamReadFeature { // // // Low-level I/O handling features: /** * Feature that determines whether parser will automatically * close underlying input source that is NOT owned by the * parser. If disabled, calling application has to separately * close the underlying {@link InputStream} and {@link Reader} * instances used to create the parser. If enabled, parser * will handle closing, as long as parser itself gets closed: * this happens when end-of-input is encountered, or parser * is closed by a call to {@link JsonParser#close}. *

* Feature is enabled by default. */ AUTO_CLOSE_SOURCE(JsonParser.Feature.AUTO_CLOSE_SOURCE), // // // Validity checks /** * Feature that determines whether {@link JsonParser} will explicitly * check that no duplicate JSON Object field names are encountered. * If enabled, parser will check all names within context and report * duplicates by throwing a {@link JsonParseException}; if disabled, * parser will not do such checking. Assumption in latter case is * that caller takes care of handling duplicates at a higher level: * data-binding, for example, has features to specify detection to * be done there. *

* Note that enabling this feature will incur performance overhead * due to having to store and check additional information: this typically * adds 20-30% to execution time for basic parsing. */ STRICT_DUPLICATE_DETECTION(JsonParser.Feature.STRICT_DUPLICATE_DETECTION), /** * Feature that determines what to do if the underlying data format requires knowledge * of all properties to decode (usually via a Schema), and if no definition is * found for a property that input content contains. * Typically most textual data formats do NOT require schema information (although * some do, such as CSV), whereas many binary data formats do require definitions * (such as Avro, protobuf), although not all (Smile, CBOR, BSON and MessagePack do not). * Further note that some formats that do require schema information will not be able * to ignore undefined properties: for example, Avro is fully positional and there is * no possibility of undefined data. This leaves formats like Protobuf that have identifiers * that may or may not map; and as such Protobuf format does make use of this feature. *

* Note that support for this feature is implemented by individual data format * module, if (and only if) it makes sense for the format in question. For JSON, * for example, this feature has no effect as properties need not be pre-defined. *

* Feature is disabled by default, meaning that if the underlying data format * requires knowledge of all properties to output, attempts to read an unknown * property will result in a {@link JsonProcessingException} */ IGNORE_UNDEFINED(JsonParser.Feature.IGNORE_UNDEFINED), // // // Other /** * Feature that determines whether {@link JsonLocation} instances should be constructed * with reference to source or not. If source reference is included, its type and contents * are included when `toString()` method is called (most notably when printing out parse * exception with that location information). If feature is disabled, no source reference * is passed and source is only indicated as "UNKNOWN". *

* Most common reason for disabling this feature is to avoid leaking information about * internal information; this may be done for security reasons. * Note that even if source reference is included, only parts of contents are usually * printed, and not the whole contents. Further, many source reference types can not * necessarily access contents (like streams), so only type is indicated, not contents. *

* Feature is enabled by default, meaning that "source reference" information is passed * and some or all of the source content may be included in {@link JsonLocation} information * constructed either when requested explicitly, or when needed for an exception. */ INCLUDE_SOURCE_IN_LOCATION(JsonParser.Feature.INCLUDE_SOURCE_IN_LOCATION), ; /** * Whether feature is enabled or disabled by default. */ private final boolean _defaultState; private final int _mask; /** * For backwards compatibility we may need to map to one of existing {@link JsonParser.Feature}s; * if so, this is the feature to enable/disable. */ final private JsonParser.Feature _mappedFeature; private StreamReadFeature(JsonParser.Feature mapTo) { // only for 2.x, let's map everything to legacy feature: _mappedFeature = mapTo; _mask = mapTo.getMask(); _defaultState = mapTo.enabledByDefault(); } /** * Method that calculates bit set (flags) of all features that * are enabled by default. */ public static int collectDefaults() { int flags = 0; for (StreamReadFeature f : values()) { if (f.enabledByDefault()) { flags |= f.getMask(); } } return flags; } public boolean enabledByDefault() { return _defaultState; } public boolean enabledIn(int flags) { return (flags & _mask) != 0; } public int getMask() { return _mask; } public JsonParser.Feature mappedFeature() { return _mappedFeature; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/StreamWriteFeature.java000066400000000000000000000140331356164247300327650ustar00rootroot00000000000000package com.fasterxml.jackson.core; import java.io.OutputStream; import java.io.Writer; import java.math.BigDecimal; /** * Token writer (generator) features not-specific to any particular format backend. * Eventual replacement for non-JSON-specific {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s. * * @since 2.10 */ public enum StreamWriteFeature { // // Low-level I/O / content features /** * Feature that determines whether generator will automatically * close underlying output target that is NOT owned by the * generator. * If disabled, calling application has to separately * close the underlying {@link OutputStream} and {@link Writer} * instances used to create the generator. If enabled, generator * will handle closing, as long as generator itself gets closed: * this happens when end-of-input is encountered, or generator * is closed by a call to {@link JsonGenerator#close}. *

* Feature is enabled by default. */ AUTO_CLOSE_TARGET(JsonGenerator.Feature.AUTO_CLOSE_TARGET), /** * Feature that determines what happens when the generator is * closed while there are still unmatched * {@link JsonToken#START_ARRAY} or {@link JsonToken#START_OBJECT} * entries in output content. If enabled, such Array(s) and/or * Object(s) are automatically closed (that is, matching END_ token write * call is made for all open scopes); if disabled, no additional * write calls are made. *

* Feature is enabled by default. */ AUTO_CLOSE_CONTENT(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT), /** * Feature that specifies that calls to {@link JsonGenerator#flush} will cause * matching flush() to underlying {@link OutputStream} * or {@link Writer}; if disabled this will not be done. * Main reason to disable this feature is to prevent flushing at * generator level, if it is not possible to prevent method being * called by other code (like ObjectMapper or third * party libraries). *

* Feature is enabled by default. */ FLUSH_PASSED_TO_STREAM(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM), // // Datatype coercion features /** * Feature that determines whether {@link java.math.BigDecimal} entries are * serialized using {@link java.math.BigDecimal#toPlainString()} to prevent * values to be written using scientific notation. *

* NOTE: only affects generators that serialize {@link java.math.BigDecimal}s * using textual representation (textual formats but potentially some binary * formats). *

* Feature is disabled by default, so default output mode is used; this generally * depends on how {@link BigDecimal} has been created. */ WRITE_BIGDECIMAL_AS_PLAIN(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN), // // Schema/Validity support features /** * Feature that determines whether {@link JsonGenerator} will explicitly * check that no duplicate JSON Object field names are written. * If enabled, generator will check all names within context and report * duplicates by throwing a {@link JsonGenerationException}; if disabled, * no such checking will be done. Assumption in latter case is * that caller takes care of not trying to write duplicate names. *

* Note that enabling this feature will incur performance overhead * due to having to store and check additional information. *

* Feature is disabled by default. */ STRICT_DUPLICATE_DETECTION(JsonGenerator.Feature.STRICT_DUPLICATE_DETECTION), /** * Feature that determines what to do if the underlying data format requires knowledge * of all properties to output, and if no definition is found for a property that * caller tries to write. If enabled, such properties will be quietly ignored; * if disabled, a {@link JsonProcessingException} will be thrown to indicate the * problem. * Typically most textual data formats do NOT require schema information (although * some do, such as CSV), whereas many binary data formats do require definitions * (such as Avro, protobuf), although not all (Smile, CBOR, BSON and MessagePack do not). *

* Note that support for this feature is implemented by individual data format * module, if (and only if) it makes sense for the format in question. For JSON, * for example, this feature has no effect as properties need not be pre-defined. *

* Feature is disabled by default, meaning that if the underlying data format * requires knowledge of all properties to output, attempts to write an unknown * property will result in a {@link JsonProcessingException} */ IGNORE_UNKNOWN(JsonGenerator.Feature.IGNORE_UNKNOWN), ; /** * Whether feature is enabled or disabled by default. */ private final boolean _defaultState; private final int _mask; /** * For backwards compatibility we may need to map to one of existing {@link JsonParser.Feature}s; * if so, this is the feature to enable/disable. */ final private JsonGenerator.Feature _mappedFeature; private StreamWriteFeature(JsonGenerator.Feature mappedTo) { // only for 2.x, let's map everything to legacy feature: _mappedFeature = mappedTo; _mask = mappedTo.getMask(); _defaultState = mappedTo.enabledByDefault(); } /** * Method that calculates bit set (flags) of all features that * are enabled by default. */ public static int collectDefaults() { int flags = 0; for (StreamWriteFeature f : values()) { if (f.enabledByDefault()) { flags |= f.getMask(); } } return flags; } public boolean enabledByDefault() { return _defaultState; } public boolean enabledIn(int flags) { return (flags & _mask) != 0; } public int getMask() { return _mask; } public JsonGenerator.Feature mappedFeature() { return _mappedFeature; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java000066400000000000000000000213361356164247300311520ustar00rootroot00000000000000package com.fasterxml.jackson.core; import com.fasterxml.jackson.core.io.InputDecorator; import com.fasterxml.jackson.core.io.OutputDecorator; import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.json.JsonWriteFeature; /** * Since 2.10, Builder class is offered for creating token stream factories * with difference configurations: with 3.x they will be fully immutable. * * @since 2.10 */ public abstract class TSFBuilder> { /* /********************************************************************** /* Constants /********************************************************************** */ /** * Bitfield (set of flags) of all factory features that are enabled by default. */ protected final static int DEFAULT_FACTORY_FEATURE_FLAGS = JsonFactory.Feature.collectDefaults(); /** * Bitfield (set of flags) of all parser features that are enabled * by default. */ protected final static int DEFAULT_PARSER_FEATURE_FLAGS = JsonParser.Feature.collectDefaults(); /** * Bitfield (set of flags) of all generator features that are enabled * by default. */ protected final static int DEFAULT_GENERATOR_FEATURE_FLAGS = JsonGenerator.Feature.collectDefaults(); /* /********************************************************************** /* Configured features /********************************************************************** */ /** * Set of {@link TokenStreamFactory.Feature}s enabled, as bitmask. */ protected int _factoryFeatures; /** * Set of {@link JsonParser.Feature}s enabled, as bitmask. */ protected int _streamReadFeatures; /** * Set of {@link JsonGenerator.Feature}s enabled, as bitmask. */ protected int _streamWriteFeatures; /* /********************************************************************** /* Other configuration /********************************************************************** */ /** * Optional helper object that may decorate input sources, to do * additional processing on input during parsing. */ protected InputDecorator _inputDecorator; /** * Optional helper object that may decorate output object, to do * additional processing on output during content generation. */ protected OutputDecorator _outputDecorator; /* /********************************************************************** /* Construction /********************************************************************** */ protected TSFBuilder() { _factoryFeatures = DEFAULT_FACTORY_FEATURE_FLAGS; _streamReadFeatures = DEFAULT_PARSER_FEATURE_FLAGS; _streamWriteFeatures = DEFAULT_GENERATOR_FEATURE_FLAGS; _inputDecorator = null; _outputDecorator = null; } protected TSFBuilder(JsonFactory base) { this(base._factoryFeatures, base._parserFeatures, base._generatorFeatures); } protected TSFBuilder(int factoryFeatures, int parserFeatures, int generatorFeatures) { _factoryFeatures = factoryFeatures; _streamReadFeatures = parserFeatures; _streamWriteFeatures = generatorFeatures; } // // // Accessors public int factoryFeaturesMask() { return _factoryFeatures; } public int streamReadFeatures() { return _streamReadFeatures; } public int streamWriteFeatures() { return _streamWriteFeatures; } public InputDecorator inputDecorator() { return _inputDecorator; } public OutputDecorator outputDecorator() { return _outputDecorator; } // // // Factory features public B enable(JsonFactory.Feature f) { _factoryFeatures |= f.getMask(); return _this(); } public B disable(JsonFactory.Feature f) { _factoryFeatures &= ~f.getMask(); return _this(); } public B configure(JsonFactory.Feature f, boolean state) { return state ? enable(f) : disable(f); } // // // StreamReadFeatures (replacement of non-json-specific parser features) public B enable(StreamReadFeature f) { _streamReadFeatures |= f.mappedFeature().getMask(); return _this(); } public B enable(StreamReadFeature first, StreamReadFeature... other) { _streamReadFeatures |= first.mappedFeature().getMask(); for (StreamReadFeature f : other) { _streamReadFeatures |= f.mappedFeature().getMask(); } return _this(); } public B disable(StreamReadFeature f) { _streamReadFeatures &= ~f.mappedFeature().getMask(); return _this(); } public B disable(StreamReadFeature first, StreamReadFeature... other) { _streamReadFeatures &= ~first.mappedFeature().getMask(); for (StreamReadFeature f : other) { _streamReadFeatures &= ~f.mappedFeature().getMask(); } return _this(); } public B configure(StreamReadFeature f, boolean state) { return state ? enable(f) : disable(f); } // // // StreamWriteFeatures (replacement of non-json-specific generator features) public B enable(StreamWriteFeature f) { _streamWriteFeatures |= f.mappedFeature().getMask(); return _this(); } public B enable(StreamWriteFeature first, StreamWriteFeature... other) { _streamWriteFeatures |= first.mappedFeature().getMask(); for (StreamWriteFeature f : other) { _streamWriteFeatures |= f.mappedFeature().getMask(); } return _this(); } public B disable(StreamWriteFeature f) { _streamWriteFeatures &= ~f.mappedFeature().getMask(); return _this(); } public B disable(StreamWriteFeature first, StreamWriteFeature... other) { _streamWriteFeatures &= ~first.mappedFeature().getMask(); for (StreamWriteFeature f : other) { _streamWriteFeatures &= ~f.mappedFeature().getMask(); } return _this(); } public B configure(StreamWriteFeature f, boolean state) { return state ? enable(f) : disable(f); } /* 26-Jun-2018, tatu: This should not be needed here, but due to 2.x limitations, * we do need to include it or require casting. * Specifically: since `JsonFactory` (and not `TokenStreamFactory`) is base class * for all backends, it can not expose JSON-specific builder, but this. * So let's select lesser evil(s). */ // // // JSON-specific, reads public B enable(JsonReadFeature f) { return _failNonJSON(f); } public B enable(JsonReadFeature first, JsonReadFeature... other) { return _failNonJSON(first); } public B disable(JsonReadFeature f) { return _failNonJSON(f); } public B disable(JsonReadFeature first, JsonReadFeature... other) { return _failNonJSON(first); } public B configure(JsonReadFeature f, boolean state) { return _failNonJSON(f); } private B _failNonJSON(Object feature) { throw new IllegalArgumentException("Feature "+feature.getClass().getName() +"#"+feature.toString()+" not supported for non-JSON backend"); } // // // JSON-specific, writes public B enable(JsonWriteFeature f) { return _failNonJSON(f); } public B enable(JsonWriteFeature first, JsonWriteFeature... other) { return _failNonJSON(first); } public B disable(JsonWriteFeature f) { return _failNonJSON(f); } public B disable(JsonWriteFeature first, JsonWriteFeature... other) { return _failNonJSON(first); } public B configure(JsonWriteFeature f, boolean state) { return _failNonJSON(f); } // // // Other configuration public B inputDecorator(InputDecorator dec) { _inputDecorator = dec; return _this(); } public B outputDecorator(OutputDecorator dec) { _outputDecorator = dec; return _this(); } // // // Other methods /** * Method for constructing actual {@link TokenStreamFactory} instance, given * configuration. */ public abstract F build(); // silly convenience cast method we need @SuppressWarnings("unchecked") protected final B _this() { return (B) this; } // // // Support for subtypes protected void _legacyEnable(JsonParser.Feature f) { _streamReadFeatures |= f.getMask(); } protected void _legacyDisable(JsonParser.Feature f) { _streamReadFeatures &= ~f.getMask(); } protected void _legacyEnable(JsonGenerator.Feature f) { _streamWriteFeatures |= f.getMask(); } protected void _legacyDisable(JsonGenerator.Feature f) { _streamWriteFeatures &= ~f.getMask(); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/TokenStreamFactory.java000066400000000000000000000166701356164247300330000ustar00rootroot00000000000000package com.fasterxml.jackson.core; import java.io.*; import java.net.URL; import com.fasterxml.jackson.core.io.DataOutputAsStream; /** * Intermediate base class for actual format-specific factories for constructing * parsers (reading) and generators (writing). Although full power will only be * available with Jackson 3, skeletal implementation added in 2.10 to help conversion * of code for 2.x to 3.x migration of projects depending on Jackson * * @since 2.10 */ public abstract class TokenStreamFactory implements Versioned, java.io.Serializable { private static final long serialVersionUID = 2; /* /********************************************************** /* Capability introspection /********************************************************** */ /** * Introspection method that higher-level functionality may call * to see whether underlying data format requires a stable ordering * of object properties or not. * This is usually used for determining * whether to force a stable ordering (like alphabetic ordering by name) * if no ordering if explicitly specified. *

* Default implementation returns false as JSON does NOT * require stable ordering. Formats that require ordering include positional * textual formats like CSV, and schema-based binary formats * like Avro. */ public abstract boolean requiresPropertyOrdering(); /** * Introspection method that higher-level functionality may call * to see whether underlying data format can read and write binary * data natively; that is, embeded it as-is without using encodings * such as Base64. *

* Default implementation returns false as JSON does not * support native access: all binary content must use Base64 encoding. * Most binary formats (like Smile and Avro) support native binary content. */ public abstract boolean canHandleBinaryNatively(); /** * Introspection method that can be used to check whether this * factory can create non-blocking parsers: parsers that do not * use blocking I/O abstractions but instead use a * {@link com.fasterxml.jackson.core.async.NonBlockingInputFeeder}. */ public abstract boolean canParseAsync(); /** * Method for accessing kind of {@link FormatFeature} that a parser * {@link JsonParser} produced by this factory would accept, if any; * null returned if none. * * @since 2.6 */ public abstract Class getFormatReadFeatureType(); /** * Method for accessing kind of {@link FormatFeature} that a parser * {@link JsonGenerator} produced by this factory would accept, if any; * null returned if none. * * @since 2.6 */ public abstract Class getFormatWriteFeatureType(); /* /********************************************************** /* Format detection functionality /********************************************************** */ /** * Method that can be used to quickly check whether given schema * is something that parsers and/or generators constructed by this * factory could use. Note that this means possible use, at the level * of data format (i.e. schema is for same data format as parsers and * generators this factory constructs); individual schema instances * may have further usage restrictions. * * @since 2.1 */ public abstract boolean canUseSchema(FormatSchema schema); /** * Method that returns short textual id identifying format * this factory supports. */ public abstract String getFormatName(); /* /********************************************************** /* Configuration access /********************************************************** */ public abstract boolean isEnabled(JsonParser.Feature f); public abstract boolean isEnabled(JsonGenerator.Feature f); public abstract int getParserFeatures(); public abstract int getGeneratorFeatures(); public abstract int getFormatParserFeatures(); public abstract int getFormatGeneratorFeatures(); /* /********************************************************** /* Factory methods, parsers /********************************************************** */ public abstract JsonParser createParser(byte[] data) throws IOException; public abstract JsonParser createParser(byte[] data, int offset, int len) throws IOException; public abstract JsonParser createParser(char[] content) throws IOException; public abstract JsonParser createParser(char[] content, int offset, int len) throws IOException; public abstract JsonParser createParser(DataInput in) throws IOException; public abstract JsonParser createParser(File f) throws IOException; public abstract JsonParser createParser(InputStream in) throws IOException; public abstract JsonParser createParser(Reader r) throws IOException; public abstract JsonParser createParser(String content) throws IOException; public abstract JsonParser createParser(URL url) throws IOException; public abstract JsonParser createNonBlockingByteArrayParser() throws IOException; /* /********************************************************** /* Factory methods, generators /********************************************************** */ public abstract JsonGenerator createGenerator(DataOutput out, JsonEncoding enc) throws IOException; public abstract JsonGenerator createGenerator(DataOutput out) throws IOException; public abstract JsonGenerator createGenerator(File f, JsonEncoding enc) throws IOException; public abstract JsonGenerator createGenerator(OutputStream out) throws IOException; public abstract JsonGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException; public abstract JsonGenerator createGenerator(Writer w) throws IOException; /* /********************************************************** /* Internal factory methods, other /********************************************************** */ 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. */ protected InputStream _optimizedStreamFromURL(URL url) throws IOException { if ("file".equals(url.getProtocol())) { /* Can not do this if the path refers * to a network drive on windows. This fixes the problem; * might not be needed on all platforms (NFS?), but should not * matter a lot: performance penalty of extra wrapping is more * relevant when accessing local file system. */ String host = url.getHost(); if (host == null || host.length() == 0) { // [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()); } // otherwise, let's fall through and let URL decoder do its magic } } return url.openStream(); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/TreeCodec.java000066400000000000000000000014551356164247300310440ustar00rootroot00000000000000package com.fasterxml.jackson.core; import java.io.IOException; /** * Interface that defines objects that can read and write * {@link TreeNode} instances using Streaming API. * * @since 2.3 */ public abstract class TreeCodec { public abstract T readTree(JsonParser p) throws IOException, JsonProcessingException; public abstract void writeTree(JsonGenerator g, TreeNode tree) throws IOException, JsonProcessingException; /** * @since 2.10 */ public TreeNode missingNode() { return null; } /** * @since 2.10 */ public TreeNode nullNode() { return null; } public abstract TreeNode createArrayNode(); public abstract TreeNode createObjectNode(); public abstract JsonParser treeAsTokens(TreeNode node); } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/TreeNode.java000066400000000000000000000242071356164247300307140ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; import java.util.Iterator; /** * Marker interface used to denote JSON Tree nodes, as far as * the core package knows them (which is very little): mostly * needed to allow {@link ObjectCodec} to have some level * of interoperability. * Most functionality is within JsonNode * base class in mapper package. *

* Note that in Jackson 1.x JsonNode itself * was part of core package: Jackson 2.x refactored this * since conceptually Tree Model is part of mapper package, * and so part visible to core package should * be minimized. *

* NOTE: starting with Jackson 2.2, there is more functionality * available via this class, and the intent is that this should * form actual base for multiple alternative tree representations; * for example, immutable trees could use different implementation * than mutable trees. It should also be possible to move actual * Tree Model implementation out of databind package eventually * (Jackson 3?). * * @since 2.2 */ public interface TreeNode { /* /********************************************************** /* Minimal introspection methods /********************************************************** */ /** * Method that can be used for efficient type detection * when using stream abstraction for traversing nodes. * Will return the first {@link JsonToken} that equivalent * stream event would produce (for most nodes there is just * one token but for structured/container types multiple) */ JsonToken asToken(); /** * If this node is a numeric type (as per {@link JsonToken#isNumeric}), * returns native type that node uses to store the numeric value; * otherwise returns null. * * @return Type of number contained, if any; or null if node does not * contain numeric value. */ JsonParser.NumberType numberType(); /** * Method that returns number of child nodes this node contains: * for Array nodes, number of child elements, for Object nodes, * number of fields, and for all other nodes 0. * * @return For non-container nodes returns 0; for arrays number of * contained elements, and for objects number of fields. * * @since 2.2 */ int size(); /** * Method that returns true for all value nodes: ones that * are not containers, and that do not represent "missing" nodes * in the path. Such value nodes represent String, Number, Boolean * and null values from JSON. *

* Note: one and only one of methods {@link #isValueNode}, * {@link #isContainerNode} and {@link #isMissingNode} ever * returns true for any given node. * * @since 2.2 */ boolean isValueNode(); /** * Method that returns true for container nodes: Arrays and Objects. *

* Note: one and only one of methods {@link #isValueNode}, * {@link #isContainerNode} and {@link #isMissingNode} ever * returns true for any given node. * * @since 2.2 */ boolean isContainerNode(); /** * Method that returns true for "virtual" nodes which represent * missing entries constructed by path accessor methods when * there is no actual node matching given criteria. *

* Note: one and only one of methods {@link #isValueNode}, * {@link #isContainerNode} and {@link #isMissingNode} ever * returns true for any given node. * * @since 2.2 */ boolean isMissingNode(); /** * Method that returns true if this node is an Array node, false * otherwise. * Note that if true is returned, {@link #isContainerNode} * must also return true. * * @since 2.2 */ boolean isArray(); /** * Method that returns true if this node is an Object node, false * otherwise. * Note that if true is returned, {@link #isContainerNode} * must also return true. * * @since 2.2 */ boolean isObject(); /* /********************************************************** /* Basic traversal through structured entries (Arrays, Objects) /********************************************************** */ /** * Method for accessing value of the specified field of * an object node. If this node is not an object (or it * does not have a value for specified field name), or * if there is no field with such name, null is returned. *

* NOTE: handling of explicit null values may vary between * implementations; some trees may retain explicit nulls, others * not. * * @return Node that represent value of the specified field, * if this node is an object and has value for the specified * field. Null otherwise. * * @since 2.2 */ TreeNode get(String fieldName); /** * Method for accessing value of the specified element of * an array node. For other nodes, null is returned. *

* For array nodes, index specifies * exact location within array and allows for efficient iteration * over child elements (underlying storage is guaranteed to * be efficiently indexable, i.e. has random-access to elements). * If index is less than 0, or equal-or-greater than * node.size(), null is returned; no exception is * thrown for any index. * * @return Node that represent value of the specified element, * if this node is an array and has specified element. * Null otherwise. * * @since 2.2 */ TreeNode get(int index); /** * Method for accessing value of the specified field of * an object node. * For other nodes, a "missing node" (virtual node * for which {@link #isMissingNode} returns true) is returned. * * @return Node that represent value of the specified field, * if this node is an object and has value for the specified field; * otherwise "missing node" is returned. * * @since 2.2 */ TreeNode path(String fieldName); /** * Method for accessing value of the specified element of * an array node. * For other nodes, a "missing node" (virtual node * for which {@link #isMissingNode} returns true) is returned. *

* For array nodes, index specifies * exact location within array and allows for efficient iteration * over child elements (underlying storage is guaranteed to * be efficiently indexable, i.e. has random-access to elements). * If index is less than 0, or equal-or-greater than * node.size(), "missing node" is returned; no exception is * thrown for any index. * * @return Node that represent value of the specified element, * if this node is an array and has specified element; * otherwise "missing node" is returned. * * @since 2.2 */ TreeNode path(int index); /** * Method for accessing names of all fields for this node, iff * this node is an Object node. Number of field names accessible * will be {@link #size}. * * @since 2.2 */ Iterator fieldNames(); /** * Method for locating node specified by given JSON pointer instances. * Method will never return null; if no matching node exists, * will return a node for which {@link TreeNode#isMissingNode()} returns true. * * @return Node that matches given JSON Pointer: if no match exists, * will return a node for which {@link TreeNode#isMissingNode()} returns true. * * @since 2.3 */ TreeNode at(JsonPointer ptr); /** * Convenience method that is functionally equivalent to: *

     *   return at(JsonPointer.valueOf(jsonPointerExpression));
     *
*

* Note that if the same expression is used often, it is preferable to construct * {@link JsonPointer} instance once and reuse it: this method will not perform * any caching of compiled expressions. * * @param jsonPointerExpression Expression to compile as a {@link JsonPointer} * instance * * @return Node that matches given JSON Pointer: if no match exists, * will return a node for which {@link TreeNode#isMissingNode()} returns true. * * @since 2.3 */ TreeNode at(String jsonPointerExpression) throws IllegalArgumentException; /* /********************************************************** /* Converting to/from Streaming API /********************************************************** */ /** * Method for constructing a {@link JsonParser} instance for * iterating over contents of the tree that this node is root of. * Functionally equivalent to first serializing tree using * {@link ObjectCodec} and then re-parsing but * more efficient. *

* NOTE: constructed parser instance will NOT initially point to a token, * so before passing it to deserializers, it is typically necessary to * advance it to the first available token by calling {@link JsonParser#nextToken()}. *

* Also note that calling this method will NOT pass {@link ObjectCodec} * reference, so data-binding callback methods like {@link JsonParser#readValueAs(Class)} * will not work with calling {@link JsonParser#setCodec}). * It is often better to call {@link #traverse(ObjectCodec)} to pass the codec explicitly. */ JsonParser traverse(); /** * Same as {@link #traverse()}, but additionally passes {@link com.fasterxml.jackson.core.ObjectCodec} * to use if {@link JsonParser#readValueAs(Class)} is used (otherwise caller must call * {@link JsonParser#setCodec} on response explicitly). *

* NOTE: constructed parser instance will NOT initially point to a token, * so before passing it to deserializers, it is typically necessary to * advance it to the first available token by calling {@link JsonParser#nextToken()}. * * @since 2.1 */ JsonParser traverse(ObjectCodec codec); } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/Version.java000066400000000000000000000105411356164247300306300ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; /** * Object that encapsulates versioning information of a component. * Version information includes not just version number but also * optionally group and artifact ids of the component being versioned. *

* Note that optional group and artifact id properties are new with Jackson 2.0: * if provided, they should align with Maven artifact information. */ public class Version implements Comparable, java.io.Serializable { private static final long serialVersionUID = 1L; private final static Version UNKNOWN_VERSION = new Version(0, 0, 0, null, null, null); protected final int _majorVersion; protected final int _minorVersion; protected final int _patchLevel; protected final String _groupId; protected final String _artifactId; /** * Additional information for snapshot versions; null for non-snapshot * (release) versions. */ protected final String _snapshotInfo; /** * @deprecated Use variant that takes group and artifact ids * * @since 2.1 */ @Deprecated public Version(int major, int minor, int patchLevel, String snapshotInfo) { this(major, minor, patchLevel, snapshotInfo, null, null); } public Version(int major, int minor, int patchLevel, String snapshotInfo, String groupId, String artifactId) { _majorVersion = major; _minorVersion = minor; _patchLevel = patchLevel; _snapshotInfo = snapshotInfo; _groupId = (groupId == null) ? "" : groupId; _artifactId = (artifactId == null) ? "" : artifactId; } /** * Method returns canonical "not known" version, which is used as version * in cases where actual version information is not known (instead of null). */ public static Version unknownVersion() { return UNKNOWN_VERSION; } /** * @since 2.7 to replace misspelled {@link #isUknownVersion()} */ public boolean isUnknownVersion() { return (this == UNKNOWN_VERSION); } public boolean isSnapshot() { return (_snapshotInfo != null && _snapshotInfo.length() > 0); } /** * @deprecated Since 2.7 use correctly spelled method {@link #isUnknownVersion()} */ @Deprecated public boolean isUknownVersion() { return isUnknownVersion(); } public int getMajorVersion() { return _majorVersion; } public int getMinorVersion() { return _minorVersion; } public int getPatchLevel() { return _patchLevel; } public String getGroupId() { return _groupId; } public String getArtifactId() { return _artifactId; } public String toFullString() { return _groupId + '/' + _artifactId + '/' + toString(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(_majorVersion).append('.'); sb.append(_minorVersion).append('.'); sb.append(_patchLevel); if (isSnapshot()) { sb.append('-').append(_snapshotInfo); } return sb.toString(); } @Override public int hashCode() { return _artifactId.hashCode() ^ _groupId.hashCode() + _majorVersion - _minorVersion + _patchLevel; } @Override public boolean equals(Object o) { if (o == this) return true; if (o == null) return false; if (o.getClass() != getClass()) return false; Version other = (Version) o; return (other._majorVersion == _majorVersion) && (other._minorVersion == _minorVersion) && (other._patchLevel == _patchLevel) && other._artifactId.equals(_artifactId) && other._groupId.equals(_groupId) ; } @Override public int compareTo(Version other) { if (other == this) return 0; int diff = _groupId.compareTo(other._groupId); if (diff == 0) { diff = _artifactId.compareTo(other._artifactId); if (diff == 0) { diff = _majorVersion - other._majorVersion; if (diff == 0) { diff = _minorVersion - other._minorVersion; if (diff == 0) { diff = _patchLevel - other._patchLevel; } } } } return diff; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/Versioned.java000066400000000000000000000015501356164247300311410ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; /** * Interface that those Jackson components that are explicitly versioned will implement. * Intention is to allow both plug-in components (custom extensions) and applications and * frameworks that use Jackson to detect exact version of Jackson in use. * This may be useful for example for ensuring that proper Jackson version is deployed * (beyond mechanisms that deployment system may have), as well as for possible * workarounds. */ public interface Versioned { /** * Method called to detect version of the component that implements this interface; * returned version should never be null, but may return specific "not available" * instance (see {@link Version} for details). */ Version version(); } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/async/000077500000000000000000000000001356164247300274545ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/async/ByteArrayFeeder.java000066400000000000000000000017631356164247300333430ustar00rootroot00000000000000package com.fasterxml.jackson.core.async; import java.io.IOException; /** * {@link NonBlockingInputFeeder} implementation used when feeding data * as byte arrays. * * @since 2.9 */ public interface ByteArrayFeeder extends NonBlockingInputFeeder { /** * Method that can be called to feed more data, if (and only if) * {@link #needMoreInput} returns true. * * @param data Byte array that contains data to feed: caller must ensure data remains * stable until it is fully processed (which is true when {@link #needMoreInput} * returns true) * @param offset Offset within array where input data to process starts * @param end Offset after last byte contained in the input array * * @throws IOException if the state is such that this method should not be called * (has not yet consumed existing input data, or has been marked as closed) */ public void feedInput(byte[] data, int offset, int end) throws IOException; } ByteBufferFeeder.java000066400000000000000000000014121356164247300334060ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/asyncpackage com.fasterxml.jackson.core.async; import java.io.IOException; import java.nio.ByteBuffer; /** * {@link NonBlockingInputFeeder} implementation used when feeding data * as {@link ByteBuffer} contents. * * @since 2.9 */ public interface ByteBufferFeeder extends NonBlockingInputFeeder { /** * Method that can be called to feed more data, if (and only if) * {@link NonBlockingInputFeeder#needMoreInput} returns true. * * @param buffer Buffer that contains additional input to read * * @throws IOException if the state is such that this method should not be called * (has not yet consumed existing input data, or has been marked as closed) */ public void feedInput(ByteBuffer buffer) throws IOException; } NonBlockingInputFeeder.java000066400000000000000000000024321356164247300345770ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/asyncpackage com.fasterxml.jackson.core.async; /** * Interface used by non-blocking {@link com.fasterxml.jackson.core.JsonParser} * implementations to feed input to parse. * Feeder is used by entity that feeds content to parse; at any given point * only one chunk of content can be processed so caller has to take care to * only feed more content when existing content has been parsed (which occurs * when parser's nextToken is called). Once application using * non-blocking parser has no more data to feed it should call * {@link #endOfInput} to indicate end of logical input (stream) to parse. * * @since 2.9 */ public interface NonBlockingInputFeeder { /** * Method called to check whether it is ok to feed more data: parser returns true * if it has no more content to parse (and it is ok to feed more); otherwise false * (and no data should yet be fed). */ public boolean needMoreInput(); /** * Method that should be called after last chunk of data to parse has been fed * (with feedInput in sub-class); can be called regardless of what {@link #needMoreInput} * returns. After calling this method, no more data can be fed; and parser assumes * no more data will be available. */ public void endOfInput(); } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/async/package-info.java000066400000000000000000000006231356164247300326440ustar00rootroot00000000000000/** * Package that contains abstractions needed to support optional * non-blocking decoding (parsing) functionality. * Although parsers are constructed normally via * {@link com.fasterxml.jackson.core.JsonFactory} * (and are, in fact, sub-types of {@link com.fasterxml.jackson.core.JsonParser}), * the way input is provided differs. * * @since 2.9 */ package com.fasterxml.jackson.core.async; jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/base/000077500000000000000000000000001356164247300272515ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java000066400000000000000000000414461356164247300326460ustar00rootroot00000000000000package com.fasterxml.jackson.core.base; 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 java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; /** * This base class implements part of API that a JSON generator exposes * to applications, adds shared internal methods that sub-classes * can use and adds some abstract methods sub-classes must implement. */ public abstract class GeneratorBase extends JsonGenerator { public final static int SURR1_FIRST = 0xD800; public final static int SURR1_LAST = 0xDBFF; public final static int SURR2_FIRST = 0xDC00; public final static int SURR2_LAST = 0xDFFF; /** * Set of feature masks related to features that need updates of other * local configuration or state. * * @since 2.5 */ @SuppressWarnings("deprecation") protected final static int DERIVED_FEATURES_MASK = Feature.WRITE_NUMBERS_AS_STRINGS.getMask() | Feature.ESCAPE_NON_ASCII.getMask() | Feature.STRICT_DUPLICATE_DETECTION.getMask() ; // // // Constants for validation messages (since 2.6) protected final static String WRITE_BINARY = "write a binary value"; protected final static String WRITE_BOOLEAN = "write a boolean value"; protected final static String WRITE_NULL = "write a null"; protected final static String WRITE_NUMBER = "write a number"; protected final static String WRITE_RAW = "write a raw (unencoded) value"; protected final static String WRITE_STRING = "write a string"; /** * This value is the limit of scale allowed for serializing {@link BigDecimal} * in "plain" (non-engineering) notation; intent is to prevent asymmetric * attack whereupon simple eng-notation with big scale is used to generate * huge "plain" serialization. See [core#315] for details. * * @since 2.7.7 */ protected final static int MAX_BIG_DECIMAL_SCALE = 9999; /* /********************************************************** /* Configuration /********************************************************** */ protected ObjectCodec _objectCodec; /** * Bit flag composed of bits that indicate which * {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s * are enabled. */ protected int _features; /** * Flag set to indicate that implicit conversion from number * to JSON String is needed (as per * {@link com.fasterxml.jackson.core.JsonGenerator.Feature#WRITE_NUMBERS_AS_STRINGS}). */ protected boolean _cfgNumbersAsStrings; /* /********************************************************** /* State /********************************************************** */ /** * Object that keeps track of the current contextual state * of the generator. */ protected JsonWriteContext _writeContext; /** * Flag that indicates whether generator is closed or not. Gets * set when it is closed by an explicit call * ({@link #close}). */ protected boolean _closed; /* /********************************************************** /* Life-cycle /********************************************************** */ @SuppressWarnings("deprecation") protected GeneratorBase(int features, ObjectCodec codec) { super(); _features = features; _objectCodec = codec; DupDetector dups = Feature.STRICT_DUPLICATE_DETECTION.enabledIn(features) ? DupDetector.rootDetector(this) : null; _writeContext = JsonWriteContext.createRootContext(dups); _cfgNumbersAsStrings = Feature.WRITE_NUMBERS_AS_STRINGS.enabledIn(features); } /** * @since 2.5 */ @SuppressWarnings("deprecation") protected GeneratorBase(int features, ObjectCodec codec, JsonWriteContext ctxt) { super(); _features = features; _objectCodec = codec; _writeContext = ctxt; _cfgNumbersAsStrings = Feature.WRITE_NUMBERS_AS_STRINGS.enabledIn(features); } /** * Implemented with standard version number detection algorithm, typically using * a simple generated class, with information extracted from Maven project file * during build. */ @Override public Version version() { return PackageVersion.VERSION; } @Override public Object getCurrentValue() { return _writeContext.getCurrentValue(); } @Override public void setCurrentValue(Object v) { if (_writeContext != null) { _writeContext.setCurrentValue(v); } } /* /********************************************************** /* Configuration /********************************************************** */ @Override public final boolean isEnabled(Feature f) { return (_features & f.getMask()) != 0; } @Override public int getFeatureMask() { return _features; } //public JsonGenerator configure(Feature f, boolean state) { } @SuppressWarnings("deprecation") @Override public JsonGenerator enable(Feature f) { final int mask = f.getMask(); _features |= mask; if ((mask & DERIVED_FEATURES_MASK) != 0) { // why not switch? Requires addition of a generated class, alas if (f == Feature.WRITE_NUMBERS_AS_STRINGS) { _cfgNumbersAsStrings = true; } else if (f == Feature.ESCAPE_NON_ASCII) { setHighestNonEscapedChar(127); } else if (f == Feature.STRICT_DUPLICATE_DETECTION) { if (_writeContext.getDupDetector() == null) { // but only if disabled currently _writeContext = _writeContext.withDupDetector(DupDetector.rootDetector(this)); } } } return this; } @SuppressWarnings("deprecation") @Override public JsonGenerator disable(Feature f) { final int mask = f.getMask(); _features &= ~mask; if ((mask & DERIVED_FEATURES_MASK) != 0) { if (f == Feature.WRITE_NUMBERS_AS_STRINGS) { _cfgNumbersAsStrings = false; } else if (f == Feature.ESCAPE_NON_ASCII) { setHighestNonEscapedChar(0); } else if (f == Feature.STRICT_DUPLICATE_DETECTION) { _writeContext = _writeContext.withDupDetector(null); } } return this; } @Override @Deprecated public JsonGenerator setFeatureMask(int newMask) { int changed = newMask ^ _features; _features = newMask; if (changed != 0) { _checkStdFeatureChanges(newMask, changed); } return this; } @Override // since 2.7 public JsonGenerator overrideStdFeatures(int values, int mask) { int oldState = _features; int newState = (oldState & ~mask) | (values & mask); int changed = oldState ^ newState; if (changed != 0) { _features = newState; _checkStdFeatureChanges(newState, changed); } return this; } /** * Helper method called to verify changes to standard features. * * @param newFeatureFlags Bitflag of standard features after they were changed * @param changedFeatures Bitflag of standard features for which setting * did change * * @since 2.7 */ @SuppressWarnings("deprecation") protected void _checkStdFeatureChanges(int newFeatureFlags, int changedFeatures) { if ((changedFeatures & DERIVED_FEATURES_MASK) == 0) { return; } _cfgNumbersAsStrings = Feature.WRITE_NUMBERS_AS_STRINGS.enabledIn(newFeatureFlags); if (Feature.ESCAPE_NON_ASCII.enabledIn(changedFeatures)) { if (Feature.ESCAPE_NON_ASCII.enabledIn(newFeatureFlags)) { setHighestNonEscapedChar(127); } else { setHighestNonEscapedChar(0); } } if (Feature.STRICT_DUPLICATE_DETECTION.enabledIn(changedFeatures)) { if (Feature.STRICT_DUPLICATE_DETECTION.enabledIn(newFeatureFlags)) { // enabling if (_writeContext.getDupDetector() == null) { // but only if disabled currently _writeContext = _writeContext.withDupDetector(DupDetector.rootDetector(this)); } } else { // disabling _writeContext = _writeContext.withDupDetector(null); } } } @Override public JsonGenerator useDefaultPrettyPrinter() { // Should not override a pretty printer if one already assigned. if (getPrettyPrinter() != null) { return this; } return setPrettyPrinter(_constructDefaultPrettyPrinter()); } @Override public JsonGenerator setCodec(ObjectCodec oc) { _objectCodec = oc; return this; } @Override public ObjectCodec getCodec() { return _objectCodec; } /* /********************************************************** /* Public API, accessors /********************************************************** */ /** * 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 JsonStreamContext getOutputContext() { return _writeContext; } /* /********************************************************** /* Public API, write methods, structural /********************************************************** */ //public void writeStartArray() throws IOException //public void writeEndArray() throws IOException //public void writeStartObject() throws IOException //public void writeEndObject() throws IOException @Override // since 2.8 public void writeStartObject(Object forValue) throws IOException { writeStartObject(); if (forValue != null) { setCurrentValue(forValue); } } /* /********************************************************** /* Public API, write methods, textual /********************************************************** */ @Override public void writeFieldName(SerializableString name) throws IOException { writeFieldName(name.getValue()); } //public abstract void writeString(String text) throws IOException; //public abstract void writeString(char[] text, int offset, int len) throws IOException; //public abstract void writeString(Reader reader, int len) throws IOException; //public abstract void writeRaw(String text) throws IOException,; //public abstract void writeRaw(char[] text, int offset, int len) throws IOException; @Override public void writeString(SerializableString text) throws IOException { writeString(text.getValue()); } @Override public void writeRawValue(String text) throws IOException { _verifyValueWrite("write raw value"); writeRaw(text); } @Override public void writeRawValue(String text, int offset, int len) throws IOException { _verifyValueWrite("write raw value"); writeRaw(text, offset, len); } @Override public void writeRawValue(char[] text, int offset, int len) throws IOException { _verifyValueWrite("write raw value"); writeRaw(text, offset, len); } @Override public void writeRawValue(SerializableString text) throws IOException { _verifyValueWrite("write raw value"); writeRaw(text); } @Override public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) throws IOException { // Let's implement this as "unsupported" to make it easier to add new parser impls _reportUnsupportedOperation(); return 0; } /* /********************************************************** /* Public API, write methods, primitive /********************************************************** */ // Not implemented at this level, added as placeholders /* public abstract void writeNumber(int i) public abstract void writeNumber(long l) public abstract void writeNumber(double d) public abstract void writeNumber(float f) public abstract void writeNumber(BigDecimal dec) public abstract void writeBoolean(boolean state) public abstract void writeNull() */ /* /********************************************************** /* Public API, write methods, POJOs, trees /********************************************************** */ @Override public void writeObject(Object value) throws IOException { if (value == null) { // important: call method that does check value write: writeNull(); } else { /* 02-Mar-2009, tatu: we are NOT to call _verifyValueWrite here, * because that will be done when codec actually serializes * contained POJO. If we did call it it would advance state * causing exception later on */ if (_objectCodec != null) { _objectCodec.writeValue(this, value); return; } _writeSimpleObject(value); } } @Override public void writeTree(TreeNode rootNode) throws IOException { // As with 'writeObject()', we are not check if write would work if (rootNode == null) { writeNull(); } else { if (_objectCodec == null) { throw new IllegalStateException("No ObjectCodec defined"); } _objectCodec.writeValue(this, rootNode); } } /* /********************************************************** /* Public API, low-level output handling /********************************************************** */ @Override public abstract void flush() throws IOException; @Override public void close() throws IOException { _closed = true; } @Override public boolean isClosed() { return _closed; } /* /********************************************************** /* Package methods for this, sub-classes /********************************************************** */ /** * Method called to release any buffers generator may be holding, * once generator is being closed. */ protected abstract void _releaseBuffers(); /** * Method called before trying to write a value (scalar or structured), * to verify that this is legal in current output state, as well as to * output separators if and as necessary. * * @param typeMsg Additional message used for generating exception message * if value output is NOT legal in current generator output state. */ protected abstract void _verifyValueWrite(String typeMsg) throws IOException; /** * Overridable factory method called to instantiate an appropriate {@link PrettyPrinter} * for case of "just use the default one", when {@link #useDefaultPrettyPrinter()} is called. * * @since 2.6 */ protected PrettyPrinter _constructDefaultPrettyPrinter() { return new DefaultPrettyPrinter(); } /** * Helper method used to serialize a {@link java.math.BigDecimal} as a String, * for serialization, taking into account configuration settings * * @since 2.7.7 */ protected String _asString(BigDecimal value) throws IOException { if (Feature.WRITE_BIGDECIMAL_AS_PLAIN.enabledIn(_features)) { // 24-Aug-2016, tatu: [core#315] prevent possible DoS vector int scale = value.scale(); if ((scale < -MAX_BIG_DECIMAL_SCALE) || (scale > MAX_BIG_DECIMAL_SCALE)) { _reportError(String.format( "Attempt to write plain `java.math.BigDecimal` (see JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN) with illegal scale (%d): needs to be between [-%d, %d]", scale, MAX_BIG_DECIMAL_SCALE, MAX_BIG_DECIMAL_SCALE)); } return value.toPlainString(); } return value.toString(); } /* /********************************************************** /* UTF-8 related helper method(s) /********************************************************** */ /** * @since 2.5 */ protected final int _decodeSurrogate(int surr1, int surr2) throws IOException { // First is known to be valid, but how about the other? if (surr2 < SURR2_FIRST || surr2 > SURR2_LAST) { String msg = "Incomplete surrogate pair: first char 0x"+Integer.toHexString(surr1)+", second 0x"+Integer.toHexString(surr2); _reportError(msg); } int c = 0x10000 + ((surr1 - SURR1_FIRST) << 10) + (surr2 - SURR2_FIRST); return c; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java000066400000000000000000001232021356164247300321430ustar00rootroot00000000000000package com.fasterxml.jackson.core.base; import java.io.*; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Arrays; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.JsonParser.Feature; import com.fasterxml.jackson.core.io.IOContext; import com.fasterxml.jackson.core.io.NumberInput; import com.fasterxml.jackson.core.json.DupDetector; import com.fasterxml.jackson.core.json.JsonReadContext; import com.fasterxml.jackson.core.json.PackageVersion; import com.fasterxml.jackson.core.util.ByteArrayBuilder; import com.fasterxml.jackson.core.util.TextBuffer; /** * Intermediate base class used by all Jackson {@link JsonParser} * implementations. Contains most common things that are independent * of actual underlying input source. */ public abstract class ParserBase extends ParserMinimalBase { /* /********************************************************** /* Generic I/O state /********************************************************** */ /** * I/O context for this reader. It handles buffer allocation * for the reader. */ final protected IOContext _ioContext; /** * Flag that indicates whether parser is closed or not. Gets * set when parser is either closed by explicit call * ({@link #close}) or when end-of-input is reached. */ protected boolean _closed; /* /********************************************************** /* Current input data /********************************************************** */ // Note: type of actual buffer depends on sub-class, can't include /** * Pointer to next available character in buffer */ protected int _inputPtr; /** * Index of character after last available one in the buffer. */ protected int _inputEnd; /* /********************************************************** /* Current input location information /********************************************************** */ /** * Number of characters/bytes that were contained in previous blocks * (blocks that were already processed prior to the current buffer). */ protected long _currInputProcessed; /** * Current row location of current point in input buffer, starting * from 1, if available. */ protected int _currInputRow = 1; /** * Current index of the first character of the current row in input * buffer. Needed to calculate column position, if necessary; benefit * of not having column itself is that this only has to be updated * once per line. */ protected int _currInputRowStart; /* /********************************************************** /* Information about starting location of event /* Reader is pointing to; updated on-demand /********************************************************** */ // // // Location info at point when current token was started /** * Total number of bytes/characters read before start of current token. * For big (gigabyte-sized) sizes are possible, needs to be long, * unlike pointers and sizes related to in-memory buffers. */ protected long _tokenInputTotal; /** * Input row on which current token starts, 1-based */ protected int _tokenInputRow = 1; /** * Column on input row that current token starts; 0-based (although * in the end it'll be converted to 1-based) */ protected int _tokenInputCol; /* /********************************************************** /* Parsing state /********************************************************** */ /** * Information about parser context, context in which * the next token is to be parsed (root, array, object). */ protected JsonReadContext _parsingContext; /** * Secondary token related to the next token after current one; * used if its type is known. This may be value token that * follows FIELD_NAME, for example. */ protected JsonToken _nextToken; /* /********************************************************** /* Buffer(s) for local name(s) and text content /********************************************************** */ /** * Buffer that contains contents of String values, including * field names if necessary (name split across boundary, * contains escape sequence, or access needed to char array) */ protected final TextBuffer _textBuffer; /** * Temporary buffer that is needed if field name is accessed * using {@link #getTextCharacters} method (instead of String * returning alternatives) */ protected char[] _nameCopyBuffer; /** * Flag set to indicate whether the field name is available * from the name copy buffer or not (in addition to its String * representation being available via read context) */ protected boolean _nameCopied; /** * ByteArrayBuilder is needed if 'getBinaryValue' is called. If so, * we better reuse it for remainder of content. */ protected ByteArrayBuilder _byteArrayBuilder; /** * We will hold on to decoded binary data, for duration of * current event, so that multiple calls to * {@link #getBinaryValue} will not need to decode data more * than once. */ protected byte[] _binaryValue; // Numeric value holders: multiple fields used for // for efficiency /** * Bitfield that indicates which numeric representations * have been calculated for the current type */ protected int _numTypesValid = NR_UNKNOWN; // First primitives protected int _numberInt; protected long _numberLong; protected double _numberDouble; // And then object types protected BigInteger _numberBigInt; protected BigDecimal _numberBigDecimal; // And then other information about value itself /** * Flag that indicates whether numeric value has a negative * value. That is, whether its textual representation starts * with minus character. */ protected boolean _numberNegative; /** * Length of integer part of the number, in characters */ protected int _intLength; /** * Length of the fractional part (not including decimal * point or exponent), in characters. * Not used for pure integer values. */ protected int _fractLength; /** * Length of the exponent part of the number, if any, not * including 'e' marker or sign, just digits. * Not used for pure integer values. */ protected int _expLength; /* /********************************************************** /* Life-cycle /********************************************************** */ protected ParserBase(IOContext ctxt, int features) { super(features); _ioContext = ctxt; _textBuffer = ctxt.constructTextBuffer(); DupDetector dups = Feature.STRICT_DUPLICATE_DETECTION.enabledIn(features) ? DupDetector.rootDetector(this) : null; _parsingContext = JsonReadContext.createRootContext(dups); } @Override public Version version() { return PackageVersion.VERSION; } @Override public Object getCurrentValue() { return _parsingContext.getCurrentValue(); } @Override public void setCurrentValue(Object v) { _parsingContext.setCurrentValue(v); } /* /********************************************************** /* Overrides for Feature handling /********************************************************** */ @Override public JsonParser enable(Feature f) { _features |= f.getMask(); if (f == Feature.STRICT_DUPLICATE_DETECTION) { // enabling dup detection? if (_parsingContext.getDupDetector() == null) { // but only if disabled currently _parsingContext = _parsingContext.withDupDetector(DupDetector.rootDetector(this)); } } return this; } @Override public JsonParser disable(Feature f) { _features &= ~f.getMask(); if (f == Feature.STRICT_DUPLICATE_DETECTION) { _parsingContext = _parsingContext.withDupDetector(null); } return this; } @Override @Deprecated public JsonParser setFeatureMask(int newMask) { int changes = (_features ^ newMask); if (changes != 0) { _features = newMask; _checkStdFeatureChanges(newMask, changes); } return this; } @Override // since 2.7 public JsonParser overrideStdFeatures(int values, int mask) { int oldState = _features; int newState = (oldState & ~mask) | (values & mask); int changed = oldState ^ newState; if (changed != 0) { _features = newState; _checkStdFeatureChanges(newState, changed); } return this; } /** * Helper method called to verify changes to standard features. * * @param newFeatureFlags Bitflag of standard features after they were changed * @param changedFeatures Bitflag of standard features for which setting * did change * * @since 2.7 */ protected void _checkStdFeatureChanges(int newFeatureFlags, int changedFeatures) { int f = Feature.STRICT_DUPLICATE_DETECTION.getMask(); if ((changedFeatures & f) != 0) { if ((newFeatureFlags & f) != 0) { if (_parsingContext.getDupDetector() == null) { _parsingContext = _parsingContext.withDupDetector(DupDetector.rootDetector(this)); } else { // disabling _parsingContext = _parsingContext.withDupDetector(null); } } } } /* /********************************************************** /* JsonParser impl /********************************************************** */ /** * Method that can be called to get the name associated with * the current event. */ @Override public String getCurrentName() throws IOException { // [JACKSON-395]: start markers require information from parent if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) { JsonReadContext parent = _parsingContext.getParent(); if (parent != null) { return parent.getCurrentName(); } } return _parsingContext.getCurrentName(); } @Override public void overrideCurrentName(String name) { // Simple, but need to look for START_OBJECT/ARRAY's "off-by-one" thing: JsonReadContext ctxt = _parsingContext; if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) { ctxt = ctxt.getParent(); } /* 24-Sep-2013, tatu: Unfortunate, but since we did not expose exceptions, * need to wrap this here */ try { ctxt.setCurrentName(name); } catch (IOException e) { throw new IllegalStateException(e); } } @Override public void close() throws IOException { if (!_closed) { // 19-Jan-2018, tatu: as per [core#440] need to ensure no more data assumed available _inputPtr = Math.max(_inputPtr, _inputEnd); _closed = true; try { _closeInput(); } finally { // as per [JACKSON-324], do in finally block // Also, internal buffer(s) can now be released as well _releaseBuffers(); } } } @Override public boolean isClosed() { return _closed; } @Override public JsonReadContext getParsingContext() { return _parsingContext; } /** * Method that return the starting location of the current * token; that is, position of the first character from input * that starts the current token. */ @Override public JsonLocation getTokenLocation() { return new JsonLocation(_getSourceReference(), -1L, getTokenCharacterOffset(), // bytes, chars getTokenLineNr(), getTokenColumnNr()); } /** * Method that returns location of the last processed character; * usually for error reporting purposes */ @Override public JsonLocation getCurrentLocation() { int col = _inputPtr - _currInputRowStart + 1; // 1-based return new JsonLocation(_getSourceReference(), -1L, _currInputProcessed + _inputPtr, // bytes, chars _currInputRow, col); } /* /********************************************************** /* Public API, access to token information, text and similar /********************************************************** */ @Override public boolean hasTextCharacters() { if (_currToken == JsonToken.VALUE_STRING) { return true; } // usually true if (_currToken == JsonToken.FIELD_NAME) { return _nameCopied; } return false; } @SuppressWarnings("resource") @Override // since 2.7 public byte[] getBinaryValue(Base64Variant variant) throws IOException { if (_binaryValue == null) { if (_currToken != JsonToken.VALUE_STRING) { _reportError("Current token ("+_currToken+") not VALUE_STRING, can not access as binary"); } ByteArrayBuilder builder = _getByteArrayBuilder(); _decodeBase64(getText(), builder, variant); _binaryValue = builder.toByteArray(); } return _binaryValue; } /* /********************************************************** /* Public low-level accessors /********************************************************** */ public long getTokenCharacterOffset() { return _tokenInputTotal; } public int getTokenLineNr() { return _tokenInputRow; } public int getTokenColumnNr() { // note: value of -1 means "not available"; otherwise convert from 0-based to 1-based int col = _tokenInputCol; return (col < 0) ? col : (col + 1); } /* /********************************************************** /* Abstract methods for sub-classes to implement /********************************************************** */ protected abstract void _closeInput() throws IOException; /* /********************************************************** /* Low-level reading, other /********************************************************** */ /** * 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). */ protected void _releaseBuffers() throws IOException { _textBuffer.releaseBuffers(); char[] buf = _nameCopyBuffer; if (buf != null) { _nameCopyBuffer = null; _ioContext.releaseNameCopyBuffer(buf); } } /** * Method called when an EOF is encountered between tokens. * If so, it may be a legitimate EOF, but only iff there * is no open non-root context. */ @Override protected void _handleEOF() throws JsonParseException { if (!_parsingContext.inRoot()) { String marker = _parsingContext.inArray() ? "Array" : "Object"; _reportInvalidEOF(String.format( ": expected close marker for %s (start marker at %s)", marker, _parsingContext.getStartLocation(_getSourceReference())), null); } } /** * @since 2.4 */ protected final int _eofAsNextChar() throws JsonParseException { _handleEOF(); return -1; } /* /********************************************************** /* Internal/package methods: shared/reusable builders /********************************************************** */ public ByteArrayBuilder _getByteArrayBuilder() { if (_byteArrayBuilder == null) { _byteArrayBuilder = new ByteArrayBuilder(); } else { _byteArrayBuilder.reset(); } return _byteArrayBuilder; } /* /********************************************************** /* Methods from former JsonNumericParserBase /********************************************************** */ // // // Life-cycle of number-parsing protected final JsonToken reset(boolean negative, int intLen, int fractLen, int expLen) { if (fractLen < 1 && expLen < 1) { // integer return resetInt(negative, intLen); } return resetFloat(negative, intLen, fractLen, expLen); } protected final JsonToken resetInt(boolean negative, int intLen) { _numberNegative = negative; _intLength = intLen; _fractLength = 0; _expLength = 0; _numTypesValid = NR_UNKNOWN; // to force parsing return JsonToken.VALUE_NUMBER_INT; } protected final JsonToken resetFloat(boolean negative, int intLen, int fractLen, int expLen) { _numberNegative = negative; _intLength = intLen; _fractLength = fractLen; _expLength = expLen; _numTypesValid = NR_UNKNOWN; // to force parsing return JsonToken.VALUE_NUMBER_FLOAT; } protected final JsonToken resetAsNaN(String valueStr, double value) { _textBuffer.resetWithString(valueStr); _numberDouble = value; _numTypesValid = NR_DOUBLE; return JsonToken.VALUE_NUMBER_FLOAT; } @Override public boolean isNaN() { if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) { if ((_numTypesValid & NR_DOUBLE) != 0) { // 10-Mar-2017, tatu: Alas, `Double.isFinite(d)` only added in JDK 8 double d = _numberDouble; return Double.isNaN(d) || Double.isInfinite(d); } } return false; } /* /********************************************************** /* Numeric accessors of public API /********************************************************** */ @Override public Number getNumberValue() throws IOException { if (_numTypesValid == NR_UNKNOWN) { _parseNumericValue(NR_UNKNOWN); // will also check event type } // Separate types for int types if (_currToken == JsonToken.VALUE_NUMBER_INT) { if ((_numTypesValid & NR_INT) != 0) { return _numberInt; } if ((_numTypesValid & NR_LONG) != 0) { return _numberLong; } if ((_numTypesValid & NR_BIGINT) != 0) { return _numberBigInt; } // Shouldn't get this far but if we do return _numberBigDecimal; } /* And then floating point types. But here optimal type * needs to be big decimal, to avoid losing any data? */ if ((_numTypesValid & NR_BIGDECIMAL) != 0) { return _numberBigDecimal; } if ((_numTypesValid & NR_DOUBLE) == 0) { // sanity check _throwInternal(); } return _numberDouble; } @Override public NumberType getNumberType() throws IOException { if (_numTypesValid == NR_UNKNOWN) { _parseNumericValue(NR_UNKNOWN); // will also check event type } if (_currToken == JsonToken.VALUE_NUMBER_INT) { if ((_numTypesValid & NR_INT) != 0) { return NumberType.INT; } if ((_numTypesValid & NR_LONG) != 0) { return NumberType.LONG; } return NumberType.BIG_INTEGER; } /* And then floating point types. Here optimal type * needs to be big decimal, to avoid losing any data? * However... using BD is slow, so let's allow returning * double as type if no explicit call has been made to access * data as BD? */ if ((_numTypesValid & NR_BIGDECIMAL) != 0) { return NumberType.BIG_DECIMAL; } return NumberType.DOUBLE; } @Override public int getIntValue() throws IOException { if ((_numTypesValid & NR_INT) == 0) { if (_numTypesValid == NR_UNKNOWN) { // not parsed at all return _parseIntValue(); } if ((_numTypesValid & NR_INT) == 0) { // wasn't an int natively? convertNumberToInt(); // let's make it so, if possible } } return _numberInt; } @Override public long getLongValue() throws IOException { if ((_numTypesValid & NR_LONG) == 0) { if (_numTypesValid == NR_UNKNOWN) { _parseNumericValue(NR_LONG); } if ((_numTypesValid & NR_LONG) == 0) { convertNumberToLong(); } } return _numberLong; } @Override public BigInteger getBigIntegerValue() throws IOException { if ((_numTypesValid & NR_BIGINT) == 0) { if (_numTypesValid == NR_UNKNOWN) { _parseNumericValue(NR_BIGINT); } if ((_numTypesValid & NR_BIGINT) == 0) { convertNumberToBigInteger(); } } return _numberBigInt; } @Override public float getFloatValue() throws IOException { double value = getDoubleValue(); /* 22-Jan-2009, tatu: Bounds/range checks would be tricky * here, so let's not bother even trying... */ /* if (value < -Float.MAX_VALUE || value > MAX_FLOAT_D) { _reportError("Numeric value ("+getText()+") out of range of Java float"); } */ return (float) value; } @Override public double getDoubleValue() throws IOException { if ((_numTypesValid & NR_DOUBLE) == 0) { if (_numTypesValid == NR_UNKNOWN) { _parseNumericValue(NR_DOUBLE); } if ((_numTypesValid & NR_DOUBLE) == 0) { convertNumberToDouble(); } } return _numberDouble; } @Override public BigDecimal getDecimalValue() throws IOException { if ((_numTypesValid & NR_BIGDECIMAL) == 0) { if (_numTypesValid == NR_UNKNOWN) { _parseNumericValue(NR_BIGDECIMAL); } if ((_numTypesValid & NR_BIGDECIMAL) == 0) { convertNumberToBigDecimal(); } } return _numberBigDecimal; } /* /********************************************************** /* Conversion from textual to numeric representation /********************************************************** */ /** * Method that will parse actual numeric value out of a syntactically * valid number value. Type it will parse into depends on whether * it is a floating point number, as well as its magnitude: smallest * legal type (of ones available) is used for efficiency. * * @param expType Numeric type that we will immediately need, if any; * mostly necessary to optimize handling of floating point numbers */ protected void _parseNumericValue(int expType) throws IOException { // Int or float? if (_currToken == JsonToken.VALUE_NUMBER_INT) { int len = _intLength; // First: optimization for simple int if (len <= 9) { int i = _textBuffer.contentsAsInt(_numberNegative); _numberInt = i; _numTypesValid = NR_INT; return; } if (len <= 18) { // definitely fits AND is easy to parse using 2 int parse calls long l = _textBuffer.contentsAsLong(_numberNegative); // Might still fit in int, need to check if (len == 10) { if (_numberNegative) { if (l >= MIN_INT_L) { _numberInt = (int) l; _numTypesValid = NR_INT; return; } } else { if (l <= MAX_INT_L) { _numberInt = (int) l; _numTypesValid = NR_INT; return; } } } _numberLong = l; _numTypesValid = NR_LONG; return; } _parseSlowInt(expType); return; } if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) { _parseSlowFloat(expType); return; } _reportError("Current token (%s) not numeric, can not use numeric value accessors", _currToken); } /** * @since 2.6 */ protected int _parseIntValue() throws IOException { // Inlined variant of: _parseNumericValue(NR_INT) if (_currToken == JsonToken.VALUE_NUMBER_INT) { if (_intLength <= 9) { int i = _textBuffer.contentsAsInt(_numberNegative); _numberInt = i; _numTypesValid = NR_INT; return i; } } // if not optimizable, use more generic _parseNumericValue(NR_INT); if ((_numTypesValid & NR_INT) == 0) { convertNumberToInt(); } return _numberInt; } private void _parseSlowFloat(int expType) throws IOException { /* Nope: floating point. Here we need to be careful to get * optimal parsing strategy: choice is between accurate but * slow (BigDecimal) and lossy but fast (Double). For now * let's only use BD when explicitly requested -- it can * still be constructed correctly at any point since we do * retain textual representation */ try { if (expType == NR_BIGDECIMAL) { _numberBigDecimal = _textBuffer.contentsAsDecimal(); _numTypesValid = NR_BIGDECIMAL; } else { // Otherwise double has to do _numberDouble = _textBuffer.contentsAsDouble(); _numTypesValid = NR_DOUBLE; } } catch (NumberFormatException nex) { // Can this ever occur? Due to overflow, maybe? _wrapError("Malformed numeric value ("+_longNumberDesc(_textBuffer.contentsAsString())+")", nex); } } private void _parseSlowInt(int expType) throws IOException { String numStr = _textBuffer.contentsAsString(); try { int len = _intLength; char[] buf = _textBuffer.getTextBuffer(); int offset = _textBuffer.getTextOffset(); if (_numberNegative) { ++offset; } // Some long cases still... if (NumberInput.inLongRange(buf, offset, len, _numberNegative)) { // Probably faster to construct a String, call parse, than to use BigInteger _numberLong = Long.parseLong(numStr); _numTypesValid = NR_LONG; } else { // 16-Oct-2018, tatu: Need to catch "too big" early due to [jackson-core#488] if ((expType == NR_INT) || (expType == NR_LONG)) { _reportTooLongIntegral(expType, numStr); } if ((expType == NR_DOUBLE) || (expType == NR_FLOAT)) { _numberDouble = NumberInput.parseDouble(numStr); _numTypesValid = NR_DOUBLE; } else { // nope, need the heavy guns... (rare case) _numberBigInt = new BigInteger(numStr); _numTypesValid = NR_BIGINT; } } } catch (NumberFormatException nex) { // Can this ever occur? Due to overflow, maybe? _wrapError("Malformed numeric value ("+_longNumberDesc(numStr)+")", nex); } } // @since 2.9.8 protected void _reportTooLongIntegral(int expType, String rawNum) throws IOException { if (expType == NR_INT) { reportOverflowInt(rawNum); } else { reportOverflowLong(rawNum); } } /* /********************************************************** /* Numeric conversions /********************************************************** */ protected void convertNumberToInt() throws IOException { // First, converting from long ought to be easy if ((_numTypesValid & NR_LONG) != 0) { // Let's verify it's lossless conversion by simple roundtrip int result = (int) _numberLong; if (((long) result) != _numberLong) { reportOverflowInt(getText(), currentToken()); } _numberInt = result; } else if ((_numTypesValid & NR_BIGINT) != 0) { if (BI_MIN_INT.compareTo(_numberBigInt) > 0 || BI_MAX_INT.compareTo(_numberBigInt) < 0) { reportOverflowInt(); } _numberInt = _numberBigInt.intValue(); } else if ((_numTypesValid & NR_DOUBLE) != 0) { // Need to check boundaries if (_numberDouble < MIN_INT_D || _numberDouble > MAX_INT_D) { reportOverflowInt(); } _numberInt = (int) _numberDouble; } else if ((_numTypesValid & NR_BIGDECIMAL) != 0) { if (BD_MIN_INT.compareTo(_numberBigDecimal) > 0 || BD_MAX_INT.compareTo(_numberBigDecimal) < 0) { reportOverflowInt(); } _numberInt = _numberBigDecimal.intValue(); } else { _throwInternal(); } _numTypesValid |= NR_INT; } protected void convertNumberToLong() throws IOException { if ((_numTypesValid & NR_INT) != 0) { _numberLong = (long) _numberInt; } else if ((_numTypesValid & NR_BIGINT) != 0) { if (BI_MIN_LONG.compareTo(_numberBigInt) > 0 || BI_MAX_LONG.compareTo(_numberBigInt) < 0) { reportOverflowLong(); } _numberLong = _numberBigInt.longValue(); } else if ((_numTypesValid & NR_DOUBLE) != 0) { // Need to check boundaries if (_numberDouble < MIN_LONG_D || _numberDouble > MAX_LONG_D) { reportOverflowLong(); } _numberLong = (long) _numberDouble; } else if ((_numTypesValid & NR_BIGDECIMAL) != 0) { if (BD_MIN_LONG.compareTo(_numberBigDecimal) > 0 || BD_MAX_LONG.compareTo(_numberBigDecimal) < 0) { reportOverflowLong(); } _numberLong = _numberBigDecimal.longValue(); } else { _throwInternal(); } _numTypesValid |= NR_LONG; } protected void convertNumberToBigInteger() throws IOException { if ((_numTypesValid & NR_BIGDECIMAL) != 0) { // here it'll just get truncated, no exceptions thrown _numberBigInt = _numberBigDecimal.toBigInteger(); } else if ((_numTypesValid & NR_LONG) != 0) { _numberBigInt = BigInteger.valueOf(_numberLong); } else if ((_numTypesValid & NR_INT) != 0) { _numberBigInt = BigInteger.valueOf(_numberInt); } else if ((_numTypesValid & NR_DOUBLE) != 0) { _numberBigInt = BigDecimal.valueOf(_numberDouble).toBigInteger(); } else { _throwInternal(); } _numTypesValid |= NR_BIGINT; } protected void convertNumberToDouble() throws IOException { /* 05-Aug-2008, tatus: Important note: this MUST start with * more accurate representations, since we don't know which * value is the original one (others get generated when * requested) */ if ((_numTypesValid & NR_BIGDECIMAL) != 0) { _numberDouble = _numberBigDecimal.doubleValue(); } else if ((_numTypesValid & NR_BIGINT) != 0) { _numberDouble = _numberBigInt.doubleValue(); } else if ((_numTypesValid & NR_LONG) != 0) { _numberDouble = (double) _numberLong; } else if ((_numTypesValid & NR_INT) != 0) { _numberDouble = (double) _numberInt; } else { _throwInternal(); } _numTypesValid |= NR_DOUBLE; } protected void convertNumberToBigDecimal() throws IOException { /* 05-Aug-2008, tatus: Important note: this MUST start with * more accurate representations, since we don't know which * value is the original one (others get generated when * requested) */ if ((_numTypesValid & NR_DOUBLE) != 0) { /* 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) { _numberBigDecimal = new BigDecimal(_numberBigInt); } else if ((_numTypesValid & NR_LONG) != 0) { _numberBigDecimal = BigDecimal.valueOf(_numberLong); } else if ((_numTypesValid & NR_INT) != 0) { _numberBigDecimal = BigDecimal.valueOf(_numberInt); } else { _throwInternal(); } _numTypesValid |= NR_BIGDECIMAL; } /* /********************************************************** /* Internal/package methods: Error reporting /********************************************************** */ protected void _reportMismatchedEndMarker(int actCh, char expCh) throws JsonParseException { JsonReadContext ctxt = getParsingContext(); _reportError(String.format( "Unexpected close marker '%s': expected '%c' (for %s starting at %s)", (char) actCh, expCh, ctxt.typeDesc(), ctxt.getStartLocation(_getSourceReference()))); } @SuppressWarnings("deprecation") protected char _handleUnrecognizedCharacterEscape(char ch) throws JsonProcessingException { // as per [JACKSON-300] if (isEnabled(Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)) { return ch; } // and [JACKSON-548] if (ch == '\'' && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { return ch; } _reportError("Unrecognized character escape "+_getCharDesc(ch)); return ch; } /** * Method called to report a problem with unquoted control character. * Note: it is possible to suppress some instances of * exception by enabling {@link Feature#ALLOW_UNQUOTED_CONTROL_CHARS}. */ @SuppressWarnings("deprecation") protected void _throwUnquotedSpace(int i, String ctxtDesc) throws JsonParseException { // JACKSON-208; possible to allow unquoted control chars: if (!isEnabled(Feature.ALLOW_UNQUOTED_CONTROL_CHARS) || i > INT_SPACE) { char c = (char) i; String msg = "Illegal unquoted character ("+_getCharDesc(c)+"): has to be escaped using backslash to be included in "+ctxtDesc; _reportError(msg); } } /** * @return Description to use as "valid tokens" in an exception message about * invalid (unrecognized) JSON token: called when parser finds something that * looks like unquoted textual token * * @since 2.10 */ protected String _validJsonTokenList() throws IOException { return _validJsonValueList(); } /** * @return Description to use as "valid JSON values" in an exception message about * invalid (unrecognized) JSON value: called when parser finds something that * does not look like a value or separator. * * @since 2.10 */ @SuppressWarnings("deprecation") protected String _validJsonValueList() throws IOException { if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { return "(JSON String, Number (or 'NaN'/'INF'/'+INF'), Array, Object or token 'null', 'true' or 'false')"; } return "(JSON String, Number, Array, Object or token 'null', 'true' or 'false')"; } /* /********************************************************** /* Base64 handling support /********************************************************** */ /** * Method that sub-classes must implement to support escaped sequences * in base64-encoded sections. * Sub-classes that do not need base64 support can leave this as is */ protected char _decodeEscaped() throws IOException { throw new UnsupportedOperationException(); } protected final int _decodeBase64Escape(Base64Variant b64variant, int 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); } int unescaped = _decodeEscaped(); // if white space, skip if first triplet; otherwise errors if (unescaped <= INT_SPACE) { if (index == 0) { // whitespace only allowed to be skipped between triplets return -1; } } // otherwise try to find actual triplet value int bits = b64variant.decodeBase64Char(unescaped); if (bits < 0) { if (bits != Base64Variant.BASE64_VALUE_PADDING) { throw reportInvalidBase64Char(b64variant, unescaped, index); } } return bits; } protected final int _decodeBase64Escape(Base64Variant b64variant, char ch, int index) throws IOException { if (ch != '\\') { throw reportInvalidBase64Char(b64variant, ch, index); } char unescaped = _decodeEscaped(); // if white space, skip if first triplet; otherwise errors if (unescaped <= INT_SPACE) { if (index == 0) { // whitespace only allowed to be skipped between triplets return -1; } } // otherwise try to find actual triplet value int bits = b64variant.decodeBase64Char(unescaped); if (bits < 0) { // second check since padding can only be 3rd or 4th byte (index #2 or #3) if ((bits != Base64Variant.BASE64_VALUE_PADDING) || (index < 2)) { throw reportInvalidBase64Char(b64variant, unescaped, index); } } return bits; } protected IllegalArgumentException reportInvalidBase64Char(Base64Variant b64variant, int ch, int bindex) throws IllegalArgumentException { return reportInvalidBase64Char(b64variant, ch, bindex, null); } /** * @param bindex Relative index within base64 character unit; between 0 * and 3 (as unit has exactly 4 characters) */ protected IllegalArgumentException reportInvalidBase64Char(Base64Variant b64variant, int ch, int bindex, String msg) throws IllegalArgumentException { String base; if (ch <= INT_SPACE) { base = String.format("Illegal white space character (code 0x%s) as character #%d of 4-char base64 unit: can only used between units", Integer.toHexString(ch), (bindex+1)); } else if (b64variant.usesPaddingChar(ch)) { base = "Unexpected padding character ('"+b64variant.getPaddingChar()+"') as character #"+(bindex+1)+" of 4-char base64 unit: padding only legal as 3rd or 4th character"; } else if (!Character.isDefined(ch) || Character.isISOControl(ch)) { // Not sure if we can really get here... ? (most illegal xml chars are caught at lower level) base = "Illegal character (code 0x"+Integer.toHexString(ch)+") in base64 content"; } else { base = "Illegal character '"+((char)ch)+"' (code 0x"+Integer.toHexString(ch)+") in base64 content"; } if (msg != null) { base = base + ": " + msg; } return new IllegalArgumentException(base); } // since 2.9.8 protected void _handleBase64MissingPadding(Base64Variant b64variant) throws IOException { _reportError(b64variant.missingPaddingMessage()); } /* /********************************************************** /* Internal/package methods: other /********************************************************** */ /** * Helper method used to encapsulate logic of including (or not) of * "source reference" when constructing {@link JsonLocation} instances. * * @since 2.9 */ protected Object _getSourceReference() { if (JsonParser.Feature.INCLUDE_SOURCE_IN_LOCATION.enabledIn(_features)) { return _ioContext.getSourceReference(); } return null; } protected static int[] growArrayBy(int[] arr, int more) { if (arr == null) { return new int[more]; } return Arrays.copyOf(arr, arr.length + more); } /* /********************************************************** /* 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 { } } ParserMinimalBase.java000066400000000000000000000612661356164247300334060ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/basepackage com.fasterxml.jackson.core.base; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.exc.InputCoercionException; 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; import static com.fasterxml.jackson.core.JsonTokenId.*; /** * Intermediate base class used by all Jackson {@link JsonParser} * implementations, but does not add any additional fields that depend * on particular method of obtaining input. *

* Note that 'minimal' here mostly refers to minimal number of fields * (size) and functionality that is specific to certain types * of parser implementations; but not necessarily to number of methods. */ public abstract class ParserMinimalBase extends JsonParser { // Control chars: protected final static int INT_TAB = '\t'; protected final static int INT_LF = '\n'; protected final static int INT_CR = '\r'; protected final static int INT_SPACE = 0x0020; // Markup protected final static int INT_LBRACKET = '['; protected final static int INT_RBRACKET = ']'; protected final static int INT_LCURLY = '{'; protected final static int INT_RCURLY = '}'; protected final static int INT_QUOTE = '"'; protected final static int INT_APOS = '\''; protected final static int INT_BACKSLASH = '\\'; protected final static int INT_SLASH = '/'; protected final static int INT_ASTERISK = '*'; protected final static int INT_COLON = ':'; protected final static int INT_COMMA = ','; protected final static int INT_HASH = '#'; // Number chars protected final static int INT_0 = '0'; protected final static int INT_9 = '9'; protected final static int INT_MINUS = '-'; protected final static int INT_PLUS = '+'; protected final static int INT_PERIOD = '.'; protected final static int INT_e = 'e'; protected final static int INT_E = 'E'; protected final static char CHAR_NULL = '\0'; /** * @since 2.9 */ protected final static byte[] NO_BYTES = new byte[0]; /** * @since 2.9 */ protected final static int[] NO_INTS = new int[0]; /* /********************************************************** /* Constants and fields of former 'JsonNumericParserBase' /********************************************************** */ protected final static int NR_UNKNOWN = 0; // First, integer types protected final static int NR_INT = 0x0001; protected final static int NR_LONG = 0x0002; protected final static int NR_BIGINT = 0x0004; // And then floating point types protected final static int NR_DOUBLE = 0x008; protected final static int NR_BIGDECIMAL = 0x0010; /** * NOTE! Not used by JSON implementation but used by many of binary codecs * * @since 2.9 */ protected final static int NR_FLOAT = 0x020; // Also, we need some numeric constants protected final static BigInteger BI_MIN_INT = BigInteger.valueOf(Integer.MIN_VALUE); protected final static BigInteger BI_MAX_INT = BigInteger.valueOf(Integer.MAX_VALUE); protected final static BigInteger BI_MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE); protected final static BigInteger BI_MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE); protected final static BigDecimal BD_MIN_LONG = new BigDecimal(BI_MIN_LONG); protected final static BigDecimal BD_MAX_LONG = new BigDecimal(BI_MAX_LONG); protected final static BigDecimal BD_MIN_INT = new BigDecimal(BI_MIN_INT); protected final static BigDecimal BD_MAX_INT = new BigDecimal(BI_MAX_INT); protected final static long MIN_INT_L = (long) Integer.MIN_VALUE; protected final static long MAX_INT_L = (long) Integer.MAX_VALUE; // These are not very accurate, but have to do... (for bounds checks) protected final static double MIN_LONG_D = (double) Long.MIN_VALUE; protected final static double MAX_LONG_D = (double) Long.MAX_VALUE; protected final static double MIN_INT_D = (double) Integer.MIN_VALUE; protected final static double MAX_INT_D = (double) Integer.MAX_VALUE; /* /********************************************************** /* Misc other constants /********************************************************** */ /** * Maximum number of characters to include in token reported * as part of error messages. * * @since 2.9 */ protected final static int MAX_ERROR_TOKEN_LENGTH = 256; /* /********************************************************** /* Minimal generally useful state /********************************************************** */ /** * Last token retrieved via {@link #nextToken}, if any. * Null before the first call to nextToken(), * as well as if token has been explicitly cleared */ protected JsonToken _currToken; /** * Last cleared token, if any: that is, value that was in * effect when {@link #clearCurrentToken} was called. */ protected JsonToken _lastClearedToken; /* /********************************************************** /* Life-cycle /********************************************************** */ protected ParserMinimalBase() { } protected ParserMinimalBase(int features) { super(features); } // NOTE: had base impl in 2.3 and before; but shouldn't // public abstract Version version(); /* /********************************************************** /* Configuration overrides if any /********************************************************** */ // from base class: //public void enableFeature(Feature f) //public void disableFeature(Feature f) //public void setFeature(Feature f, boolean state) //public boolean isFeatureEnabled(Feature f) /* /********************************************************** /* JsonParser impl /********************************************************** */ @Override public abstract JsonToken nextToken() throws IOException; @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(); } @Override public boolean hasCurrentToken() { return _currToken != null; } @Override public boolean hasTokenId(int id) { final JsonToken t = _currToken; if (t == null) { return (JsonTokenId.ID_NO_TOKEN == id); } return t.id() == id; } @Override public boolean hasToken(JsonToken t) { return (_currToken == t); } @Override public boolean isExpectedStartArrayToken() { return _currToken == JsonToken.START_ARRAY; } @Override public boolean isExpectedStartObjectToken() { return _currToken == JsonToken.START_OBJECT; } @Override public JsonToken nextValue() throws IOException { // Implementation should be as trivial as follows; only needs to change if // we are to skip other tokens (for example, if comments were exposed as tokens) JsonToken t = nextToken(); if (t == JsonToken.FIELD_NAME) { t = nextToken(); } return t; } @Override public JsonParser skipChildren() throws IOException { if (_currToken != JsonToken.START_OBJECT && _currToken != JsonToken.START_ARRAY) { return this; } int open = 1; // Since proper matching of start/end markers is handled // by nextToken(), we'll just count nesting levels here while (true) { JsonToken t = nextToken(); if (t == null) { _handleEOF(); /* given constraints, above should never return; * however, FindBugs doesn't know about it and * complains... so let's add dummy break here */ return this; } if (t.isStructStart()) { ++open; } else if (t.isStructEnd()) { if (--open == 0) { return this; } // 23-May-2018, tatu: [core#463] Need to consider non-blocking case... } else if (t == JsonToken.NOT_AVAILABLE) { // Nothing much we can do except to either return `null` (which seems wrong), // or, what we actually do, signal error _reportError("Not enough content available for `skipChildren()`: non-blocking parser? (%s)", getClass().getName()); } } } /** * Method sub-classes need to implement */ protected abstract void _handleEOF() throws JsonParseException; //public JsonToken getCurrentToken() //public boolean hasCurrentToken() @Override public abstract String getCurrentName() throws IOException; @Override public abstract void close() throws IOException; @Override public abstract boolean isClosed(); @Override public abstract JsonStreamContext getParsingContext(); // public abstract JsonLocation getTokenLocation(); // public abstract JsonLocation getCurrentLocation(); /* /********************************************************** /* Public API, token state overrides /********************************************************** */ @Override public void clearCurrentToken() { if (_currToken != null) { _lastClearedToken = _currToken; _currToken = null; } } @Override public JsonToken getLastClearedToken() { return _lastClearedToken; } @Override public abstract void overrideCurrentName(String name); /* /********************************************************** /* Public API, access to token information, text /********************************************************** */ @Override public abstract String getText() throws IOException; @Override public abstract char[] getTextCharacters() throws IOException; @Override public abstract boolean hasTextCharacters(); @Override public abstract int getTextLength() throws IOException; @Override public abstract int getTextOffset() throws IOException; /* /********************************************************** /* Public API, access to token information, binary /********************************************************** */ @Override public abstract byte[] getBinaryValue(Base64Variant b64variant) throws IOException; /* /********************************************************** /* Public API, access with conversion/coercion /********************************************************** */ @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { JsonToken t = _currToken; if (t != null) { switch (t.id()) { case ID_STRING: String str = getText().trim(); if ("true".equals(str)) { return true; } if ("false".equals(str)) { return false; } if (_hasTextualNull(str)) { return false; } break; case ID_NUMBER_INT: return getIntValue() != 0; case ID_TRUE: return true; case ID_FALSE: case ID_NULL: return false; case ID_EMBEDDED_OBJECT: Object value = getEmbeddedObject(); if (value instanceof Boolean) { return (Boolean) value; } break; default: } } return defaultValue; } @Override public int getValueAsInt() throws IOException { JsonToken t = _currToken; if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) { return getIntValue(); } return getValueAsInt(0); } @Override public int getValueAsInt(int defaultValue) throws IOException { JsonToken t = _currToken; if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) { return getIntValue(); } if (t != null) { switch (t.id()) { case ID_STRING: String str = getText(); if (_hasTextualNull(str)) { return 0; } return NumberInput.parseAsInt(str, defaultValue); case ID_TRUE: return 1; case ID_FALSE: return 0; case ID_NULL: return 0; case ID_EMBEDDED_OBJECT: Object value = getEmbeddedObject(); if (value instanceof Number) { return ((Number) value).intValue(); } } } return defaultValue; } @Override public long getValueAsLong() throws IOException { JsonToken t = _currToken; if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) { return getLongValue(); } return getValueAsLong(0L); } @Override public long getValueAsLong(long defaultValue) throws IOException { JsonToken t = _currToken; if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) { return getLongValue(); } if (t != null) { switch (t.id()) { case ID_STRING: String str = getText(); if (_hasTextualNull(str)) { return 0L; } return NumberInput.parseAsLong(str, defaultValue); case ID_TRUE: return 1L; case ID_FALSE: case ID_NULL: return 0L; case ID_EMBEDDED_OBJECT: Object value = getEmbeddedObject(); if (value instanceof Number) { return ((Number) value).longValue(); } } } return defaultValue; } @Override public double getValueAsDouble(double defaultValue) throws IOException { JsonToken t = _currToken; if (t != null) { switch (t.id()) { case ID_STRING: String str = getText(); if (_hasTextualNull(str)) { return 0L; } return NumberInput.parseAsDouble(str, defaultValue); case ID_NUMBER_INT: case ID_NUMBER_FLOAT: return getDoubleValue(); case ID_TRUE: return 1.0; case ID_FALSE: case ID_NULL: return 0.0; case ID_EMBEDDED_OBJECT: Object value = this.getEmbeddedObject(); if (value instanceof Number) { return ((Number) value).doubleValue(); } } } return defaultValue; } @Override public String getValueAsString() throws IOException { if (_currToken == JsonToken.VALUE_STRING) { return getText(); } if (_currToken == JsonToken.FIELD_NAME) { return getCurrentName(); } return getValueAsString(null); } @Override public String getValueAsString(String defaultValue) throws IOException { if (_currToken == JsonToken.VALUE_STRING) { return getText(); } if (_currToken == JsonToken.FIELD_NAME) { return getCurrentName(); } if (_currToken == null || _currToken == JsonToken.VALUE_NULL || !_currToken.isScalarValue()) { return defaultValue; } return getText(); } /* /********************************************************** /* Base64 decoding /********************************************************** */ /** * Helper method that can be used for base64 decoding in cases where * encoded content has already been read as a String. */ protected void _decodeBase64(String str, ByteArrayBuilder builder, Base64Variant b64variant) throws IOException { try { b64variant.decode(str, builder); } catch (IllegalArgumentException e) { _reportError(e.getMessage()); } } /* /********************************************************** /* Coercion helper methods (overridable) /********************************************************** */ /** * Helper method used to determine whether we are currently pointing to * a String value of "null" (NOT a null token); and, if so, that parser * is to recognize and return it similar to if it was real null token. * * @since 2.3 */ protected boolean _hasTextualNull(String value) { return "null".equals(value); } /* /********************************************************** /* Error reporting /********************************************************** */ protected void reportUnexpectedNumberChar(int ch, String comment) throws JsonParseException { String msg = String.format("Unexpected character (%s) in numeric value", _getCharDesc(ch)); if (comment != null) { msg += ": "+comment; } _reportError(msg); } /** * Method called to throw an exception for input token that looks like a number * based on first character(s), but is not valid according to rules of format. * In case of JSON this also includes invalid forms like positive sign and * leading zeroes. */ protected void reportInvalidNumber(String msg) throws JsonParseException { _reportError("Invalid numeric value: "+msg); } /** * Method called to throw an exception for integral (not floating point) input * token with value outside of Java signed 32-bit range when requested as {@link int}. * Result will be {@link InputCoercionException} being thrown. */ protected void reportOverflowInt() throws IOException { reportOverflowInt(getText()); } // @since 2.10 protected void reportOverflowInt(String numDesc) throws IOException { reportOverflowInt(numDesc, JsonToken.VALUE_NUMBER_INT); } // @since 2.10 protected void reportOverflowInt(String numDesc, JsonToken inputType) throws IOException { _reportInputCoercion(String.format("Numeric value (%s) out of range of int (%d - %s)", _longIntegerDesc(numDesc), Integer.MIN_VALUE, Integer.MAX_VALUE), inputType, Integer.TYPE); } /** * Method called to throw an exception for integral (not floating point) input * token with value outside of Java signed 64-bit range when requested as {@link long}. * Result will be {@link InputCoercionException} being thrown. */ protected void reportOverflowLong() throws IOException { reportOverflowLong(getText()); } // @since 2.10 protected void reportOverflowLong(String numDesc) throws IOException { reportOverflowLong(numDesc, JsonToken.VALUE_NUMBER_INT); } // @since 2.10 protected void reportOverflowLong(String numDesc, JsonToken inputType) throws IOException { _reportInputCoercion(String.format("Numeric value (%s) out of range of long (%d - %s)", _longIntegerDesc(numDesc), Long.MIN_VALUE, Long.MAX_VALUE), inputType, Long.TYPE); } /** * @since 2.10 */ protected void _reportInputCoercion(String msg, JsonToken inputType, Class targetType) throws InputCoercionException { throw new InputCoercionException(this, msg, inputType, targetType); } // @since 2.9.8 protected String _longIntegerDesc(String rawNum) { int rawLen = rawNum.length(); if (rawLen < 1000) { return rawNum; } if (rawNum.startsWith("-")) { rawLen -= 1; } return String.format("[Integer with %d digits]", rawLen); } // @since 2.9.8 protected String _longNumberDesc(String rawNum) { int rawLen = rawNum.length(); if (rawLen < 1000) { return rawNum; } if (rawNum.startsWith("-")) { rawLen -= 1; } return String.format("[number with %d characters]", rawLen); } protected void _reportUnexpectedChar(int ch, String comment) throws JsonParseException { if (ch < 0) { // sanity check _reportInvalidEOF(); } String msg = String.format("Unexpected character (%s)", _getCharDesc(ch)); if (comment != null) { msg += ": "+comment; } _reportError(msg); } protected void _reportInvalidEOF() throws JsonParseException { _reportInvalidEOF(" in "+_currToken, _currToken); } /** * @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"); } protected void _throwInvalidSpace(int i) throws JsonParseException { char c = (char) i; String msg = "Illegal character ("+_getCharDesc(c)+"): only regular white space (\\r, \\n, \\t) is allowed between tokens"; _reportError(msg); } /* /********************************************************** /* Error reporting, generic /********************************************************** */ protected final static String _getCharDesc(int ch) { char c = (char) ch; if (Character.isISOControl(c)) { return "(CTRL-CHAR, code "+ch+")"; } if (ch > 255) { return "'"+c+"' (code "+ch+" / 0x"+Integer.toHexString(ch)+")"; } return "'"+c+"' (code "+ch+")"; } protected final void _reportError(String msg) throws JsonParseException { throw _constructError(msg); } // @since 2.9 protected final void _reportError(String msg, Object arg) throws JsonParseException { throw _constructError(String.format(msg, arg)); } // @since 2.9 protected final void _reportError(String msg, Object arg1, Object arg2) throws JsonParseException { throw _constructError(String.format(msg, arg1, arg2)); } protected final void _wrapError(String msg, Throwable t) throws JsonParseException { throw _constructError(msg, t); } protected final void _throwInternal() { VersionUtil.throwInternal(); } protected final JsonParseException _constructError(String msg, Throwable t) { return new JsonParseException(this, msg, t); } protected static byte[] _asciiBytes(String str) { byte[] b = new byte[str.length()]; for (int i = 0, len = str.length(); i < len; ++i) { b[i] = (byte) str.charAt(i); } return b; } protected static String _ascii(byte[] b) { try { return new String(b, "US-ASCII"); } catch (IOException e) { // never occurs throw new RuntimeException(e); } } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/base/package-info.java000066400000000000000000000006231356164247300324410ustar00rootroot00000000000000/** * Base classes used by concrete Parser and Generator implementations; * contain functionality that is not specific to JSON or input * abstraction (byte vs char). * Most formats extend these types, although it is also possible to * directly extend {@link com.fasterxml.jackson.core.JsonParser} or * {@link com.fasterxml.jackson.core.JsonGenerator}. */ package com.fasterxml.jackson.core.base; jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/exc/000077500000000000000000000000001356164247300271165ustar00rootroot00000000000000InputCoercionException.java000066400000000000000000000043731356164247300343510ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/excpackage com.fasterxml.jackson.core.exc; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.util.RequestPayload; /** * Exception type for read-side problems that are not direct decoding ("parsing") * problems (those would be reported as {@link com.fasterxml.jackson.core.JsonParseException}s), * but rather result from failed attempts to convert specific Java value out of valid * but incompatible input value. One example is numeric coercions where target number type's * range does not allow mapping of too large/too small input value. * * @since 2.10 */ public class InputCoercionException extends StreamReadException { private static final long serialVersionUID = 1L; /** * Input token that represents input value that failed to coerce. */ protected final JsonToken _inputType; /** * Target type that input value failed to coerce to. */ protected final Class _targetType; /** * Constructor that uses current parsing location as location, and * sets processor (accessible via {@link #getProcessor()}) to * specified parser. */ public InputCoercionException(JsonParser p, String msg, JsonToken inputType, Class targetType) { super(p, msg); _inputType = inputType; _targetType = targetType; } /** * Fluent method that may be used to assign originating {@link JsonParser}, * to be accessed using {@link #getProcessor()}. *

* NOTE: `this` instance is modified and no new instance is constructed. */ @Override public InputCoercionException withParser(JsonParser p) { _processor = p; return this; } @Override public InputCoercionException withRequestPayload(RequestPayload p) { _requestPayload = p; return this; } /** * Accessor for getting information about input type (in form of token, giving "shape" * of input) for which coercion failed. */ public JsonToken getInputType() { return _inputType; } /** * Accessor for getting information about target type (in form of Java {@link java.lang.Class}) * for which coercion failed. */ public Class getTargetType() { return _targetType; } } StreamReadException.java000066400000000000000000000061671356164247300336220ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/excpackage com.fasterxml.jackson.core.exc; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.util.RequestPayload; /** * Intermediate base class for all read-side streaming processing problems, including * parsing and input value coercion problems. * * @since 2.10 */ public abstract class StreamReadException extends JsonProcessingException { final static long serialVersionUID = 1L; 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. */ protected RequestPayload _requestPayload; public StreamReadException(JsonParser p, String msg) { super(msg, (p == null) ? null : p.getCurrentLocation()); _processor = p; } public StreamReadException(JsonParser p, String msg, Throwable root) { super(msg, (p == null) ? null : p.getCurrentLocation(), root); _processor = p; } public StreamReadException(JsonParser p, String msg, JsonLocation loc) { super(msg, loc, null); _processor = p; } protected StreamReadException(String msg, JsonLocation loc, Throwable rootCause) { super(msg); if (rootCause != null) { initCause(rootCause); } _location = loc; } /** * Fluent method that may be used to assign originating {@link JsonParser}, * to be accessed using {@link #getProcessor()}. *

* NOTE: `this` instance is modified and no new instance is constructed. */ public abstract StreamReadException withParser(JsonParser p); /** * Fluent method that may be used to assign payload to this exception, * to let recipient access it for diagnostics purposes. *

* NOTE: `this` instance is modified and no new instance is constructed. */ public abstract StreamReadException withRequestPayload(RequestPayload p); @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 */ 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 */ 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; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/exc/package-info.java000066400000000000000000000002521356164247300323040ustar00rootroot00000000000000/** * Package for some of {@link com.fasterxml.jackson.core.JsonProcessingException} * subtypes contained by streaming API. */ package com.fasterxml.jackson.core.exc; jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/filter/000077500000000000000000000000001356164247300276245ustar00rootroot00000000000000FilteringGeneratorDelegate.java000066400000000000000000000651501356164247300356440ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/filterpackage com.fasterxml.jackson.core.filter; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.util.JsonGeneratorDelegate; /** * Specialized {@link JsonGeneratorDelegate} that allows use of * {@link TokenFilter} for outputting a subset of content that * caller tries to generate. * * @since 2.6 */ public class FilteringGeneratorDelegate extends JsonGeneratorDelegate { /* /********************************************************** /* Configuration /********************************************************** */ /** * Object consulted to determine whether to write parts of content generator * is asked to write or not. */ protected TokenFilter rootFilter; /** * Flag that determines whether filtering will continue after the first * match is indicated or not: if `false`, output is based on just the first * full match (returning {@link TokenFilter#INCLUDE_ALL}) and no more * checks are made; if `true` then filtering will be applied as necessary * until end of content. */ protected boolean _allowMultipleMatches; /** * Flag that determines whether path leading up to included content should * also be automatically included or not. If `false`, no path inclusion is * done and only explicitly included entries are output; if `true` then * path from main level down to match is also included as necessary. */ protected boolean _includePath; /* NOTE: this feature is included in the first version (2.6), but * there is no public API to enable it, yet, since there isn't an * actual use case. But it seemed possible need could arise, which * is feature has not yet been removed. If no use is found within * first version or two, just remove. * * Marked as deprecated since its status is uncertain. */ @Deprecated protected boolean _includeImmediateParent; /* /********************************************************** /* Additional state /********************************************************** */ /** * Although delegate has its own output context it is not sufficient since we actually * have to keep track of excluded (filtered out) structures as well as ones delegate * actually outputs. */ protected TokenFilterContext _filterContext; /** * State that applies to the item within container, used where applicable. * Specifically used to pass inclusion state between property name and * property, and also used for array elements. */ protected TokenFilter _itemFilter; /** * Number of tokens for which {@link TokenFilter#INCLUDE_ALL} * has been returned */ protected int _matchCount; /* /********************************************************** /* Construction, initialization /********************************************************** */ public FilteringGeneratorDelegate(JsonGenerator d, TokenFilter f, boolean includePath, boolean allowMultipleMatches) { // By default, do NOT delegate copy methods super(d, false); rootFilter = f; // and this is the currently active filter for root values _itemFilter = f; _filterContext = TokenFilterContext.createRootContext(f); _includePath = includePath; _allowMultipleMatches = allowMultipleMatches; } /* /********************************************************** /* Extended API /********************************************************** */ public TokenFilter getFilter() { return rootFilter; } public JsonStreamContext getFilterContext() { return _filterContext; } /** * Accessor for finding number of matches, where specific token and sub-tree * starting (if structured type) are passed. */ public int getMatchCount() { return _matchCount; } /* /********************************************************** /* Public API, accessors /********************************************************** */ @Override public JsonStreamContext getOutputContext() { /* 11-Apr-2015, tatu: Choice is between pre- and post-filter context; * let's expose post-filter context that correlates with the view * of caller. */ return _filterContext; } /* /********************************************************** /* Public API, write methods, structural /********************************************************** */ @Override public void writeStartArray() throws IOException { // First things first: whole-sale skipping easy if (_itemFilter == null) { _filterContext = _filterContext.createChildArrayContext(null, false); return; } if (_itemFilter == TokenFilter.INCLUDE_ALL) { // include the whole sub-tree? _filterContext = _filterContext.createChildArrayContext(_itemFilter, true); delegate.writeStartArray(); return; } // Ok; regular checking state then _itemFilter = _filterContext.checkValue(_itemFilter); if (_itemFilter == null) { _filterContext = _filterContext.createChildArrayContext(null, false); return; } if (_itemFilter != TokenFilter.INCLUDE_ALL) { _itemFilter = _itemFilter.filterStartArray(); } if (_itemFilter == TokenFilter.INCLUDE_ALL) { _checkParentPath(); _filterContext = _filterContext.createChildArrayContext(_itemFilter, true); delegate.writeStartArray(); } else { _filterContext = _filterContext.createChildArrayContext(_itemFilter, false); } } @Override public void writeStartArray(int size) throws IOException { if (_itemFilter == null) { _filterContext = _filterContext.createChildArrayContext(null, false); return; } if (_itemFilter == TokenFilter.INCLUDE_ALL) { _filterContext = _filterContext.createChildArrayContext(_itemFilter, true); delegate.writeStartArray(size); return; } _itemFilter = _filterContext.checkValue(_itemFilter); if (_itemFilter == null) { _filterContext = _filterContext.createChildArrayContext(null, false); return; } if (_itemFilter != TokenFilter.INCLUDE_ALL) { _itemFilter = _itemFilter.filterStartArray(); } if (_itemFilter == TokenFilter.INCLUDE_ALL) { _checkParentPath(); _filterContext = _filterContext.createChildArrayContext(_itemFilter, true); delegate.writeStartArray(size); } else { _filterContext = _filterContext.createChildArrayContext(_itemFilter, false); } } @Override public void writeEndArray() throws IOException { _filterContext = _filterContext.closeArray(delegate); if (_filterContext != null) { _itemFilter = _filterContext.getFilter(); } } @Override public void writeStartObject() throws IOException { if (_itemFilter == null) { _filterContext = _filterContext.createChildObjectContext(_itemFilter, false); return; } if (_itemFilter == TokenFilter.INCLUDE_ALL) { _filterContext = _filterContext.createChildObjectContext(_itemFilter, true); delegate.writeStartObject(); return; } TokenFilter f = _filterContext.checkValue(_itemFilter); if (f == null) { return; } if (f != TokenFilter.INCLUDE_ALL) { f = f.filterStartObject(); } if (f == TokenFilter.INCLUDE_ALL) { _checkParentPath(); _filterContext = _filterContext.createChildObjectContext(f, true); delegate.writeStartObject(); } else { // filter out _filterContext = _filterContext.createChildObjectContext(f, false); } } @Override public void writeStartObject(Object forValue) throws IOException { if (_itemFilter == null) { _filterContext = _filterContext.createChildObjectContext(_itemFilter, false); return; } if (_itemFilter == TokenFilter.INCLUDE_ALL) { _filterContext = _filterContext.createChildObjectContext(_itemFilter, true); delegate.writeStartObject(forValue); return; } TokenFilter f = _filterContext.checkValue(_itemFilter); if (f == null) { return; } if (f != TokenFilter.INCLUDE_ALL) { f = f.filterStartObject(); } if (f == TokenFilter.INCLUDE_ALL) { _checkParentPath(); _filterContext = _filterContext.createChildObjectContext(f, true); delegate.writeStartObject(forValue); } else { // filter out _filterContext = _filterContext.createChildObjectContext(f, false); } } @Override public void writeEndObject() throws IOException { _filterContext = _filterContext.closeObject(delegate); if (_filterContext != null) { _itemFilter = _filterContext.getFilter(); } } @Override public void writeFieldName(String name) throws IOException { TokenFilter state = _filterContext.setFieldName(name); if (state == null) { _itemFilter = null; return; } if (state == TokenFilter.INCLUDE_ALL) { _itemFilter = state; delegate.writeFieldName(name); return; } state = state.includeProperty(name); _itemFilter = state; if (state == TokenFilter.INCLUDE_ALL) { _checkPropertyParentPath(); } } @Override public void writeFieldName(SerializableString name) throws IOException { TokenFilter state = _filterContext.setFieldName(name.getValue()); if (state == null) { _itemFilter = null; return; } if (state == TokenFilter.INCLUDE_ALL) { _itemFilter = state; delegate.writeFieldName(name); return; } state = state.includeProperty(name.getValue()); _itemFilter = state; if (state == TokenFilter.INCLUDE_ALL) { _checkPropertyParentPath(); } } /* /********************************************************** /* Public API, write methods, text/String values /********************************************************** */ @Override public void writeString(String value) throws IOException { if (_itemFilter == null) { return; } if (_itemFilter != TokenFilter.INCLUDE_ALL) { TokenFilter state = _filterContext.checkValue(_itemFilter); if (state == null) { return; } if (state != TokenFilter.INCLUDE_ALL) { if (!state.includeString(value)) { return; } } _checkParentPath(); } delegate.writeString(value); } @Override public void writeString(char[] text, int offset, int len) throws IOException { if (_itemFilter == null) { return; } if (_itemFilter != TokenFilter.INCLUDE_ALL) { String value = new String(text, offset, len); TokenFilter state = _filterContext.checkValue(_itemFilter); if (state == null) { return; } if (state != TokenFilter.INCLUDE_ALL) { if (!state.includeString(value)) { return; } } _checkParentPath(); } delegate.writeString(text, offset, len); } @Override public void writeString(SerializableString value) throws IOException { if (_itemFilter == null) { return; } if (_itemFilter != TokenFilter.INCLUDE_ALL) { TokenFilter state = _filterContext.checkValue(_itemFilter); if (state == null) { return; } if (state != TokenFilter.INCLUDE_ALL) { if (!state.includeString(value.getValue())) { return; } } _checkParentPath(); } delegate.writeString(value); } @Override public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException { if (_checkRawValueWrite()) { delegate.writeRawUTF8String(text, offset, length); } } @Override public void writeUTF8String(byte[] text, int offset, int length) throws IOException { // not exact match, but best we can do if (_checkRawValueWrite()) { delegate.writeUTF8String(text, offset, length); } } /* /********************************************************** /* Public API, write methods, binary/raw content /********************************************************** */ @Override public void writeRaw(String text) throws IOException { if (_checkRawValueWrite()) { delegate.writeRaw(text); } } @Override public void writeRaw(String text, int offset, int len) throws IOException { if (_checkRawValueWrite()) { delegate.writeRaw(text); } } @Override public void writeRaw(SerializableString text) throws IOException { if (_checkRawValueWrite()) { delegate.writeRaw(text); } } @Override public void writeRaw(char[] text, int offset, int len) throws IOException { if (_checkRawValueWrite()) { delegate.writeRaw(text, offset, len); } } @Override public void writeRaw(char c) throws IOException { if (_checkRawValueWrite()) { delegate.writeRaw(c); } } @Override public void writeRawValue(String text) throws IOException { if (_checkRawValueWrite()) { delegate.writeRaw(text); } } @Override public void writeRawValue(String text, int offset, int len) throws IOException { if (_checkRawValueWrite()) { delegate.writeRaw(text, offset, len); } } @Override public void writeRawValue(char[] text, int offset, int len) throws IOException { if (_checkRawValueWrite()) { delegate.writeRaw(text, offset, len); } } @Override public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException { if (_checkBinaryWrite()) { delegate.writeBinary(b64variant, data, offset, len); } } @Override public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) throws IOException { if (_checkBinaryWrite()) { return delegate.writeBinary(b64variant, data, dataLength); } return -1; } /* /********************************************************** /* Public API, write methods, other value types /********************************************************** */ @Override public void writeNumber(short v) throws IOException { if (_itemFilter == null) { return; } if (_itemFilter != TokenFilter.INCLUDE_ALL) { TokenFilter state = _filterContext.checkValue(_itemFilter); if (state == null) { return; } if (state != TokenFilter.INCLUDE_ALL) { if (!state.includeNumber(v)) { return; } } _checkParentPath(); } delegate.writeNumber(v); } @Override public void writeNumber(int v) throws IOException { if (_itemFilter == null) { return; } if (_itemFilter != TokenFilter.INCLUDE_ALL) { TokenFilter state = _filterContext.checkValue(_itemFilter); if (state == null) { return; } if (state != TokenFilter.INCLUDE_ALL) { if (!state.includeNumber(v)) { return; } } _checkParentPath(); } delegate.writeNumber(v); } @Override public void writeNumber(long v) throws IOException { if (_itemFilter == null) { return; } if (_itemFilter != TokenFilter.INCLUDE_ALL) { TokenFilter state = _filterContext.checkValue(_itemFilter); if (state == null) { return; } if (state != TokenFilter.INCLUDE_ALL) { if (!state.includeNumber(v)) { return; } } _checkParentPath(); } delegate.writeNumber(v); } @Override public void writeNumber(BigInteger v) throws IOException { if (_itemFilter == null) { return; } if (_itemFilter != TokenFilter.INCLUDE_ALL) { TokenFilter state = _filterContext.checkValue(_itemFilter); if (state == null) { return; } if (state != TokenFilter.INCLUDE_ALL) { if (!state.includeNumber(v)) { return; } } _checkParentPath(); } delegate.writeNumber(v); } @Override public void writeNumber(double v) throws IOException { if (_itemFilter == null) { return; } if (_itemFilter != TokenFilter.INCLUDE_ALL) { TokenFilter state = _filterContext.checkValue(_itemFilter); if (state == null) { return; } if (state != TokenFilter.INCLUDE_ALL) { if (!state.includeNumber(v)) { return; } } _checkParentPath(); } delegate.writeNumber(v); } @Override public void writeNumber(float v) throws IOException { if (_itemFilter == null) { return; } if (_itemFilter != TokenFilter.INCLUDE_ALL) { TokenFilter state = _filterContext.checkValue(_itemFilter); if (state == null) { return; } if (state != TokenFilter.INCLUDE_ALL) { if (!state.includeNumber(v)) { return; } } _checkParentPath(); } delegate.writeNumber(v); } @Override public void writeNumber(BigDecimal v) throws IOException { if (_itemFilter == null) { return; } if (_itemFilter != TokenFilter.INCLUDE_ALL) { TokenFilter state = _filterContext.checkValue(_itemFilter); if (state == null) { return; } if (state != TokenFilter.INCLUDE_ALL) { if (!state.includeNumber(v)) { return; } } _checkParentPath(); } delegate.writeNumber(v); } @Override public void writeNumber(String encodedValue) throws IOException, UnsupportedOperationException { if (_itemFilter == null) { return; } if (_itemFilter != TokenFilter.INCLUDE_ALL) { TokenFilter state = _filterContext.checkValue(_itemFilter); if (state == null) { return; } if (state != TokenFilter.INCLUDE_ALL) { if (!state.includeRawValue()) { // close enough? return; } } _checkParentPath(); } delegate.writeNumber(encodedValue); } @Override public void writeBoolean(boolean v) throws IOException { if (_itemFilter == null) { return; } if (_itemFilter != TokenFilter.INCLUDE_ALL) { TokenFilter state = _filterContext.checkValue(_itemFilter); if (state == null) { return; } if (state != TokenFilter.INCLUDE_ALL) { if (!state.includeBoolean(v)) { return; } } _checkParentPath(); } delegate.writeBoolean(v); } @Override public void writeNull() throws IOException { if (_itemFilter == null) { return; } if (_itemFilter != TokenFilter.INCLUDE_ALL) { TokenFilter state = _filterContext.checkValue(_itemFilter); if (state == null) { return; } if (state != TokenFilter.INCLUDE_ALL) { if (!state.includeNull()) { return; } } _checkParentPath(); } delegate.writeNull(); } /* /********************************************************** /* Overridden field methods /********************************************************** */ @Override public void writeOmittedField(String fieldName) throws IOException { // Hmmh. Not sure how this would work but... if (_itemFilter != null) { delegate.writeOmittedField(fieldName); } } /* /********************************************************** /* Public API, write methods, Native Ids /********************************************************** */ // 25-Mar-2015, tatu: These are tricky as they sort of predate actual filtering calls. // Let's try to use current state as a clue at least... @Override public void writeObjectId(Object id) throws IOException { if (_itemFilter != null) { delegate.writeObjectId(id); } } @Override public void writeObjectRef(Object id) throws IOException { if (_itemFilter != null) { delegate.writeObjectRef(id); } } @Override public void writeTypeId(Object id) throws IOException { if (_itemFilter != null) { delegate.writeTypeId(id); } } /* /********************************************************** /* Public API, write methods, serializing Java objects /********************************************************** */ // Base class definitions for these seems correct to me, iff not directly delegating: /* @Override public void writeObject(Object pojo) throws IOException,JsonProcessingException { if (delegateCopyMethods) { delegate.writeObject(pojo); return; } // NOTE: copied from if (pojo == null) { writeNull(); } else { if (getCodec() != null) { getCodec().writeValue(this, pojo); return; } _writeSimpleObject(pojo); } } @Override public void writeTree(TreeNode rootNode) throws IOException { if (delegateCopyMethods) { delegate.writeTree(rootNode); return; } // As with 'writeObject()', we are not check if write would work if (rootNode == null) { writeNull(); } else { if (getCodec() == null) { throw new IllegalStateException("No ObjectCodec defined"); } getCodec().writeValue(this, rootNode); } } */ /* /********************************************************** /* Public API, copy-through methods /********************************************************** */ // Base class definitions for these seems correct to me, iff not directly delegating: /* @Override public void copyCurrentEvent(JsonParser jp) throws IOException { if (delegateCopyMethods) delegate.copyCurrentEvent(jp); else super.copyCurrentEvent(jp); } @Override public void copyCurrentStructure(JsonParser jp) throws IOException { if (delegateCopyMethods) delegate.copyCurrentStructure(jp); else super.copyCurrentStructure(jp); } */ /* /********************************************************** /* Helper methods /********************************************************** */ protected void _checkParentPath() throws IOException { ++_matchCount; // only need to construct path if parent wasn't written if (_includePath) { _filterContext.writePath(delegate); } // also: if no multiple matches desired, short-cut checks if (!_allowMultipleMatches) { // Mark parents as "skip" so that further check calls are not made _filterContext.skipParentChecks(); } } /** * Specialized variant of {@link #_checkParentPath} used when checking * parent for a property name to be included with value: rules are slightly * different. */ protected void _checkPropertyParentPath() throws IOException { ++_matchCount; if (_includePath) { _filterContext.writePath(delegate); } else if (_includeImmediateParent) { // 21-Apr-2015, tatu: Note that there is no API to enable this currently... // retained for speculative future use _filterContext.writeImmediatePath(delegate); } // also: if no multiple matches desired, short-cut checks if (!_allowMultipleMatches) { // Mark parents as "skip" so that further check calls are not made _filterContext.skipParentChecks(); } } protected boolean _checkBinaryWrite() throws IOException { if (_itemFilter == null) { return false; } if (_itemFilter == TokenFilter.INCLUDE_ALL) { return true; } if (_itemFilter.includeBinary()) { // close enough? _checkParentPath(); return true; } return false; } protected boolean _checkRawValueWrite() throws IOException { if (_itemFilter == null) { return false; } if (_itemFilter == TokenFilter.INCLUDE_ALL) { return true; } if (_itemFilter.includeRawValue()) { // close enough? _checkParentPath(); return true; } return false; } } FilteringParserDelegate.java000066400000000000000000001046761356164247300351610ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/filterpackage com.fasterxml.jackson.core.filter; import java.io.IOException; import java.io.OutputStream; import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.util.JsonParserDelegate; import static com.fasterxml.jackson.core.JsonTokenId.*; /** * Specialized {@link JsonParserDelegate} that allows use of * {@link TokenFilter} for outputting a subset of content that * is visible to caller * * @since 2.6 */ public class FilteringParserDelegate extends JsonParserDelegate { /* /********************************************************** /* Configuration /********************************************************** */ /** * Object consulted to determine whether to write parts of content generator * is asked to write or not. */ protected TokenFilter rootFilter; /** * Flag that determines whether filtering will continue after the first * match is indicated or not: if `false`, output is based on just the first * full match (returning {@link TokenFilter#INCLUDE_ALL}) and no more * checks are made; if `true` then filtering will be applied as necessary * until end of content. */ protected boolean _allowMultipleMatches; /** * Flag that determines whether path leading up to included content should * also be automatically included or not. If `false`, no path inclusion is * done and only explicitly included entries are output; if `true` then * path from main level down to match is also included as necessary. */ protected boolean _includePath; /* NOTE: this feature is included in the first version (2.6), but * there is no public API to enable it, yet, since there isn't an * actual use case. But it seemed possible need could arise, which * is feature has not yet been removed. If no use is found within * first version or two, just remove. * * Marked as deprecated since its status is uncertain. */ @Deprecated protected boolean _includeImmediateParent; /* /********************************************************** /* State /********************************************************** */ /** * Last token retrieved via {@link #nextToken}, if any. * Null before the first call to nextToken(), * as well as if token has been explicitly cleared */ protected JsonToken _currToken; /** * Last cleared token, if any: that is, value that was in * effect when {@link #clearCurrentToken} was called. */ protected JsonToken _lastClearedToken; /** * During traversal this is the actual "open" parse tree, which sometimes * is the same as {@link #_exposedContext}, and at other times is ahead * of it. Note that this context is never null. */ protected TokenFilterContext _headContext; /** * In cases where {@link #_headContext} is "ahead" of context exposed to * caller, this context points to what is currently exposed to caller. * When the two are in sync, this context reference will be null. */ protected TokenFilterContext _exposedContext; /** * State that applies to the item within container, used where applicable. * Specifically used to pass inclusion state between property name and * property, and also used for array elements. */ protected TokenFilter _itemFilter; /** * Number of tokens for which {@link TokenFilter#INCLUDE_ALL} * has been returned. */ protected int _matchCount; /* /********************************************************** /* Construction, initialization /********************************************************** */ public FilteringParserDelegate(JsonParser p, TokenFilter f, boolean includePath, boolean allowMultipleMatches) { super(p); rootFilter = f; // and this is the currently active filter for root values _itemFilter = f; _headContext = TokenFilterContext.createRootContext(f); _includePath = includePath; _allowMultipleMatches = allowMultipleMatches; } /* /********************************************************** /* Extended API /********************************************************** */ public TokenFilter getFilter() { return rootFilter; } /** * Accessor for finding number of matches, where specific token and sub-tree * starting (if structured type) are passed. */ public int getMatchCount() { return _matchCount; } /* /********************************************************** /* Public API, token accessors /********************************************************** */ @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) { final JsonToken t = _currToken; if (t == null) { return (JsonTokenId.ID_NO_TOKEN == id); } return t.id() == id; } @Override public final boolean hasToken(JsonToken t) { return (_currToken == t); } @Override public boolean isExpectedStartArrayToken() { return _currToken == JsonToken.START_ARRAY; } @Override public boolean isExpectedStartObjectToken() { return _currToken == JsonToken.START_OBJECT; } @Override public JsonLocation getCurrentLocation() { return delegate.getCurrentLocation(); } @Override public JsonStreamContext getParsingContext() { return _filterContext(); } // !!! TODO: Verify it works as expected: copied from standard JSON parser impl @Override public String getCurrentName() throws IOException { JsonStreamContext ctxt = _filterContext(); if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) { JsonStreamContext parent = ctxt.getParent(); return (parent == null) ? null : parent.getCurrentName(); } return ctxt.getCurrentName(); } /* /********************************************************** /* Public API, token state overrides /********************************************************** */ @Override public void clearCurrentToken() { if (_currToken != null) { _lastClearedToken = _currToken; _currToken = null; } } @Override public JsonToken getLastClearedToken() { return _lastClearedToken; } @Override public void overrideCurrentName(String name) { /* 14-Apr-2015, tatu: Not sure whether this can be supported, and if so, * what to do with it... Delegation won't work for sure, so let's for * now throw an exception */ throw new UnsupportedOperationException("Can not currently override name during filtering read"); } /* /********************************************************** /* Public API, traversal /********************************************************** */ @Override public JsonToken nextToken() throws IOException { // 23-May-2017, tatu: To be honest, code here is rather hairy and I don't like all // conditionals; and it seems odd to return `null` but NOT considering input // as closed... would love a rewrite to simplify/clear up logic here. // Check for _allowMultipleMatches - false and at least there is one token - which is _currToken // check for no buffered context _exposedContext - null // If all the conditions matches then check for scalar / non-scalar property if (!_allowMultipleMatches && (_currToken != null) && (_exposedContext == null)) { // if scalar, and scalar not present in obj/array and !includePath and INCLUDE_ALL // matched once, return null if (_currToken.isScalarValue() && !_headContext.isStartHandled() && !_includePath && (_itemFilter == TokenFilter.INCLUDE_ALL)) { return (_currToken = null); } } // Anything buffered? TokenFilterContext ctxt = _exposedContext; if (ctxt != null) { while (true) { JsonToken t = ctxt.nextTokenToRead(); if (t != null) { _currToken = t; return t; } // all done with buffered stuff? if (ctxt == _headContext) { _exposedContext = null; if (ctxt.inArray()) { t = delegate.getCurrentToken(); // Is this guaranteed to work without further checks? // if (t != JsonToken.START_ARRAY) { _currToken = t; return t; } // Almost! Most likely still have the current token; // with the sole exception of /* t = delegate.getCurrentToken(); if (t != JsonToken.FIELD_NAME) { _currToken = t; return t; } */ break; } // If not, traverse down the context chain ctxt = _headContext.findChildOf(ctxt); _exposedContext = ctxt; if (ctxt == null) { // should never occur throw _constructError("Unexpected problem: chain of filtered context broken"); } } } // If not, need to read more. If we got any: JsonToken t = delegate.nextToken(); if (t == null) { // no strict need to close, since we have no state here _currToken = t; return t; } // otherwise... to include or not? TokenFilter f; switch (t.id()) { case ID_START_ARRAY: f = _itemFilter; if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildArrayContext(f, true); return (_currToken = t); } if (f == null) { // does this occur? delegate.skipChildren(); break; } // Otherwise still iffy, need to check f = _headContext.checkValue(f); if (f == null) { delegate.skipChildren(); break; } if (f != TokenFilter.INCLUDE_ALL) { f = f.filterStartArray(); } _itemFilter = f; if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildArrayContext(f, true); return (_currToken = t); } _headContext = _headContext.createChildArrayContext(f, false); // Also: only need buffering if parent path to be included if (_includePath) { t = _nextTokenWithBuffering(_headContext); if (t != null) { _currToken = t; return t; } } break; case ID_START_OBJECT: f = _itemFilter; if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildObjectContext(f, true); return (_currToken = t); } if (f == null) { // does this occur? delegate.skipChildren(); break; } // Otherwise still iffy, need to check f = _headContext.checkValue(f); if (f == null) { delegate.skipChildren(); break; } if (f != TokenFilter.INCLUDE_ALL) { f = f.filterStartObject(); } _itemFilter = f; if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildObjectContext(f, true); return (_currToken = t); } _headContext = _headContext.createChildObjectContext(f, false); // Also: only need buffering if parent path to be included if (_includePath) { t = _nextTokenWithBuffering(_headContext); if (t != null) { _currToken = t; return t; } } // note: inclusion of surrounding Object handled separately via // FIELD_NAME break; case ID_END_ARRAY: case ID_END_OBJECT: { boolean returnEnd = _headContext.isStartHandled(); f = _headContext.getFilter(); if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) { f.filterFinishArray(); } _headContext = _headContext.getParent(); _itemFilter = _headContext.getFilter(); if (returnEnd) { return (_currToken = t); } } break; case ID_FIELD_NAME: { final String name = delegate.getCurrentName(); // note: this will also set 'needToHandleName' f = _headContext.setFieldName(name); if (f == TokenFilter.INCLUDE_ALL) { _itemFilter = f; if (!_includePath) { // Minor twist here: if parent NOT included, may need to induce output of // surrounding START_OBJECT/END_OBJECT if (_includeImmediateParent && !_headContext.isStartHandled()) { t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled _exposedContext = _headContext; } } return (_currToken = t); } if (f == null) { delegate.nextToken(); delegate.skipChildren(); break; } f = f.includeProperty(name); if (f == null) { delegate.nextToken(); delegate.skipChildren(); break; } _itemFilter = f; if (f == TokenFilter.INCLUDE_ALL) { if (_verifyAllowedMatches()) { if (_includePath) { return (_currToken = t); } } else { delegate.nextToken(); delegate.skipChildren(); } } if (_includePath) { t = _nextTokenWithBuffering(_headContext); if (t != null) { _currToken = t; return t; } } break; } default: // scalar value f = _itemFilter; if (f == TokenFilter.INCLUDE_ALL) { return (_currToken = t); } if (f != null) { f = _headContext.checkValue(f); if ((f == TokenFilter.INCLUDE_ALL) || ((f != null) && f.includeValue(delegate))) { if (_verifyAllowedMatches()) { return (_currToken = t); } } } // Otherwise not included (leaves must be explicitly included) break; } // We get here if token was not yet found; offlined handling return _nextToken2(); } /** * Offlined handling for cases where there was no buffered token to * return, and the token read next could not be returned as-is, * at least not yet, but where we have not yet established that * buffering is needed. */ protected final JsonToken _nextToken2() throws IOException { main_loop: while (true) { JsonToken t = delegate.nextToken(); if (t == null) { // is this even legal? _currToken = t; return t; } TokenFilter f; switch (t.id()) { case ID_START_ARRAY: f = _itemFilter; if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildArrayContext(f, true); return (_currToken = t); } if (f == null) { // does this occur? delegate.skipChildren(); continue main_loop; } // Otherwise still iffy, need to check f = _headContext.checkValue(f); if (f == null) { delegate.skipChildren(); continue main_loop; } if (f != TokenFilter.INCLUDE_ALL) { f = f.filterStartArray(); } _itemFilter = f; if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildArrayContext(f, true); return (_currToken = t); } _headContext = _headContext.createChildArrayContext(f, false); // but if we didn't figure it out yet, need to buffer possible events if (_includePath) { t = _nextTokenWithBuffering(_headContext); if (t != null) { _currToken = t; return t; } } continue main_loop; case ID_START_OBJECT: f = _itemFilter; if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildObjectContext(f, true); return (_currToken = t); } if (f == null) { // does this occur? delegate.skipChildren(); continue main_loop; } // Otherwise still iffy, need to check f = _headContext.checkValue(f); if (f == null) { delegate.skipChildren(); continue main_loop; } if (f != TokenFilter.INCLUDE_ALL) { f = f.filterStartObject(); } _itemFilter = f; if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildObjectContext(f, true); return (_currToken = t); } _headContext = _headContext.createChildObjectContext(f, false); if (_includePath) { t = _nextTokenWithBuffering(_headContext); if (t != null) { _currToken = t; return t; } } continue main_loop; case ID_END_ARRAY: case ID_END_OBJECT: { boolean returnEnd = _headContext.isStartHandled(); f = _headContext.getFilter(); if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) { f.filterFinishArray(); } _headContext = _headContext.getParent(); _itemFilter = _headContext.getFilter(); if (returnEnd) { return (_currToken = t); } } continue main_loop; case ID_FIELD_NAME: { final String name = delegate.getCurrentName(); f = _headContext.setFieldName(name); if (f == TokenFilter.INCLUDE_ALL) { _itemFilter = f; return (_currToken = t); } if (f == null) { // filter out the value delegate.nextToken(); delegate.skipChildren(); continue main_loop; } f = f.includeProperty(name); if (f == null) { // filter out the value delegate.nextToken(); delegate.skipChildren(); continue main_loop; } _itemFilter = f; if (f == TokenFilter.INCLUDE_ALL) { if (_verifyAllowedMatches() && _includePath) { return (_currToken = t); } // if (_includeImmediateParent) { ... continue main_loop; } if (_includePath) { t = _nextTokenWithBuffering(_headContext); if (t != null) { _currToken = t; return t; } } } continue main_loop; default: // scalar value f = _itemFilter; if (f == TokenFilter.INCLUDE_ALL) { return (_currToken = t); } if (f != null) { f = _headContext.checkValue(f); if ((f == TokenFilter.INCLUDE_ALL) || ((f != null) && f.includeValue(delegate))) { if (_verifyAllowedMatches()) { return (_currToken = t); } } } // Otherwise not included (leaves must be explicitly included) break; } } } /** * Method called when a new potentially included context is found. */ protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffRoot) throws IOException { main_loop: while (true) { JsonToken t = delegate.nextToken(); if (t == null) { // is this even legal? return t; } TokenFilter f; // One simplification here: we know for a fact that the item filter is // neither null nor 'include all', for most cases; the only exception // being FIELD_NAME handling switch (t.id()) { case ID_START_ARRAY: f = _headContext.checkValue(_itemFilter); if (f == null) { delegate.skipChildren(); continue main_loop; } if (f != TokenFilter.INCLUDE_ALL) { f = f.filterStartArray(); } _itemFilter = f; if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildArrayContext(f, true); return _nextBuffered(buffRoot); } _headContext = _headContext.createChildArrayContext(f, false); continue main_loop; case ID_START_OBJECT: f = _itemFilter; if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildObjectContext(f, true); return t; } if (f == null) { // does this occur? delegate.skipChildren(); continue main_loop; } // Otherwise still iffy, need to check f = _headContext.checkValue(f); if (f == null) { delegate.skipChildren(); continue main_loop; } if (f != TokenFilter.INCLUDE_ALL) { f = f.filterStartObject(); } _itemFilter = f; if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildObjectContext(f, true); return _nextBuffered(buffRoot); } _headContext = _headContext.createChildObjectContext(f, false); continue main_loop; case ID_END_ARRAY: case ID_END_OBJECT: { // Unlike with other loops, here we know that content was NOT // included (won't get this far otherwise) f = _headContext.getFilter(); if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) { f.filterFinishArray(); } boolean gotEnd = (_headContext == buffRoot); boolean returnEnd = gotEnd && _headContext.isStartHandled(); _headContext = _headContext.getParent(); _itemFilter = _headContext.getFilter(); if (returnEnd) { return t; } } continue main_loop; case ID_FIELD_NAME: { final String name = delegate.getCurrentName(); f = _headContext.setFieldName(name); if (f == TokenFilter.INCLUDE_ALL) { _itemFilter = f; return _nextBuffered(buffRoot); } if (f == null) { // filter out the value delegate.nextToken(); delegate.skipChildren(); continue main_loop; } f = f.includeProperty(name); if (f == null) { // filter out the value delegate.nextToken(); delegate.skipChildren(); continue main_loop; } _itemFilter = f; if (f == TokenFilter.INCLUDE_ALL) { if (_verifyAllowedMatches()) { return _nextBuffered(buffRoot); } else { // edge case: if no more matches allowed, reset filter // to initial state to prevent missing a token in next iteration _itemFilter = _headContext.setFieldName(name); } } } continue main_loop; default: // scalar value f = _itemFilter; if (f == TokenFilter.INCLUDE_ALL) { return _nextBuffered(buffRoot); } if (f != null) { f = _headContext.checkValue(f); if ((f == TokenFilter.INCLUDE_ALL) || ((f != null) && f.includeValue(delegate))) { if (_verifyAllowedMatches()) { return _nextBuffered(buffRoot); } } } // Otherwise not included (leaves must be explicitly included) continue main_loop; } } } private JsonToken _nextBuffered(TokenFilterContext buffRoot) throws IOException { _exposedContext = buffRoot; TokenFilterContext ctxt = buffRoot; JsonToken t = ctxt.nextTokenToRead(); if (t != null) { return t; } while (true) { // all done with buffered stuff? if (ctxt == _headContext) { throw _constructError("Internal error: failed to locate expected buffered tokens"); /* _exposedContext = null; break; */ } // If not, traverse down the context chain ctxt = _exposedContext.findChildOf(ctxt); _exposedContext = ctxt; if (ctxt == null) { // should never occur throw _constructError("Unexpected problem: chain of filtered context broken"); } t = _exposedContext.nextTokenToRead(); if (t != null) { return t; } } } private final boolean _verifyAllowedMatches() throws IOException { if (_matchCount == 0 || _allowMultipleMatches) { ++_matchCount; return true; } return false; } @Override public JsonToken nextValue() throws IOException { // Re-implemented same as ParserMinimalBase: JsonToken t = nextToken(); if (t == JsonToken.FIELD_NAME) { t = nextToken(); } return t; } /** * Need to override, re-implement similar to how method defined in * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep * state correct here. */ @Override public JsonParser skipChildren() throws IOException { if ((_currToken != JsonToken.START_OBJECT) && (_currToken != JsonToken.START_ARRAY)) { return this; } int open = 1; // Since proper matching of start/end markers is handled // by nextToken(), we'll just count nesting levels here while (true) { JsonToken t = nextToken(); if (t == null) { // not ideal but for now, just return return this; } if (t.isStructStart()) { ++open; } else if (t.isStructEnd()) { if (--open == 0) { return this; } } } } /* /********************************************************** /* Public API, access to token information, text /********************************************************** */ @Override public String getText() throws IOException { return delegate.getText(); } @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); } @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(); } /* /********************************************************** /* Public API, access to token information, numeric /********************************************************** */ @Override public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); } @Override public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); } @Override public byte getByteValue() throws IOException { return delegate.getByteValue(); } @Override public short getShortValue() throws IOException { return delegate.getShortValue(); } @Override public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); } @Override public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); } @Override public float getFloatValue() throws IOException { return delegate.getFloatValue(); } @Override public int getIntValue() throws IOException { return delegate.getIntValue(); } @Override public long getLongValue() throws IOException { return delegate.getLongValue(); } @Override public NumberType getNumberType() throws IOException { return delegate.getNumberType(); } @Override public Number getNumberValue() throws IOException { return delegate.getNumberValue(); } /* /********************************************************** /* Public API, access to token information, coercion/conversion /********************************************************** */ @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); } @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); } @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); } @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); } @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); } @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); } @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); } @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); } @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); } @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); } /* /********************************************************** /* Public API, access to token values, other /********************************************************** */ @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); } @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); } @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); } @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); } /* /********************************************************** /* Internal helper methods /********************************************************** */ protected JsonStreamContext _filterContext() { if (_exposedContext != null) { return _exposedContext; } return _headContext; } } JsonPointerBasedFilter.java000066400000000000000000000033251356164247300347720ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/filterpackage com.fasterxml.jackson.core.filter; import com.fasterxml.jackson.core.JsonPointer; /** * Simple {@link TokenFilter} implementation that takes a single * {@link JsonPointer} and matches a single value accordingly. * Instances are immutable and fully thread-safe, shareable, * and efficient to use. * * @since 2.6 */ public class JsonPointerBasedFilter extends TokenFilter { protected final JsonPointer _pathToMatch; public JsonPointerBasedFilter(String ptrExpr) { this(JsonPointer.compile(ptrExpr)); } public JsonPointerBasedFilter(JsonPointer match) { _pathToMatch = match; } @Override public TokenFilter includeElement(int index) { JsonPointer next = _pathToMatch.matchElement(index); if (next == null) { return null; } if (next.matches()) { return TokenFilter.INCLUDE_ALL; } return new JsonPointerBasedFilter(next); } @Override public TokenFilter includeProperty(String name) { JsonPointer next = _pathToMatch.matchProperty(name); if (next == null) { return null; } if (next.matches()) { return TokenFilter.INCLUDE_ALL; } return new JsonPointerBasedFilter(next); } @Override public TokenFilter filterStartArray() { return this; } @Override public TokenFilter filterStartObject() { return this; } @Override protected boolean _includeScalar() { // should only occur for root-level scalars, path "/" return _pathToMatch.matches(); } @Override public String toString() { return "[JsonPointerFilter at: "+_pathToMatch+"]"; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/filter/TokenFilter.java000066400000000000000000000300111356164247300327100ustar00rootroot00000000000000package com.fasterxml.jackson.core.filter; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; /** * Strategy class that can be implemented to specify actual inclusion/exclusion * criteria for filtering, used by {@link FilteringGeneratorDelegate}. * * @since 2.6 */ public class TokenFilter { // // Marker values /** * Marker value that should be used to indicate inclusion of a structured * value (sub-tree representing Object or Array), or value of a named * property (regardless of type). * Note that if this instance is returned, it will used as a marker, and * no actual callbacks need to be made. For this reason, it is more efficient * to return this instance if the whole sub-tree is to be included, instead * of implementing similar filter functionality explicitly. */ public final static TokenFilter INCLUDE_ALL = new TokenFilter(); // Life-cycle protected TokenFilter() { } /* /********************************************************** /* API, structured values /********************************************************** */ /** * Method called to check whether Object value at current output * location should be included in output. * Three kinds of return values may be used as follows: *

*

* Default implementation returns this, which means that checks * are made recursively for properties of the Object to determine possible inclusion. * * @return TokenFilter to use for further calls within Array, unless return value * is null or {@link #INCLUDE_ALL} (which have simpler semantics) */ public TokenFilter filterStartObject() { return this; } /** * Method called to check whether Array value at current output * location should be included in output. * Three kinds of return values may be used as follows: *

*

* Default implementation returns this, which means that checks * are made recursively for elements of the array to determine possible inclusion. * * @return TokenFilter to use for further calls within Array, unless return value * is null or {@link #INCLUDE_ALL} (which have simpler semantics) */ public TokenFilter filterStartArray() { return this; } /** * Method called to indicate that output of non-filtered Object (one that may * have been included either completely, or in part) is completed, * in cases where filter other that {@link #INCLUDE_ALL} was returned. * This occurs when {@link JsonGenerator#writeEndObject()} is called. */ public void filterFinishObject() { } /** * Method called to indicate that output of non-filtered Array (one that may * have been included either completely, or in part) is completed, * in cases where filter other that {@link #INCLUDE_ALL} was returned. * This occurs when {@link JsonGenerator#writeEndArray()} is called. */ public void filterFinishArray() { } /* /********************************************************** /* API, properties/elements /********************************************************** */ /** * Method called to check whether property value with specified name, * at current output location, should be included in output. * Three kinds of return values may be used as follows: *

*

* The default implementation simply returns this to continue calling * methods on this filter object, without full inclusion or exclusion. * * @return TokenFilter to use for further calls within property value, unless return value * is null or {@link #INCLUDE_ALL} (which have simpler semantics) */ public TokenFilter includeProperty(String name) { return this; } /** * Method called to check whether array element with specified index (zero-based), * at current output location, should be included in output. * Three kinds of return values may be used as follows: *

*

* The default implementation simply returns this to continue calling * methods on this filter object, without full inclusion or exclusion. * * @return TokenFilter to use for further calls within element value, unless return value * is null or {@link #INCLUDE_ALL} (which have simpler semantics) */ public TokenFilter includeElement(int index) { return this; } /** * Method called to check whether root-level value, * at current output location, should be included in output. * Three kinds of return values may be used as follows: *

*

* The default implementation simply returns this to continue calling * methods on this filter object, without full inclusion or exclusion. * * @return TokenFilter to use for further calls within root value, unless return value * is null or {@link #INCLUDE_ALL} (which have simpler semantics) */ public TokenFilter includeRootValue(int index) { return this; } /* /********************************************************** /* API, scalar values (being read) /********************************************************** */ /** * Call made when verifying whether a scaler value is being * read from a parser. *

* Default action is to call _includeScalar() and return * whatever it indicates. */ public boolean includeValue(JsonParser p) throws IOException { return _includeScalar(); } /* /********************************************************** /* API, scalar values (being written) /********************************************************** */ /** * Call made to verify whether leaf-level * boolean value * should be included in output or not. */ public boolean includeBoolean(boolean value) { return _includeScalar(); } /** * Call made to verify whether leaf-level * null value * should be included in output or not. */ public boolean includeNull() { return _includeScalar(); } /** * Call made to verify whether leaf-level * String value * should be included in output or not. */ public boolean includeString(String value) { return _includeScalar(); } /** * Call made to verify whether leaf-level * int value * should be included in output or not. * * NOTE: also called for `short`, `byte` */ public boolean includeNumber(int v) { return _includeScalar(); } /** * Call made to verify whether leaf-level * long value * should be included in output or not. */ public boolean includeNumber(long v) { return _includeScalar(); } /** * Call made to verify whether leaf-level * float value * should be included in output or not. */ public boolean includeNumber(float v) { return _includeScalar(); } /** * Call made to verify whether leaf-level * double value * should be included in output or not. */ public boolean includeNumber(double v) { return _includeScalar(); } /** * Call made to verify whether leaf-level * {@link BigDecimal} value * should be included in output or not. */ public boolean includeNumber(BigDecimal v) { return _includeScalar(); } /** * Call made to verify whether leaf-level * {@link BigInteger} value * should be included in output or not. */ public boolean includeNumber(BigInteger v) { return _includeScalar(); } /** * Call made to verify whether leaf-level * Binary value * should be included in output or not. *

* NOTE: no binary payload passed; assumption is this won't be of much use. */ public boolean includeBinary() { return _includeScalar(); } /** * Call made to verify whether leaf-level * raw (pre-encoded, not quoted by generator) value * should be included in output or not. *

* NOTE: value itself not passed since it may come on multiple forms * and is unlikely to be of much use in determining inclusion * criteria. */ public boolean includeRawValue() { return _includeScalar(); } /** * Call made to verify whether leaf-level * embedded (Opaque) value * should be included in output or not. */ public boolean includeEmbeddedValue(Object ob) { return _includeScalar(); } /* /********************************************************** /* Overrides /********************************************************** */ @Override public String toString() { if (this == INCLUDE_ALL) { return "TokenFilter.INCLUDE_ALL"; } return super.toString(); } /* /********************************************************** /* Other methods /********************************************************** */ /** * Overridable default implementation delegated to all scalar value * inclusion check methods. * The default implementation simply includes all leaf values. */ protected boolean _includeScalar() { return true; } } TokenFilterContext.java000066400000000000000000000254301356164247300342070ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/filterpackage com.fasterxml.jackson.core.filter; import java.io.IOException; import com.fasterxml.jackson.core.*; /** * Alternative variant of {@link JsonStreamContext}, used when filtering * content being read or written (based on {@link TokenFilter}). * * @since 2.6 */ public class TokenFilterContext extends JsonStreamContext { /** * Parent context for this context; null for root context. */ protected final TokenFilterContext _parent; /* /********************************************************** /* Simple instance reuse slots; speed up things /* a bit (10-15%) for docs with lots of small /* arrays/objects /********************************************************** */ protected TokenFilterContext _child; /* /********************************************************** /* Location/state information /********************************************************** */ /** * Name of the field of which value is to be parsed; only * used for OBJECT contexts */ protected String _currentName; /** * Filter to use for items in this state (for properties of Objects, * elements of Arrays, and root-level values of root context) */ protected TokenFilter _filter; /** * Flag that indicates that start token has been read/written, * so that matching close token needs to be read/written as well * when context is getting closed. */ protected boolean _startHandled; /** * Flag that indicates that the current name of this context * still needs to be read/written, if path from root down to * included leaf is to be exposed. */ protected boolean _needToHandleName; /* /********************************************************** /* Life-cycle /********************************************************** */ protected TokenFilterContext(int type, TokenFilterContext parent, TokenFilter filter, boolean startHandled) { super(); _type = type; _parent = parent; _filter = filter; _index = -1; _startHandled = startHandled; _needToHandleName = false; } protected TokenFilterContext reset(int type, TokenFilter filter, boolean startWritten) { _type = type; _filter = filter; _index = -1; _currentName = null; _startHandled = startWritten; _needToHandleName = false; return this; } /* /********************************************************** /* Factory methods /********************************************************** */ public static TokenFilterContext createRootContext(TokenFilter filter) { // true -> since we have no start/end marker, consider start handled return new TokenFilterContext(TYPE_ROOT, null, filter, true); } public TokenFilterContext createChildArrayContext(TokenFilter filter, boolean writeStart) { TokenFilterContext ctxt = _child; if (ctxt == null) { _child = ctxt = new TokenFilterContext(TYPE_ARRAY, this, filter, writeStart); return ctxt; } return ctxt.reset(TYPE_ARRAY, filter, writeStart); } public TokenFilterContext createChildObjectContext(TokenFilter filter, boolean writeStart) { TokenFilterContext ctxt = _child; if (ctxt == null) { _child = ctxt = new TokenFilterContext(TYPE_OBJECT, this, filter, writeStart); return ctxt; } return ctxt.reset(TYPE_OBJECT, filter, writeStart); } /* /********************************************************** /* State changes /********************************************************** */ public TokenFilter setFieldName(String name) throws JsonProcessingException { _currentName = name; _needToHandleName = true; return _filter; } /** * Method called to check whether value is to be included at current output * position, either as Object property, Array element, or root value. */ public TokenFilter checkValue(TokenFilter filter) { // First, checks for Object properties have been made earlier: if (_type == TYPE_OBJECT) { return filter; } // We increase it first because at the beginning of array, value is -1 int ix = ++_index; if (_type == TYPE_ARRAY) { return filter.includeElement(ix); } return filter.includeRootValue(ix); } /** * Method called to ensure that parent path from root is written up to * and including this node. */ public void writePath(JsonGenerator gen) throws IOException { if ((_filter == null) || (_filter == TokenFilter.INCLUDE_ALL)) { return; } if (_parent != null) { _parent._writePath(gen); } if (_startHandled) { // even if Object started, need to start leaf-level name if (_needToHandleName) { gen.writeFieldName(_currentName); } } else { _startHandled = true; if (_type == TYPE_OBJECT) { gen.writeStartObject(); gen.writeFieldName(_currentName); // we know name must be written } else if (_type == TYPE_ARRAY) { gen.writeStartArray(); } } } /** * Variant of {@link #writePath(JsonGenerator)} called when all we * need is immediately surrounding Object. Method typically called * when including a single property but not including full path * to root. */ public void writeImmediatePath(JsonGenerator gen) throws IOException { if ((_filter == null) || (_filter == TokenFilter.INCLUDE_ALL)) { return; } if (_startHandled) { // even if Object started, need to start leaf-level name if (_needToHandleName) { gen.writeFieldName(_currentName); } } else { _startHandled = true; if (_type == TYPE_OBJECT) { gen.writeStartObject(); if (_needToHandleName) { gen.writeFieldName(_currentName); } } else if (_type == TYPE_ARRAY) { gen.writeStartArray(); } } } private void _writePath(JsonGenerator gen) throws IOException { if ((_filter == null) || (_filter == TokenFilter.INCLUDE_ALL)) { return; } if (_parent != null) { _parent._writePath(gen); } if (_startHandled) { // even if Object started, need to start leaf-level name if (_needToHandleName) { _needToHandleName = false; // at parent must explicitly clear gen.writeFieldName(_currentName); } } else { _startHandled = true; if (_type == TYPE_OBJECT) { gen.writeStartObject(); if (_needToHandleName) { _needToHandleName = false; // at parent must explicitly clear gen.writeFieldName(_currentName); } } else if (_type == TYPE_ARRAY) { gen.writeStartArray(); } } } public TokenFilterContext closeArray(JsonGenerator gen) throws IOException { if (_startHandled) { gen.writeEndArray(); } if ((_filter != null) && (_filter != TokenFilter.INCLUDE_ALL)) { _filter.filterFinishArray(); } return _parent; } public TokenFilterContext closeObject(JsonGenerator gen) throws IOException { if (_startHandled) { gen.writeEndObject(); } if ((_filter != null) && (_filter != TokenFilter.INCLUDE_ALL)) { _filter.filterFinishObject(); } return _parent; } public void skipParentChecks() { _filter = null; for (TokenFilterContext ctxt = _parent; ctxt != null; ctxt = ctxt._parent) { _parent._filter = null; } } /* /********************************************************** /* Accessors, mutators /********************************************************** */ @Override public Object getCurrentValue() { return null; } @Override public void setCurrentValue(Object v) { } @Override public final TokenFilterContext getParent() { return _parent; } @Override public final String getCurrentName() { return _currentName; } // @since 2.9 @Override public boolean hasCurrentName() { return _currentName != null; } public TokenFilter getFilter() { return _filter; } public boolean isStartHandled() { return _startHandled; } public JsonToken nextTokenToRead() { if (!_startHandled) { _startHandled = true; if (_type == TYPE_OBJECT) { return JsonToken.START_OBJECT; } // Note: root should never be unhandled return JsonToken.START_ARRAY; } // But otherwise at most might have FIELD_NAME if (_needToHandleName && (_type == TYPE_OBJECT)) { _needToHandleName = false; return JsonToken.FIELD_NAME; } return null; } public TokenFilterContext findChildOf(TokenFilterContext parent) { if (_parent == parent) { return this; } TokenFilterContext curr = _parent; while (curr != null) { TokenFilterContext p = curr._parent; if (p == parent) { return curr; } curr = p; } // should never occur but... return null; } // // // Internally used abstract methods protected void appendDesc(StringBuilder sb) { if (_parent != null) { _parent.appendDesc(sb); } if (_type == TYPE_OBJECT) { sb.append('{'); if (_currentName != null) { sb.append('"'); // !!! TODO: Name chars should be escaped? sb.append(_currentName); sb.append('"'); } else { sb.append('?'); } sb.append('}'); } else if (_type == TYPE_ARRAY) { sb.append('['); sb.append(getCurrentIndex()); sb.append(']'); } else { // nah, ROOT: sb.append("/"); } } // // // Overridden standard methods /** * Overridden to provide developer writeable "JsonPath" representation * of the context. */ @Override public String toString() { StringBuilder sb = new StringBuilder(64); appendDesc(sb); return sb.toString(); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/format/000077500000000000000000000000001356164247300276275ustar00rootroot00000000000000DataFormatDetector.java000066400000000000000000000161251356164247300341340ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/formatpackage com.fasterxml.jackson.core.format; import java.io.*; import java.util.*; import com.fasterxml.jackson.core.*; /** * Simple helper class that allows data format (content type) auto-detection, * given an ordered set of {@link JsonFactory} instances to use for actual low-level * detection. */ public class DataFormatDetector { /** * By default we will look ahead at most 64 bytes; in most cases, * much less (4 bytes or so) is needed, but we will allow bit more * leniency to support data formats that need more complex heuristics. */ public final static int DEFAULT_MAX_INPUT_LOOKAHEAD = 64; /** * Ordered list of factories which both represent data formats to * detect (in precedence order, starting with highest) and are used * for actual detection. */ protected final JsonFactory[] _detectors; /** * Strength of match we consider to be good enough to be used * without checking any other formats. * Default value is {@link MatchStrength#SOLID_MATCH}, */ protected final MatchStrength _optimalMatch; /** * Strength of minimal match we accept as the answer, unless * better matches are found. * Default value is {@link MatchStrength#WEAK_MATCH}, */ protected final MatchStrength _minimalMatch; /** * Maximum number of leading bytes of the input that we can read * to determine data format. *

* Default value is {@link #DEFAULT_MAX_INPUT_LOOKAHEAD}. */ protected final int _maxInputLookahead; /* /********************************************************** /* Construction /********************************************************** */ public DataFormatDetector(JsonFactory... detectors) { this(detectors, MatchStrength.SOLID_MATCH, MatchStrength.WEAK_MATCH, DEFAULT_MAX_INPUT_LOOKAHEAD); } public DataFormatDetector(Collection detectors) { this(detectors.toArray(new JsonFactory[detectors.size()])); } /** * Method that will return a detector instance that uses given * optimal match level (match that is considered sufficient to return, without * trying to find stronger matches with other formats). */ public DataFormatDetector withOptimalMatch(MatchStrength optMatch) { if (optMatch == _optimalMatch) { return this; } return new DataFormatDetector(_detectors, optMatch, _minimalMatch, _maxInputLookahead); } /** * Method that will return a detector instance that uses given * minimal match level; match that may be returned unless a stronger match * is found with other format detectors. */ public DataFormatDetector withMinimalMatch(MatchStrength minMatch) { if (minMatch == _minimalMatch) { return this; } return new DataFormatDetector(_detectors, _optimalMatch, minMatch, _maxInputLookahead); } /** * Method that will return a detector instance that allows detectors to * read up to specified number of bytes when determining format match strength. */ public DataFormatDetector withMaxInputLookahead(int lookaheadBytes) { if (lookaheadBytes == _maxInputLookahead) { return this; } return new DataFormatDetector(_detectors, _optimalMatch, _minimalMatch, lookaheadBytes); } private DataFormatDetector(JsonFactory[] detectors, MatchStrength optMatch, MatchStrength minMatch, int maxInputLookahead) { _detectors = detectors; _optimalMatch = optMatch; _minimalMatch = minMatch; _maxInputLookahead = maxInputLookahead; } /* /********************************************************** /* Public API /********************************************************** */ /** * Method to call to find format that content (accessible via given * {@link InputStream}) given has, as per configuration of this detector * instance. * * @return Matcher object which contains result; never null, even in cases * where no match (with specified minimal match strength) is found. */ public DataFormatMatcher findFormat(InputStream in) throws IOException { return _findFormat(new InputAccessor.Std(in, new byte[_maxInputLookahead])); } /** * Method to call to find format that given content (full document) * has, as per configuration of this detector instance. * * @return Matcher object which contains result; never null, even in cases * where no match (with specified minimal match strength) is found. */ public DataFormatMatcher findFormat(byte[] fullInputData) throws IOException { return _findFormat(new InputAccessor.Std(fullInputData)); } /** * Method to call to find format that given content (full document) * has, as per configuration of this detector instance. * * @return Matcher object which contains result; never null, even in cases * where no match (with specified minimal match strength) is found. * * @since 2.1 */ public DataFormatMatcher findFormat(byte[] fullInputData, int offset, int len) throws IOException { return _findFormat(new InputAccessor.Std(fullInputData, offset, len)); } /* /********************************************************** /* Overrides /********************************************************** */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('['); final int len = _detectors.length; if (len > 0) { sb.append(_detectors[0].getFormatName()); for (int i = 1; i < len; ++i) { sb.append(", "); sb.append(_detectors[i].getFormatName()); } } sb.append(']'); return sb.toString(); } /* /********************************************************** /* Internal methods /********************************************************** */ private DataFormatMatcher _findFormat(InputAccessor.Std acc) throws IOException { JsonFactory bestMatch = null; MatchStrength bestMatchStrength = null; for (JsonFactory f : _detectors) { acc.reset(); MatchStrength strength = f.hasFormat(acc); // if not better than what we have so far (including minimal level limit), skip if (strength == null || strength.ordinal() < _minimalMatch.ordinal()) { continue; } // also, needs to better match than before if (bestMatch != null) { if (bestMatchStrength.ordinal() >= strength.ordinal()) { continue; } } // finally: if it's good enough match, we are done bestMatch = f; bestMatchStrength = strength; if (strength.ordinal() >= _optimalMatch.ordinal()) { break; } } return acc.createMatcher(bestMatch, bestMatchStrength); } } DataFormatMatcher.java000066400000000000000000000101671356164247300337460ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/formatpackage com.fasterxml.jackson.core.format; import java.io.*; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.io.MergedStream; /** * Result object constructed by {@link DataFormatDetector} when requested * to detect format of given input data. */ public class DataFormatMatcher { protected final InputStream _originalStream; /** * Content read during format matching process */ protected final byte[] _bufferedData; /** * Pointer to the first byte in buffer available for reading */ protected final int _bufferedStart; /** * Number of bytes available in buffer. */ protected final int _bufferedLength; /** * Factory that produced sufficient match (if any) */ protected final JsonFactory _match; /** * Strength of match with {@link #_match} */ protected final MatchStrength _matchStrength; protected DataFormatMatcher(InputStream in, byte[] buffered, int bufferedStart, int bufferedLength, JsonFactory match, MatchStrength strength) { _originalStream = in; _bufferedData = buffered; _bufferedStart = bufferedStart; _bufferedLength = bufferedLength; _match = match; _matchStrength = strength; // can't have negative offset or length if ((bufferedStart | bufferedLength) < 0 || (bufferedStart + bufferedLength) > buffered.length) { throw new IllegalArgumentException(String.format("Illegal start/length (%d/%d) wrt input array of %d bytes", bufferedStart, bufferedLength, buffered.length)); } } /* /********************************************************** /* Public API, simple accessors /********************************************************** */ /** * Accessor to use to see if any formats matched well enough with * the input data. */ public boolean hasMatch() { return _match != null; } /** * Method for accessing strength of the match, if any; if no match, * will return {@link MatchStrength#INCONCLUSIVE}. */ public MatchStrength getMatchStrength() { return (_matchStrength == null) ? MatchStrength.INCONCLUSIVE : _matchStrength; } /** * Accessor for {@link JsonFactory} that represents format that data matched. */ public JsonFactory getMatch() { return _match; } /** * Accessor for getting brief textual name of matched format if any (null * if none). Equivalent to: *

     *   return hasMatch() ? getMatch().getFormatName() : null;
     *
*/ public String getMatchedFormatName() { return _match.getFormatName(); } /* /********************************************************** /* Public API, factory methods /********************************************************** */ /** * Convenience method for trying to construct a {@link JsonParser} for * parsing content which is assumed to be in detected data format. * If no match was found, returns null. */ public JsonParser createParserWithMatch() throws IOException { if (_match == null) { return null; } if (_originalStream == null) { return _match.createParser(_bufferedData, _bufferedStart, _bufferedLength); } return _match.createParser(getDataStream()); } /** * Method to use for accessing input for which format detection has been done. * This must be used instead of using stream passed to detector * unless given stream itself can do buffering. * Stream will return all content that was read during matching process, as well * as remaining contents of the underlying stream. */ public InputStream getDataStream() { if (_originalStream == null) { return new ByteArrayInputStream(_bufferedData, _bufferedStart, _bufferedLength); } return new MergedStream(null, _originalStream, _bufferedData, _bufferedStart, _bufferedLength); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/format/InputAccessor.java000066400000000000000000000104141356164247300332540ustar00rootroot00000000000000package com.fasterxml.jackson.core.format; import java.io.*; import com.fasterxml.jackson.core.JsonFactory; /** * Interface used to expose beginning of a data file to data format * detection code. */ public interface InputAccessor { /** * Method to call to check if more input is available. * Since this may result in more content to be read (at least * one more byte), a {@link IOException} may get thrown. */ boolean hasMoreBytes() throws IOException; /** * Returns next byte available, if any; if no more bytes are * available, will throw {@link java.io.EOFException}. */ byte nextByte() throws IOException; /** * Method that can be called to reset accessor to read from beginning * of input. */ void reset(); /* /********************************************************** /* Standard implementation /********************************************************** */ /** * Basic implementation that reads data from given * {@link InputStream} and buffers it as necessary. */ class Std implements InputAccessor { protected final InputStream _in; protected final byte[] _buffer; protected final int _bufferedStart; /** * End of valid bytes in the buffer (points to one past last valid) */ protected int _bufferedEnd; /** * Pointer to next available buffered byte in {@link #_buffer}. */ protected int _ptr; /** * Constructor used when content to check is available via * input stream and must be read. */ public Std(InputStream in, byte[] buffer) { _in = in; _buffer = buffer; _bufferedStart = 0; _ptr = 0; _bufferedEnd = 0; } /** * Constructor used when the full input (or at least enough leading bytes * of full input) is available. */ public Std(byte[] inputDocument) { _in = null; _buffer = inputDocument; // we have it all: _bufferedStart = 0; _bufferedEnd = inputDocument.length; } /** * Constructor used when the full input (or at least enough leading bytes * of full input) is available. * * @since 2.1 */ public Std(byte[] inputDocument, int start, int len) { _in = null; _buffer = inputDocument; _ptr = start; _bufferedStart = start; _bufferedEnd = start+len; } @Override public boolean hasMoreBytes() throws IOException { if (_ptr < _bufferedEnd) { // already got more return true; } if (_in == null) { // nowhere to read from return false; } int amount = _buffer.length - _ptr; if (amount < 1) { // can not load any more return false; } int count = _in.read(_buffer, _ptr, amount); if (count <= 0) { // EOF return false; } _bufferedEnd += count; return true; } @Override public byte nextByte() throws IOException { // should we just try loading more automatically? if (_ptr >= _bufferedEnd) { if (!hasMoreBytes()) { throw new EOFException("Failed auto-detect: could not read more than "+_ptr+" bytes (max buffer size: "+_buffer.length+")"); } } return _buffer[_ptr++]; } @Override public void reset() { _ptr = _bufferedStart; } /* /********************************************************** /* Extended API for DataFormatDetector/Matcher /********************************************************** */ public DataFormatMatcher createMatcher(JsonFactory match, MatchStrength matchStrength) { return new DataFormatMatcher(_in, _buffer, _bufferedStart, (_bufferedEnd - _bufferedStart), match, matchStrength); } } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/format/MatchStrength.java000066400000000000000000000050261356164247300332500ustar00rootroot00000000000000package com.fasterxml.jackson.core.format; /** * Enumeration used to indicate strength of match between data format * and piece of data (typically beginning of a data file). * Values are in increasing match strength; and detectors should return * "strongest" value: that is, it should start with strongest match * criteria, and downgrading if criteria is not fulfilled. */ public enum MatchStrength { /** * Value that indicates that given data can not be in given format. */ NO_MATCH, /** * Value that indicates that detector can not find out whether could * be a match or not. * This can occur for example for textual data formats t * when there are so many leading spaces that detector can not * find the first data byte (because detectors typically limit lookahead * to some smallish value). */ INCONCLUSIVE, /** * Value that indicates that given data could be of specified format (i.e. * it can not be ruled out). This can occur for example when seen data * is both not in canonical formats (for example: JSON data should be a JSON Array or Object * not a scalar value, as per JSON specification) and there are known use case * where a format detected is actually used (plain JSON Strings are actually used, even * though specification does not indicate that as valid usage: as such, seeing a leading * double-quote could indicate a JSON String, which plausibly could indicate * non-standard JSON usage). */ WEAK_MATCH, /** * Value that indicates that given data conforms to (one of) canonical form(s) of * the data format. *

* For example, when testing for XML data format, * seeing a less-than character ("<") alone (with possible leading spaces) * would be a strong indication that data could * be in xml format (but see below for {@link #FULL_MATCH} description for more) */ SOLID_MATCH, /** * Value that indicates that given data contains a signature that is deemed * specific enough to uniquely indicate data format used. *

* For example, when testing for XML data format, * seing "<xml" as the first data bytes ("XML declaration", as per XML specification) * could give full confidence that data is indeed in XML format. * Not all data formats have unique leading identifiers to allow full matches; for example, * JSON only has heuristic matches and can have at most {@link #SOLID_MATCH}) match. */ FULL_MATCH ; } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/format/package-info.java000066400000000000000000000003371356164247300330210ustar00rootroot00000000000000/** * Package that contains interfaces needed for dynamic, pluggable * format (auto)detection; as well as basic utility classes for * simple format detection functionality. */ package com.fasterxml.jackson.core.format; jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/000077500000000000000000000000001356164247300267465ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/CharTypes.java000066400000000000000000000237371356164247300315270ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.util.Arrays; public final class CharTypes { private final static char[] HC = "0123456789ABCDEF".toCharArray(); private final static byte[] HB; static { int len = HC.length; HB = new byte[len]; for (int i = 0; i < len; ++i) { HB[i] = (byte) HC[i]; } } /** * Lookup table used for determining which input characters * need special handling when contained in text segment. */ private final static int[] sInputCodes; static { /* 96 would do for most cases (backslash is ASCII 94) * but if we want to do lookups by raw bytes it's better * to have full table */ final int[] table = new int[256]; // Control chars and non-space white space are not allowed unquoted for (int i = 0; i < 32; ++i) { table[i] = -1; } // And then string end and quote markers are special too table['"'] = 1; table['\\'] = 1; sInputCodes = table; } /** * Additionally we can combine UTF-8 decoding info into similar * data table. */ private final static int[] sInputCodesUTF8; static { final int[] table = new int[sInputCodes.length]; System.arraycopy(sInputCodes, 0, table, 0, table.length); for (int c = 128; c < 256; ++c) { int code; // We'll add number of bytes needed for decoding if ((c & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF) code = 2; } else if ((c & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF) code = 3; } else if ((c & 0xF8) == 0xF0) { // 4 bytes; double-char with surrogates and all... code = 4; } else { // And -1 seems like a good "universal" error marker... code = -1; } table[c] = code; } sInputCodesUTF8 = table; } /** * To support non-default (and -standard) unquoted field names mode, * need to have alternate checking. * Basically this is list of 8-bit ASCII characters that are legal * as part of Javascript identifier */ private final static int[] sInputCodesJsNames; static { final int[] table = new int[256]; // Default is "not a name char", mark ones that are Arrays.fill(table, -1); // Assume rules with JS same as Java (change if/as needed) for (int i = 33; i < 256; ++i) { if (Character.isJavaIdentifierPart((char) i)) { table[i] = 0; } } /* As per [JACKSON-267], '@', '#' and '*' are also to be accepted as well. * And '-' (for hyphenated names); and '+' for sake of symmetricity... */ table['@'] = 0; table['#'] = 0; table['*'] = 0; table['-'] = 0; table['+'] = 0; sInputCodesJsNames = table; } /** * This table is similar to Latin-1, except that it marks all "high-bit" * code as ok. They will be validated at a later point, when decoding * name */ private final static int[] sInputCodesUtf8JsNames; static { final int[] table = new int[256]; // start with 8-bit JS names System.arraycopy(sInputCodesJsNames, 0, table, 0, table.length); Arrays.fill(table, 128, 128, 0); sInputCodesUtf8JsNames = table; } /** * Decoding table used to quickly determine characters that are * relevant within comment content. */ private final static int[] sInputCodesComment; static { final int[] buf = new int[256]; // but first: let's start with UTF-8 multi-byte markers: System.arraycopy(sInputCodesUTF8, 128, buf, 128, 128); // default (0) means "ok" (skip); -1 invalid, others marked by char itself Arrays.fill(buf, 0, 32, -1); // invalid white space buf['\t'] = 0; // tab is still fine buf['\n'] = '\n'; // lf/cr need to be observed, ends cpp comment buf['\r'] = '\r'; buf['*'] = '*'; // end marker for c-style comments sInputCodesComment = buf; } /** * Decoding table used for skipping white space and comments. * * @since 2.3 */ private final static int[] sInputCodesWS; static { // but first: let's start with UTF-8 multi-byte markers: final int[] buf = new int[256]; System.arraycopy(sInputCodesUTF8, 128, buf, 128, 128); // default (0) means "not whitespace" (end); 1 "whitespace", -1 invalid, // 2-4 UTF-8 multi-bytes, others marked by char itself // Arrays.fill(buf, 0, 32, -1); // invalid white space buf[' '] = 1; buf['\t'] = 1; buf['\n'] = '\n'; // lf/cr need to be observed, ends cpp comment buf['\r'] = '\r'; buf['/'] = '/'; // start marker for c/cpp comments buf['#'] = '#'; // start marker for YAML comments sInputCodesWS = buf; } /** * Lookup table used for determining which output characters in * 7-bit ASCII range need to be quoted. */ private final static int[] sOutputEscapes128; static { int[] table = new int[128]; // Control chars need generic escape sequence for (int i = 0; i < 32; ++i) { // 04-Mar-2011, tatu: Used to use "-(i + 1)", replaced with constant table[i] = CharacterEscapes.ESCAPE_STANDARD; } // Others (and some within that range too) have explicit shorter sequences table['"'] = '"'; table['\\'] = '\\'; // Escaping of slash is optional, so let's not add it table[0x08] = 'b'; table[0x09] = 't'; table[0x0C] = 'f'; table[0x0A] = 'n'; table[0x0D] = 'r'; sOutputEscapes128 = table; } /** * Lookup table for the first 256 Unicode characters (ASCII / UTF-8) * range. For actual hex digits, contains corresponding value; * for others -1. *

* NOTE: before 2.10.1, was of size 128, extended for simpler handling */ private final static int[] sHexValues = new int[256]; static { Arrays.fill(sHexValues, -1); for (int i = 0; i < 10; ++i) { sHexValues['0' + i] = i; } for (int i = 0; i < 6; ++i) { sHexValues['a' + i] = 10 + i; sHexValues['A' + i] = 10 + i; } } public static int[] getInputCodeLatin1() { return sInputCodes; } public static int[] getInputCodeUtf8() { return sInputCodesUTF8; } public static int[] getInputCodeLatin1JsNames() { return sInputCodesJsNames; } public static int[] getInputCodeUtf8JsNames() { return sInputCodesUtf8JsNames; } public static int[] getInputCodeComment() { return sInputCodesComment; } public static int[] getInputCodeWS() { return sInputCodesWS; } /** * Accessor for getting a read-only encoding table for first 128 Unicode * code points (single-byte UTF-8 characters). * Value of 0 means "no escaping"; other positive values that value is character * to use after backslash; and negative values that generic (backslash - u) * escaping is to be used. */ public static int[] get7BitOutputEscapes() { return sOutputEscapes128; } /** * Alternative to {@link #get7BitOutputEscapes()} when a non-standard quote character * is used. * * @since 2.10 */ public static int[] get7BitOutputEscapes(int quoteChar) { if (quoteChar == '"') { return sOutputEscapes128; } return AltEscapes.instance.escapesFor(quoteChar); } public static int charToHex(int ch) { // 08-Nov-2019, tatu: As per [core#540] and [core#578], changed to // force masking here so caller need not do that. return sHexValues[ch & 0xFF]; } public static void appendQuoted(StringBuilder sb, String content) { final int[] escCodes = sOutputEscapes128; int escLen = escCodes.length; for (int i = 0, len = content.length(); i < len; ++i) { char c = content.charAt(i); if (c >= escLen || escCodes[c] == 0) { sb.append(c); continue; } sb.append('\\'); int escCode = escCodes[c]; if (escCode < 0) { // generic quoting (hex value) // The only negative value sOutputEscapes128 returns // is CharacterEscapes.ESCAPE_STANDARD, which mean // appendQuotes should encode using the Unicode encoding; // not sure if this is the right way to encode for // CharacterEscapes.ESCAPE_CUSTOM or other (future) // CharacterEscapes.ESCAPE_XXX values. // We know that it has to fit in just 2 hex chars sb.append('u'); sb.append('0'); sb.append('0'); int value = c; // widening sb.append(HC[value >> 4]); sb.append(HC[value & 0xF]); } else { // "named", i.e. prepend with slash sb.append((char) escCode); } } } public static char[] copyHexChars() { return (char[]) HC.clone(); } public static byte[] copyHexBytes() { return (byte[]) HB.clone(); } // @since 2.10 private static class AltEscapes { public final static AltEscapes instance = new AltEscapes(); private int[][] _altEscapes = new int[128][]; public int[] escapesFor(int quoteChar) { int[] esc = _altEscapes[quoteChar]; if (esc == null) { esc = Arrays.copyOf(sOutputEscapes128, 128); // Only add escape setting if character does not already have it if (esc[quoteChar] == 0) { esc[quoteChar] = CharacterEscapes.ESCAPE_STANDARD; } _altEscapes[quoteChar] = esc; } return esc; } } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/CharacterEscapes.java000066400000000000000000000053461356164247300330210ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.util.Arrays; import com.fasterxml.jackson.core.SerializableString; /** * Abstract base class that defines interface for customizing character * escaping aspects for String values, for formats that use escaping. * For JSON this applies to both property names and String values. */ @SuppressWarnings("serial") public abstract class CharacterEscapes implements java.io.Serializable // since 2.1 { /** * Value used for lookup tables to indicate that matching characters * do not need to be escaped. */ public final static int ESCAPE_NONE = 0; /** * Value used for lookup tables to indicate that matching characters * are to be escaped using standard escaping; for JSON this means * (for example) using "backslash - u" escape method. */ public final static int ESCAPE_STANDARD = -1; /** * Value used for lookup tables to indicate that matching characters * will need custom escapes; and that another call * to {@link #getEscapeSequence} is needed to figure out exact escape * sequence to output. */ public final static int ESCAPE_CUSTOM = -2; /** * Method generators can call to get lookup table for determining * escape handling for first 128 characters of Unicode (ASCII * characters. Caller is not to modify contents of this array, since * this is expected to be a shared copy. * * @return Array with size of at least 128, where first 128 entries * have either one of ESCAPE_xxx constants, or non-zero positive * integer (meaning of which is data format specific; for JSON it means * that combination of backslash and character with that value is to be used) * to indicate that specific escape sequence is to be used. */ public abstract int[] getEscapeCodesForAscii(); /** * Method generators can call to get lookup table for determining * exact escape sequence to use for given character. * It can be called for any character, but typically is called for * either for ASCII characters for which custom escape * sequence is needed; or for any non-ASCII character. */ public abstract SerializableString getEscapeSequence(int ch); /** * Helper method that can be used to get a copy of standard JSON * escape definitions; this is useful when just wanting to slightly * customize definitions. Caller can modify this array as it sees * fit and usually returns modified instance via {@link #getEscapeCodesForAscii} */ public static int[] standardAsciiEscapesForJSON() { int[] esc = CharTypes.get7BitOutputEscapes(); return Arrays.copyOf(esc, esc.length); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/DataOutputAsStream.java000066400000000000000000000016641356164247300333520ustar00rootroot00000000000000package 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 { } */ } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/IOContext.java000066400000000000000000000233131356164247300314670ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.util.BufferRecycler; import com.fasterxml.jackson.core.util.TextBuffer; /** * To limit number of configuration and state objects to pass, all * contextual objects that need to be passed by the factory to * readers and writers are combined under this object. One instance * is created for each reader and writer. *

* NOTE: non-final since 2.4, to allow sub-classing. */ public class IOContext { /* /********************************************************** /* Configuration /********************************************************** */ /** * Reference to the source object, which can be used for displaying * location information */ protected final Object _sourceRef; /** * Encoding used by the underlying stream, if known. */ protected JsonEncoding _encoding; /** * Flag that indicates whether underlying input/output source/target * object is fully managed by the owner of this context (parser or * generator). If true, it is, and is to be closed by parser/generator; * if false, calling application has to do closing (unless auto-closing * feature is enabled for the parser/generator in question; in which * case it acts like the owner). */ protected final boolean _managedResource; /* /********************************************************** /* Buffer handling, recycling /********************************************************** */ /** * Recycler used for actual allocation/deallocation/reuse */ protected final BufferRecycler _bufferRecycler; /** * Reference to the allocated I/O buffer for low-level input reading, * if any allocated. */ protected byte[] _readIOBuffer; /** * Reference to the allocated I/O buffer used for low-level * encoding-related buffering. */ protected byte[] _writeEncodingBuffer; /** * Reference to the buffer allocated for temporary use with * base64 encoding or decoding. */ protected byte[] _base64Buffer; /** * Reference to the buffer allocated for tokenization purposes, * in which character input is read, and from which it can be * further returned. */ protected char[] _tokenCBuffer; /** * Reference to the buffer allocated for buffering it for * output, before being encoded: generally this means concatenating * output, then encoding when buffer fills up. */ protected char[] _concatCBuffer; /** * Reference temporary buffer Parser instances need if calling * app decides it wants to access name via 'getTextCharacters' method. * Regular text buffer can not be used as it may contain textual * representation of the value token. */ protected char[] _nameCopyBuffer; /* /********************************************************** /* Life-cycle /********************************************************** */ public IOContext(BufferRecycler br, Object sourceRef, boolean managedResource) { _bufferRecycler = br; _sourceRef = sourceRef; _managedResource = managedResource; } public void setEncoding(JsonEncoding enc) { _encoding = enc; } /** * @since 1.6 */ public IOContext withEncoding(JsonEncoding enc) { _encoding = enc; return this; } /* /********************************************************** /* Public API, accessors /********************************************************** */ public Object getSourceReference() { return _sourceRef; } public JsonEncoding getEncoding() { return _encoding; } public boolean isResourceManaged() { return _managedResource; } /* /********************************************************** /* Public API, buffer management /********************************************************** */ public TextBuffer constructTextBuffer() { return new TextBuffer(_bufferRecycler); } /** *

* Note: the method can only be called once during its life cycle. * This is to protect against accidental sharing. */ public byte[] allocReadIOBuffer() { _verifyAlloc(_readIOBuffer); return (_readIOBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.BYTE_READ_IO_BUFFER)); } /** * @since 2.4 */ public byte[] allocReadIOBuffer(int minSize) { _verifyAlloc(_readIOBuffer); return (_readIOBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.BYTE_READ_IO_BUFFER, minSize)); } public byte[] allocWriteEncodingBuffer() { _verifyAlloc(_writeEncodingBuffer); return (_writeEncodingBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.BYTE_WRITE_ENCODING_BUFFER)); } /** * @since 2.4 */ public byte[] allocWriteEncodingBuffer(int minSize) { _verifyAlloc(_writeEncodingBuffer); return (_writeEncodingBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.BYTE_WRITE_ENCODING_BUFFER, minSize)); } /** * @since 2.1 */ public byte[] allocBase64Buffer() { _verifyAlloc(_base64Buffer); return (_base64Buffer = _bufferRecycler.allocByteBuffer(BufferRecycler.BYTE_BASE64_CODEC_BUFFER)); } /** * @since 2.9 */ public byte[] allocBase64Buffer(int minSize) { _verifyAlloc(_base64Buffer); return (_base64Buffer = _bufferRecycler.allocByteBuffer(BufferRecycler.BYTE_BASE64_CODEC_BUFFER, minSize)); } public char[] allocTokenBuffer() { _verifyAlloc(_tokenCBuffer); return (_tokenCBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CHAR_TOKEN_BUFFER)); } /** * @since 2.4 */ public char[] allocTokenBuffer(int minSize) { _verifyAlloc(_tokenCBuffer); return (_tokenCBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CHAR_TOKEN_BUFFER, minSize)); } public char[] allocConcatBuffer() { _verifyAlloc(_concatCBuffer); return (_concatCBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CHAR_CONCAT_BUFFER)); } public char[] allocNameCopyBuffer(int minSize) { _verifyAlloc(_nameCopyBuffer); return (_nameCopyBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CHAR_NAME_COPY_BUFFER, minSize)); } /** * Method to call when all the processing buffers can be safely * recycled. */ public void releaseReadIOBuffer(byte[] buf) { if (buf != null) { /* Let's do sanity checks to ensure once-and-only-once release, * as well as avoiding trying to release buffers not owned */ _verifyRelease(buf, _readIOBuffer); _readIOBuffer = null; _bufferRecycler.releaseByteBuffer(BufferRecycler.BYTE_READ_IO_BUFFER, buf); } } public void releaseWriteEncodingBuffer(byte[] buf) { if (buf != null) { /* Let's do sanity checks to ensure once-and-only-once release, * as well as avoiding trying to release buffers not owned */ _verifyRelease(buf, _writeEncodingBuffer); _writeEncodingBuffer = null; _bufferRecycler.releaseByteBuffer(BufferRecycler.BYTE_WRITE_ENCODING_BUFFER, buf); } } public void releaseBase64Buffer(byte[] buf) { if (buf != null) { // sanity checks, release once-and-only-once, must be one owned _verifyRelease(buf, _base64Buffer); _base64Buffer = null; _bufferRecycler.releaseByteBuffer(BufferRecycler.BYTE_BASE64_CODEC_BUFFER, buf); } } public void releaseTokenBuffer(char[] buf) { if (buf != null) { _verifyRelease(buf, _tokenCBuffer); _tokenCBuffer = null; _bufferRecycler.releaseCharBuffer(BufferRecycler.CHAR_TOKEN_BUFFER, buf); } } public void releaseConcatBuffer(char[] buf) { if (buf != null) { // 14-Jan-2014, tatu: Let's actually allow upgrade of the original buffer. _verifyRelease(buf, _concatCBuffer); _concatCBuffer = null; _bufferRecycler.releaseCharBuffer(BufferRecycler.CHAR_CONCAT_BUFFER, buf); } } public void releaseNameCopyBuffer(char[] buf) { if (buf != null) { // 14-Jan-2014, tatu: Let's actually allow upgrade of the original buffer. _verifyRelease(buf, _nameCopyBuffer); _nameCopyBuffer = null; _bufferRecycler.releaseCharBuffer(BufferRecycler.CHAR_NAME_COPY_BUFFER, buf); } } /* /********************************************************** /* Internal helpers /********************************************************** */ protected final void _verifyAlloc(Object buffer) { if (buffer != null) { throw new IllegalStateException("Trying to call same allocXxx() method second time"); } } protected final void _verifyRelease(byte[] toRelease, byte[] src) { // 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) { // 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() { // sanity check failed; trying to return different, smaller buffer. return new IllegalArgumentException("Trying to release buffer smaller than original"); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/InputDecorator.java000066400000000000000000000076311356164247300325620ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.io.*; /** * Handler class that can be used to decorate input sources. * Typical use is to use a filter abstraction (filtered stream, * reader) around original input source, and apply additional * processing during read operations. */ public abstract class InputDecorator implements java.io.Serializable // since 2.1 { private static final long serialVersionUID = 1L; /** * Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when * creating parser given an {@link InputStream}, when this decorator * has been registered. * * @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 in Original input source * * @return InputStream to use; either 'in' as is, or decorator * version that typically delogates to 'in' */ public abstract InputStream decorate(IOContext ctxt, InputStream in) throws IOException; /** * Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when * creating parser on given "raw" byte source. * Method can either construct a {@link InputStream} for reading; or return * null to indicate that no wrapping should occur. * * @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 src Input buffer that contains contents to parse * @param offset Offset of the first available byte in the input buffer * @param length Number of bytes available in the input buffer * * @return Either {@link InputStream} to use as input source; or null to indicate * that contents are to be processed as-is by caller */ 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. *

* 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 * has been registered. * * @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 r Original reader * * @return Reader to use; either passed in argument, or something that * calls it (for example, a {@link FilterReader}) */ public abstract Reader decorate(IOContext ctxt, Reader r) throws IOException; } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/JsonEOFException.java000066400000000000000000000022121356164247300327300ustar00rootroot00000000000000package 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; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/JsonStringEncoder.java000066400000000000000000000447151356164247300332240ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.util.Arrays; import com.fasterxml.jackson.core.util.ByteArrayBuilder; import com.fasterxml.jackson.core.util.TextBuffer; /** * Helper class used for efficient encoding of JSON String values (including * JSON field names) into Strings or UTF-8 byte arrays. *

* Note that methods in here are somewhat optimized, but not ridiculously so. * Reason is that conversion method results are expected to be cached so that * these methods will not be hot spots during normal operation. */ public final class JsonStringEncoder { /* /********************************************************************** /* Constants /********************************************************************** */ private final static char[] HC = CharTypes.copyHexChars(); private final static byte[] HB = CharTypes.copyHexBytes(); private final static int SURR1_FIRST = 0xD800; private final static int SURR1_LAST = 0xDBFF; private final static int SURR2_FIRST = 0xDC00; private final static int SURR2_LAST = 0xDFFF; private final static int INITIAL_CHAR_BUFFER_SIZE = 120; private final static int INITIAL_BYTE_BUFFER_SIZE = 200; /* /********************************************************************** /* Construction, instance access /********************************************************************** */ // Since 2.10 we have stateless singleton and NO fancy ThreadLocal/SofRef caching!!! private final static JsonStringEncoder instance = new JsonStringEncoder(); public JsonStringEncoder() { } /** * Factory method for getting an instance; this is either recycled per-thread instance, * or a newly constructed one. */ public static JsonStringEncoder getInstance() { return instance; } /* /********************************************************************** /* Public API /********************************************************************** */ /** * Method that will quote text contents using JSON standard quoting, * and return results as a character array */ public char[] quoteAsString(String input) { char[] outputBuffer = new char[INITIAL_CHAR_BUFFER_SIZE]; final int[] escCodes = CharTypes.get7BitOutputEscapes(); final int escCodeCount = escCodes.length; int inPtr = 0; final int inputLen = input.length(); TextBuffer textBuffer = null; int outPtr = 0; char[] qbuf = null; outer: while (inPtr < inputLen) { tight_loop: while (true) { char c = input.charAt(inPtr); if (c < escCodeCount && escCodes[c] != 0) { break tight_loop; } if (outPtr >= outputBuffer.length) { if (textBuffer == null) { textBuffer = TextBuffer.fromInitial(outputBuffer); } outputBuffer = textBuffer.finishCurrentSegment(); outPtr = 0; } outputBuffer[outPtr++] = c; if (++inPtr >= inputLen) { break outer; } } // something to escape; 2 or 6-char variant? if (qbuf == null) { qbuf = _qbuf(); } char d = input.charAt(inPtr++); int escCode = escCodes[d]; int length = (escCode < 0) ? _appendNumeric(d, qbuf) : _appendNamed(escCode, qbuf); ; if ((outPtr + length) > outputBuffer.length) { int first = outputBuffer.length - outPtr; if (first > 0) { System.arraycopy(qbuf, 0, outputBuffer, outPtr, first); } if (textBuffer == null) { textBuffer = TextBuffer.fromInitial(outputBuffer); } outputBuffer = textBuffer.finishCurrentSegment(); int second = length - first; System.arraycopy(qbuf, first, outputBuffer, 0, second); outPtr = second; } else { System.arraycopy(qbuf, 0, outputBuffer, outPtr, length); outPtr += length; } } if (textBuffer == null) { return Arrays.copyOfRange(outputBuffer, 0, outPtr); } textBuffer.setCurrentLength(outPtr); return textBuffer.contentsAsArray(); } /** * Overloaded variant of {@link #quoteAsString(String)}. * * @since 2.10 */ public char[] quoteAsString(CharSequence input) { // 15-Aug-2019, tatu: Optimize common case as JIT can't get rid of overhead otherwise if (input instanceof String) { return quoteAsString((String) input); } TextBuffer textBuffer = null; char[] outputBuffer = new char[INITIAL_CHAR_BUFFER_SIZE]; final int[] escCodes = CharTypes.get7BitOutputEscapes(); final int escCodeCount = escCodes.length; int inPtr = 0; final int inputLen = input.length(); int outPtr = 0; char[] qbuf = null; outer: while (inPtr < inputLen) { tight_loop: while (true) { char c = input.charAt(inPtr); if (c < escCodeCount && escCodes[c] != 0) { break tight_loop; } if (outPtr >= outputBuffer.length) { if (textBuffer == null) { textBuffer = TextBuffer.fromInitial(outputBuffer); } outputBuffer = textBuffer.finishCurrentSegment(); outPtr = 0; } outputBuffer[outPtr++] = c; if (++inPtr >= inputLen) { break outer; } } // something to escape; 2 or 6-char variant? if (qbuf == null) { qbuf = _qbuf(); } char d = input.charAt(inPtr++); int escCode = escCodes[d]; int length = (escCode < 0) ? _appendNumeric(d, qbuf) : _appendNamed(escCode, qbuf); ; if ((outPtr + length) > outputBuffer.length) { int first = outputBuffer.length - outPtr; if (first > 0) { System.arraycopy(qbuf, 0, outputBuffer, outPtr, first); } if (textBuffer == null) { textBuffer = TextBuffer.fromInitial(outputBuffer); } outputBuffer = textBuffer.finishCurrentSegment(); int second = length - first; System.arraycopy(qbuf, first, outputBuffer, 0, second); outPtr = second; } else { System.arraycopy(qbuf, 0, outputBuffer, outPtr, length); outPtr += length; } } if (textBuffer == null) { return Arrays.copyOfRange(outputBuffer, 0, outPtr); } textBuffer.setCurrentLength(outPtr); return textBuffer.contentsAsArray(); } /** * 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(); char[] qbuf = null; 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? if (qbuf == null) { qbuf = _qbuf(); } 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. */ @SuppressWarnings("resource") public byte[] quoteAsUTF8(String text) { int inputPtr = 0; int inputEnd = text.length(); int outputPtr = 0; byte[] outputBuffer = new byte[INITIAL_BYTE_BUFFER_SIZE]; ByteArrayBuilder bb = null; main: while (inputPtr < inputEnd) { final int[] escCodes = CharTypes.get7BitOutputEscapes(); inner_loop: // ASCII and escapes while (true) { int ch = text.charAt(inputPtr); if (ch > 0x7F || escCodes[ch] != 0) { break inner_loop; } if (outputPtr >= outputBuffer.length) { if (bb == null) { bb = ByteArrayBuilder.fromInitial(outputBuffer, outputPtr); } outputBuffer = bb.finishCurrentSegment(); outputPtr = 0; } outputBuffer[outputPtr++] = (byte) ch; if (++inputPtr >= inputEnd) { break main; } } if (bb == null) { bb = ByteArrayBuilder.fromInitial(outputBuffer, outputPtr); } if (outputPtr >= outputBuffer.length) { outputBuffer = bb.finishCurrentSegment(); outputPtr = 0; } // Ok, so what did we hit? int ch = (int) text.charAt(inputPtr++); if (ch <= 0x7F) { // needs quoting int escape = escCodes[ch]; // ctrl-char, 6-byte escape... outputPtr = _appendByte(ch, escape, bb, outputPtr); outputBuffer = bb.getCurrentSegment(); continue main; } if (ch <= 0x7FF) { // fine, just needs 2 byte output outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6)); ch = (0x80 | (ch & 0x3f)); } else { // 3 or 4 bytes // Surrogates? if (ch < SURR1_FIRST || ch > SURR2_LAST) { // nope outputBuffer[outputPtr++] = (byte) (0xe0 | (ch >> 12)); if (outputPtr >= outputBuffer.length) { outputBuffer = bb.finishCurrentSegment(); outputPtr = 0; } outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f)); ch = (0x80 | (ch & 0x3f)); } else { // yes, surrogate pair if (ch > SURR1_LAST) { // must be from first range _illegal(ch); } // and if so, followed by another from next range if (inputPtr >= inputEnd) { _illegal(ch); } ch = _convert(ch, text.charAt(inputPtr++)); if (ch > 0x10FFFF) { // illegal, as per RFC 4627 _illegal(ch); } outputBuffer[outputPtr++] = (byte) (0xf0 | (ch >> 18)); if (outputPtr >= outputBuffer.length) { outputBuffer = bb.finishCurrentSegment(); outputPtr = 0; } outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 12) & 0x3f)); if (outputPtr >= outputBuffer.length) { outputBuffer = bb.finishCurrentSegment(); outputPtr = 0; } outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f)); ch = (0x80 | (ch & 0x3f)); } } if (outputPtr >= outputBuffer.length) { outputBuffer = bb.finishCurrentSegment(); outputPtr = 0; } outputBuffer[outputPtr++] = (byte) ch; } if (bb == null) { return Arrays.copyOfRange(outputBuffer, 0, outputPtr); } return bb.completeAndCoalesce(outputPtr); } /** * Will encode given String as UTF-8 (without any quoting), return * resulting byte array. */ @SuppressWarnings("resource") public byte[] encodeAsUTF8(String text) { int inputPtr = 0; int inputEnd = text.length(); int outputPtr = 0; byte[] outputBuffer = new byte[INITIAL_BYTE_BUFFER_SIZE]; int outputEnd = outputBuffer.length; ByteArrayBuilder bb = null; main_loop: while (inputPtr < inputEnd) { int c = text.charAt(inputPtr++); // first tight loop for ascii while (c <= 0x7F) { if (outputPtr >= outputEnd) { if (bb == null) { bb = ByteArrayBuilder.fromInitial(outputBuffer, outputPtr); } outputBuffer = bb.finishCurrentSegment(); outputEnd = outputBuffer.length; outputPtr = 0; } outputBuffer[outputPtr++] = (byte) c; if (inputPtr >= inputEnd) { break main_loop; } c = text.charAt(inputPtr++); } // then multi-byte... if (bb == null) { bb = ByteArrayBuilder.fromInitial(outputBuffer, outputPtr); } if (outputPtr >= outputEnd) { outputBuffer = bb.finishCurrentSegment(); outputEnd = outputBuffer.length; outputPtr = 0; } if (c < 0x800) { // 2-byte outputBuffer[outputPtr++] = (byte) (0xc0 | (c >> 6)); } else { // 3 or 4 bytes // Surrogates? if (c < SURR1_FIRST || c > SURR2_LAST) { // nope outputBuffer[outputPtr++] = (byte) (0xe0 | (c >> 12)); if (outputPtr >= outputEnd) { outputBuffer = bb.finishCurrentSegment(); outputEnd = outputBuffer.length; outputPtr = 0; } outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); } else { // yes, surrogate pair if (c > SURR1_LAST) { // must be from first range _illegal(c); } // and if so, followed by another from next range if (inputPtr >= inputEnd) { _illegal(c); } c = _convert(c, text.charAt(inputPtr++)); if (c > 0x10FFFF) { // illegal, as per RFC 4627 _illegal(c); } outputBuffer[outputPtr++] = (byte) (0xf0 | (c >> 18)); if (outputPtr >= outputEnd) { outputBuffer = bb.finishCurrentSegment(); outputEnd = outputBuffer.length; outputPtr = 0; } outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); if (outputPtr >= outputEnd) { outputBuffer = bb.finishCurrentSegment(); outputEnd = outputBuffer.length; outputPtr = 0; } outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); } } if (outputPtr >= outputEnd) { outputBuffer = bb.finishCurrentSegment(); outputEnd = outputBuffer.length; outputPtr = 0; } outputBuffer[outputPtr++] = (byte) (0x80 | (c & 0x3f)); } if (bb == null) { return Arrays.copyOfRange(outputBuffer, 0, outputPtr); } return bb.completeAndCoalesce(outputPtr); } /* /********************************************************************** /* Internal methods /********************************************************************** */ private char[] _qbuf() { char[] qbuf = new char[6]; qbuf[0] = '\\'; qbuf[2] = '0'; qbuf[3] = '0'; return qbuf; } private int _appendNumeric(int value, char[] qbuf) { qbuf[1] = 'u'; // We know it's a control char, so only the last 2 chars are non-0 qbuf[4] = HC[value >> 4]; qbuf[5] = HC[value & 0xF]; return 6; } private int _appendNamed(int esc, char[] qbuf) { qbuf[1] = (char) esc; return 2; } private int _appendByte(int ch, int esc, ByteArrayBuilder bb, int ptr) { bb.setCurrentSegmentLength(ptr); bb.append('\\'); if (esc < 0) { // standard escape bb.append('u'); if (ch > 0xFF) { int hi = (ch >> 8); bb.append(HB[hi >> 4]); bb.append(HB[hi & 0xF]); ch &= 0xFF; } else { bb.append('0'); bb.append('0'); } bb.append(HB[ch >> 4]); bb.append(HB[ch & 0xF]); } else { // 2-char simple escape bb.append((byte) esc); } return bb.getCurrentSegmentLength(); } private static int _convert(int p1, int p2) { // Ok, then, is the second part valid? if (p2 < SURR2_FIRST || p2 > SURR2_LAST) { throw new IllegalArgumentException("Broken surrogate pair: first char 0x"+Integer.toHexString(p1)+", second 0x"+Integer.toHexString(p2)+"; illegal combination"); } return 0x10000 + ((p1 - SURR1_FIRST) << 10) + (p2 - SURR2_FIRST); } private static void _illegal(int c) { throw new IllegalArgumentException(UTF8Writer.illegalSurrogateDesc(c)); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/MergedStream.java000066400000000000000000000057071356164247300322010ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.io.*; /** * Simple {@link InputStream} implementation that is used to "unwind" some * data previously read from an input stream; so that as long as some of * that data remains, it's returned; but as long as it's read, we'll * just use data from the underlying original stream. * This is similar to {@link java.io.PushbackInputStream}, but here there's * only one implicit pushback, when instance is constructed. */ public final class MergedStream extends InputStream { final private IOContext _ctxt; final private InputStream _in; private byte[] _b; private int _ptr; final private int _end; public MergedStream(IOContext ctxt, InputStream in, byte[] buf, int start, int end) { _ctxt = ctxt; _in = in; _b = buf; _ptr = start; _end = end; } @Override public int available() throws IOException { if (_b != null) { return _end - _ptr; } return _in.available(); } @Override public void close() throws IOException { _free(); _in.close(); } @Override public void mark(int readlimit) { if (_b == null) { _in.mark(readlimit); } } @Override public boolean markSupported() { // Only supports marks past the initial rewindable section... return (_b == null) && _in.markSupported(); } @Override public int read() throws IOException { if (_b != null) { int c = _b[_ptr++] & 0xFF; if (_ptr >= _end) { _free(); } return c; } return _in.read(); } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public int read(byte[] b, int off, int len) throws IOException { if (_b != null) { int avail = _end - _ptr; if (len > avail) { len = avail; } System.arraycopy(_b, _ptr, b, off, len); _ptr += len; if (_ptr >= _end) { _free(); } return len; } return _in.read(b, off, len); } @Override public void reset() throws IOException { if (_b == null) { _in.reset(); } } @Override public long skip(long n) throws IOException { long count = 0L; if (_b != null) { int amount = _end - _ptr; if (amount > n) { // all in pushed back segment? _ptr += (int) n; return n; } _free(); count += amount; n -= amount; } if (n > 0) { count += _in.skip(n); } return count; } private void _free() { byte[] buf = _b; if (buf != null) { _b = null; if (_ctxt != null) { _ctxt.releaseReadIOBuffer(buf); } } } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java000066400000000000000000000227551356164247300320740ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.math.BigDecimal; public final class NumberInput { /** * Textual representation of a double constant that can cause nasty problems * with JDK (see http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308). */ public final static String NASTY_SMALL_DOUBLE = "2.2250738585072012e-308"; /** * Constants needed for parsing longs from basic int parsing methods */ final static long L_BILLION = 1000000000; final static String MIN_LONG_STR_NO_SIGN = String.valueOf(Long.MIN_VALUE).substring(1); final static String MAX_LONG_STR = String.valueOf(Long.MAX_VALUE); /** * Fast method for parsing integers that are known to fit into * regular 32-bit signed int type. This means that length is * between 1 and 9 digits (inclusive) *

* Note: public to let unit tests call it */ public static int parseInt(char[] ch, int off, int len) { int num = ch[off + len - 1] - '0'; switch(len) { case 9: num += (ch[off++] - '0') * 100000000; case 8: num += (ch[off++] - '0') * 10000000; case 7: num += (ch[off++] - '0') * 1000000; case 6: num += (ch[off++] - '0') * 100000; case 5: num += (ch[off++] - '0') * 10000; case 4: num += (ch[off++] - '0') * 1000; case 3: num += (ch[off++] - '0') * 100; case 2: num += (ch[off] - '0') * 10; } return num; } /** * Helper method to (more) efficiently parse integer numbers from * String values. */ public static int parseInt(String s) { /* Ok: let's keep strategy simple: ignoring optional minus sign, * we'll accept 1 - 9 digits and parse things efficiently; * otherwise just defer to JDK parse functionality. */ char c = s.charAt(0); int len = s.length(); boolean neg = (c == '-'); int offset = 1; // must have 1 - 9 digits after optional sign: // negative? if (neg) { if (len == 1 || len > 10) { return Integer.parseInt(s); } c = s.charAt(offset++); } else { if (len > 9) { return Integer.parseInt(s); } } if (c > '9' || c < '0') { return Integer.parseInt(s); } int num = c - '0'; if (offset < len) { c = s.charAt(offset++); if (c > '9' || c < '0') { return Integer.parseInt(s); } num = (num * 10) + (c - '0'); if (offset < len) { c = s.charAt(offset++); if (c > '9' || c < '0') { return Integer.parseInt(s); } num = (num * 10) + (c - '0'); // Let's just loop if we have more than 3 digits: if (offset < len) { do { c = s.charAt(offset++); if (c > '9' || c < '0') { return Integer.parseInt(s); } num = (num * 10) + (c - '0'); } while (offset < len); } } } return neg ? -num : num; } public static long parseLong(char[] ch, int off, int len) { // Note: caller must ensure length is [10, 18] int len1 = len-9; long val = parseInt(ch, off, len1) * L_BILLION; return val + (long) parseInt(ch, off+len1, 9); } public static long parseLong(String s) { /* Ok, now; as the very first thing, let's just optimize case of "fake longs"; * that is, if we know they must be ints, call int parsing */ int length = s.length(); if (length <= 9) { return (long) parseInt(s); } // !!! TODO: implement efficient 2-int parsing... return Long.parseLong(s); } /** * Helper method for determining if given String representation of * an integral number would fit in 64-bit Java long or not. * Note that input String must NOT contain leading minus sign (even * if 'negative' is set to true). * * @param negative Whether original number had a minus sign (which is * NOT passed to this method) or not */ public static boolean inLongRange(char[] ch, int off, int len, boolean negative) { String cmpStr = negative ? MIN_LONG_STR_NO_SIGN : MAX_LONG_STR; int cmpLen = cmpStr.length(); if (len < cmpLen) return true; if (len > cmpLen) return false; for (int i = 0; i < cmpLen; ++i) { int diff = ch[off+i] - cmpStr.charAt(i); if (diff != 0) { return (diff < 0); } } return true; } /** * Similar to {@link #inLongRange(char[],int,int,boolean)}, but * with String argument * * @param negative Whether original number had a minus sign (which is * NOT passed to this method) or not */ public static boolean inLongRange(String s, boolean negative) { String cmp = negative ? MIN_LONG_STR_NO_SIGN : MAX_LONG_STR; int cmpLen = cmp.length(); int alen = s.length(); if (alen < cmpLen) return true; if (alen > cmpLen) return false; // could perhaps just use String.compareTo()? for (int i = 0; i < cmpLen; ++i) { int diff = s.charAt(i) - cmp.charAt(i); if (diff != 0) { return (diff < 0); } } return true; } public static int parseAsInt(String s, int def) { if (s == null) { return def; } s = s.trim(); int len = s.length(); if (len == 0) { return def; } // One more thing: use integer parsing for 'simple' int i = 0; if (i < len) { // skip leading sign: char c = s.charAt(0); if (c == '+') { // for plus, actually physically remove s = s.substring(1); len = s.length(); } else if (c == '-') { // minus, just skip for checks, must retain ++i; } } for (; i < len; ++i) { char c = s.charAt(i); // if other symbols, parse as Double, coerce if (c > '9' || c < '0') { try { return (int) parseDouble(s); } catch (NumberFormatException e) { return def; } } } try { return Integer.parseInt(s); } catch (NumberFormatException e) { } return def; } public static long parseAsLong(String s, long def) { if (s == null) { return def; } s = s.trim(); int len = s.length(); if (len == 0) { return def; } // One more thing: use long parsing for 'simple' int i = 0; if (i < len) { // skip leading sign: char c = s.charAt(0); if (c == '+') { // for plus, actually physically remove s = s.substring(1); len = s.length(); } else if (c == '-') { // minus, just skip for checks, must retain ++i; } } for (; i < len; ++i) { char c = s.charAt(i); // if other symbols, parse as Double, coerce if (c > '9' || c < '0') { try { return (long) parseDouble(s); } catch (NumberFormatException e) { return def; } } } try { return Long.parseLong(s); } catch (NumberFormatException e) { } return def; } public static double parseAsDouble(String s, double def) { if (s == null) { return def; } s = s.trim(); int len = s.length(); if (len == 0) { return def; } try { return parseDouble(s); } catch (NumberFormatException e) { } return def; } public static double parseDouble(String s) throws NumberFormatException { // [JACKSON-486]: avoid some nasty float representations... but should it be MIN_NORMAL or MIN_VALUE? /* as per [JACKSON-827], let's use MIN_VALUE as it is available on all JDKs; normalized * only in JDK 1.6. In practice, should not really matter. */ if (NASTY_SMALL_DOUBLE.equals(s)) { return Double.MIN_VALUE; } return Double.parseDouble(s); } public static BigDecimal parseBigDecimal(String s) throws NumberFormatException { try { return new BigDecimal(s); } catch (NumberFormatException e) { throw _badBD(s); } } public static BigDecimal parseBigDecimal(char[] b) throws NumberFormatException { return parseBigDecimal(b, 0, b.length); } public static BigDecimal parseBigDecimal(char[] b, int off, int len) throws NumberFormatException { try { return new BigDecimal(b, off, len); } catch (NumberFormatException e) { throw _badBD(new String(b, off, len)); } } private static NumberFormatException _badBD(String s) { return new NumberFormatException("Value \""+s+"\" can not be represented as BigDecimal"); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/NumberOutput.java000066400000000000000000000373241356164247300322730ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; public final class NumberOutput { private static int MILLION = 1000000; private static int BILLION = 1000000000; 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); /** * 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 fullIx = 0; for (int i1 = 0; i1 < 10; ++i1) { for (int i2 = 0; i2 < 10; ++i2) { for (int i3 = 0; i3 < 10; ++i3) { int enc = ((i1 + '0') << 16) | ((i2 + '0') << 8) | (i3 + '0'); TRIPLET_TO_CHARS[fullIx++] = enc; } } } } private final static String[] sSmallIntStrs = new String[] { "0","1","2","3","4","5","6","7","8","9","10" }; private final static String[] sSmallIntStrs2 = new String[] { "-1","-2","-3","-4","-5","-6","-7","-8","-9","-10" }; /* /********************************************************** /* Efficient serialization methods using raw buffers /********************************************************** */ /** * @return Offset within buffer after outputting int */ public static int outputInt(int v, char[] b, int off) { 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 _outputSmallestI(b, off); } b[off++] = '-'; v = -v; } if (v < MILLION) { // at most 2 triplets... if (v < 1000) { if (v < 10) { b[off] = (char) ('0' + v); return off+1; } 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; } // ok, all 3 triplets included /* Let's first hand possible billions separately before * handling 3 triplets. This is possible since we know we * can have at most '2' as billion count. */ if (v >= BILLION) { v -= BILLION; if (v >= BILLION) { v -= BILLION; b[off++] = '2'; } 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)); 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 _outputSmallestI(b, off); } b[off++] = '-'; v = -v; } if (v < MILLION) { // at most 2 triplets... if (v < 1000) { if (v < 10) { b[off++] = (byte) ('0' + v); } else { 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); } return off; } if (v >= BILLION) { v -= BILLION; if (v >= BILLION) { v -= BILLION; b[off++] = '2'; } 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)); off = _leading3(newValue, b, off); off = _full3(thousands, b, off); return _full3(ones, b, off); } /** * @return Offset within buffer after outputting int */ public static int outputLong(long v, char[] b, int off) { // First: does it actually fit in an int? if (v < 0L) { if (v > MIN_INT_AS_LONG) { return outputInt((int) v, b, off); } if (v == Long.MIN_VALUE) { return _outputSmallestL(b, off); } b[off++] = '-'; v = -v; } else { if (v <= MAX_INT_AS_LONG) { return outputInt((int) v, b, off); } } // 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); } return _outputFullBillion((int) v, b, off); } public static int outputLong(long v, byte[] b, int off) { if (v < 0L) { if (v > MIN_INT_AS_LONG) { return outputInt((int) v, b, off); } if (v == Long.MIN_VALUE) { return _outputSmallestL(b, off); } b[off++] = '-'; v = -v; } else { if (v <= MAX_INT_AS_LONG) { return outputInt((int) v, b, off); } } // 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); } return _outputFullBillion((int) v, b, off); } /* /********************************************************** /* 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 if (v < sSmallIntStrs.length) { if (v >= 0) { return sSmallIntStrs[v]; } int v2 = -v - 1; if (v2 < sSmallIntStrs2.length) { return sSmallIntStrs2[v2]; } } return Integer.toString(v); } public static String toString(long v) { if (v <= Integer.MAX_VALUE && v >= Integer.MIN_VALUE) { return toString((int) v); } return Long.toString(v); } public static String toString(double v) { return Double.toString(v); } /** * @since 2.6.0 */ public static String toString(float v) { return Float.toString(v); } /* /********************************************************** /* Other convenience methods /********************************************************** */ /** * Helper method to verify whether given {@code double} value is finite * (regular rational number} or not (NaN or Infinity). * * @return True if number is NOT finite (is Infinity or NaN); false otherwise * * Since 2.10 */ public static boolean notFinite(double value) { // before Java 8 need separate checks return Double.isNaN(value) || Double.isInfinite(value); } /** * Helper method to verify whether given {@code float} value is finite * (regular rational number} or not (NaN or Infinity). * * @return True if number is NOT finite (is Infinity or NaN); false otherwise * * Since 2.10 */ public static boolean notFinite(float value) { // before Java 8 need separate checks return Float.isNaN(value) || Float.isInfinite(value); } /* /********************************************************** /* Internal helper methods /********************************************************** */ private static int _outputUptoBillion(int v, char[] 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++] = (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; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/OutputDecorator.java000066400000000000000000000026721356164247300327630ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.io.*; /** * Handler class that can be used to decorate output destinations. * Typical use is to use a filter abstraction (filtered output stream, * writer) around original output destination, and apply additional * processing during write operations. */ @SuppressWarnings("serial") public abstract class OutputDecorator implements java.io.Serializable // since 2.1 { /** * Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when * creating generator for given {@link OutputStream}, when this decorator * has been registered. * * @param ctxt IO context in use (provides access to declared encoding) * @param out Original output destination * * @return OutputStream to use; either passed in argument, or something that * calls it */ public abstract OutputStream decorate(IOContext ctxt, OutputStream out) throws IOException; /** * Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when * creating generator for given {@link Writer}, when this decorator * has been registered. * * @param ctxt IO context in use (provides access to declared encoding) * @param w Original output writer * * @return Writer to use; either passed in argument, or something that calls it */ public abstract Writer decorate(IOContext ctxt, Writer w) throws IOException; } SegmentedStringWriter.java000066400000000000000000000047321356164247300340370ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/iopackage com.fasterxml.jackson.core.io; import java.io.*; import com.fasterxml.jackson.core.util.BufferRecycler; import com.fasterxml.jackson.core.util.TextBuffer; /** * Efficient alternative to {@link StringWriter}, based on using segmented * internal buffer. Initial input buffer is also recyclable. *

* This class is most useful when serializing JSON content as a String: * if so, instance of this class can be given as the writer to * JsonGenerator. */ public final class SegmentedStringWriter extends Writer { final private TextBuffer _buffer; public SegmentedStringWriter(BufferRecycler br) { super(); _buffer = new TextBuffer(br); } /* /********************************************************** /* java.io.Writer implementation /********************************************************** */ @Override public Writer append(char c) { write(c); return this; } @Override public Writer append(CharSequence csq) { String str = csq.toString(); _buffer.append(str, 0, str.length()); return this; } @Override public Writer append(CharSequence csq, int start, int end) { String str = csq.subSequence(start, end).toString(); _buffer.append(str, 0, str.length()); return this; } @Override public void close() { } // NOP @Override public void flush() { } // NOP @Override public void write(char[] cbuf) { _buffer.append(cbuf, 0, cbuf.length); } @Override public void write(char[] cbuf, int off, int len) { _buffer.append(cbuf, off, len); } @Override public void write(int c) { _buffer.append((char) c); } @Override public void write(String str) { _buffer.append(str, 0, str.length()); } @Override public void write(String str, int off, int len) { _buffer.append(str, off, len); } /* /********************************************************** /* Extended API /********************************************************** */ /** * Main access method that will construct a String that contains * all the contents, release all internal buffers we may have, * and return result String. * Note that the method is not idempotent -- if called second time, * will just return an empty String. */ public String getAndClear() { String result = _buffer.contentsAsString(); _buffer.releaseBuffers(); return result; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/SerializedString.java000066400000000000000000000205541356164247300331010ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.io.*; import java.nio.ByteBuffer; import com.fasterxml.jackson.core.SerializableString; /** * String token that can lazily serialize String contained and then reuse that * serialization later on. This is similar to JDBC prepared statements, for example, * in that instances should only be created when they are used more than use; * prime candidates are various serializers. *

* Class is final for performance reasons and since this is not designed to * be extensible or customizable (customizations would occur in calling code) */ public class SerializedString implements SerializableString, java.io.Serializable { private static final long serialVersionUID = 1L; private static final JsonStringEncoder JSON_ENCODER = JsonStringEncoder.getInstance(); protected final String _value; /* 13-Dec-2010, tatu: Whether use volatile or not is actually an important * decision for multi-core use cases. Cost of volatility can be non-trivial * for heavy use cases, and serialized-string instances are accessed often. * Given that all code paths with common Jackson usage patterns go through * a few memory barriers (mostly with cache/reuse pool access) it seems safe * enough to omit volatiles here, given how simple lazy initialization is. * This can be compared to how {@link String#hashCode} works; lazily and * without synchronization or use of volatile keyword. * * Change to remove volatile was a request by implementors of a high-throughput * search framework; and they believed this is an important optimization for * heaviest, multi-core deployed use cases. */ /* * 22-Sep-2013, tatu: FWIW, there have been no reports of problems in this * area, or anything pointing to it. So I think we are safe up to JDK7 * and hopefully beyond. */ protected /*volatile*/ byte[] _quotedUTF8Ref; protected /*volatile*/ byte[] _unquotedUTF8Ref; protected /*volatile*/ char[] _quotedChars; public SerializedString(String v) { if (v == null) { throw new IllegalStateException("Null String illegal for SerializedString"); } _value = v; } /* /********************************************************** /* Serializable overrides /********************************************************** */ /** * Ugly hack, to work through the requirement that _value is indeed final, * and that JDK serialization won't call ctor(s). * * @since 2.1 */ protected transient String _jdkSerializeValue; private void readObject(ObjectInputStream in) throws IOException { _jdkSerializeValue = in.readUTF(); } private void writeObject(ObjectOutputStream out) throws IOException { out.writeUTF(_value); } protected Object readResolve() { return new SerializedString(_jdkSerializeValue); } /* /********************************************************** /* API /********************************************************** */ @Override public final String getValue() { return _value; } /** * Returns length of the String as characters */ @Override public final int charLength() { return _value.length(); } /** * Accessor for accessing value that has been quoted (escaped) using JSON * quoting rules (using backslash-prefixed codes) into a char array. */ @Override public final char[] asQuotedChars() { char[] result = _quotedChars; if (result == null) { _quotedChars = result = JSON_ENCODER.quoteAsString(_value); } return result; } /** * Accessor for accessing value that has been quoted (escaped) using JSON * quoting rules (using backslash-prefixed codes), and encoded using * UTF-8 encoding into a byte array. */ @Override public final byte[] asQuotedUTF8() { byte[] result = _quotedUTF8Ref; if (result == null) { _quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value); } return result; } /** * Accessor for accessing value as is (without JSON quoting (ecaping)) * encoded as UTF-8 byte array. */ @Override public final byte[] asUnquotedUTF8() { byte[] result = _unquotedUTF8Ref; if (result == null) { _unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value); } return result; } /* /********************************************************** /* Additional 2.0 methods for appending/writing contents /********************************************************** */ @Override public int appendQuoted(char[] buffer, int offset) { char[] result = _quotedChars; if (result == null) { _quotedChars = result = JSON_ENCODER.quoteAsString(_value); } final int length = result.length; if ((offset + length) > buffer.length) { return -1; } System.arraycopy(result, 0, buffer, offset, length); return length; } @Override public int appendQuotedUTF8(byte[] buffer, int offset) { byte[] result = _quotedUTF8Ref; if (result == null) { _quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value); } final int length = result.length; if ((offset + length) > buffer.length) { return -1; } System.arraycopy(result, 0, buffer, offset, length); return length; } @Override public int appendUnquoted(char[] buffer, int offset) { String str = _value; final int length = str.length(); if ((offset + length) > buffer.length) { return -1; } str.getChars(0, length, buffer, offset); return length; } @Override public int appendUnquotedUTF8(byte[] buffer, int offset) { byte[] result = _unquotedUTF8Ref; if (result == null) { _unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value); } final int length = result.length; if ((offset + length) > buffer.length) { return -1; } System.arraycopy(result, 0, buffer, offset, length); return length; } @Override public int writeQuotedUTF8(OutputStream out) throws IOException { byte[] result = _quotedUTF8Ref; if (result == null) { _quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value); } final int length = result.length; out.write(result, 0, length); return length; } @Override public int writeUnquotedUTF8(OutputStream out) throws IOException { byte[] result = _unquotedUTF8Ref; if (result == null) { _unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value); } final int length = result.length; out.write(result, 0, length); return length; } @Override public int putQuotedUTF8(ByteBuffer buffer) { byte[] result = _quotedUTF8Ref; if (result == null) { _quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value); } final int length = result.length; if (length > buffer.remaining()) { return -1; } buffer.put(result, 0, length); return length; } @Override public int putUnquotedUTF8(ByteBuffer buffer) { byte[] result = _unquotedUTF8Ref; if (result == null) { _unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value); } final int length = result.length; if (length > buffer.remaining()) { return -1; } buffer.put(result, 0, length); return length; } /* /********************************************************** /* Standard method overrides /********************************************************** */ @Override public final String toString() { return _value; } @Override public final int hashCode() { return _value.hashCode(); } @Override public final boolean equals(Object o) { if (o == this) return true; if (o == null || o.getClass() != getClass()) return false; SerializedString other = (SerializedString) o; return _value.equals(other._value); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/UTF32Reader.java000066400000000000000000000214411356164247300315410ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.io.*; /** * Since JDK does not come with UTF-32/UCS-4, let's implement a simple * decoder to use. */ public class UTF32Reader extends Reader { /** * JSON actually limits available Unicode range in the high end * to the same as xml (to basically limit UTF-8 max byte sequence * length to 4) */ final protected static int LAST_VALID_UNICODE_CHAR = 0x10FFFF; final protected static char NC = (char) 0; final protected IOContext _context; protected InputStream _in; protected byte[] _buffer; protected int _ptr; protected int _length; protected final boolean _bigEndian; /** * Although input is fine with full Unicode set, Java still uses * 16-bit chars, so we may have to split high-order chars into * surrogate pairs. */ protected char _surrogate = NC; /** * Total read character count; used for error reporting purposes */ protected int _charCount; /** * Total read byte count; used for error reporting purposes */ protected int _byteCount; protected final boolean _managedBuffers; /* /********************************************************** /* Life-cycle /********************************************************** */ public UTF32Reader(IOContext ctxt, InputStream in, byte[] buf, int ptr, int len, boolean isBigEndian) { _context = ctxt; _in = in; _buffer = buf; _ptr = ptr; _length = len; _bigEndian = isBigEndian; _managedBuffers = (in != null); } /* /********************************************************** /* Public API /********************************************************** */ @Override public void close() throws IOException { InputStream in = _in; if (in != null) { _in = null; freeBuffers(); in.close(); } } protected char[] _tmpBuf; /** * Although this method is implemented by the base class, AND it should * never be called by main code, let's still implement it bit more * efficiently just in case */ @Override public int read() throws IOException { if (_tmpBuf == null) { _tmpBuf = new char[1]; } if (read(_tmpBuf, 0, 1) < 1) { return -1; } return _tmpBuf[0]; } @Override public int read(char[] cbuf, int start, int len) throws IOException { // Already EOF? if (_buffer == null) { return -1; } if (len < 1) { return len; } // Let's then ensure there's enough room... if (start < 0 || (start+len) > cbuf.length) { reportBounds(cbuf, start, len); } int outPtr = start; final int outEnd = len+start; // Ok, first; do we have a surrogate from last round? if (_surrogate != NC) { cbuf[outPtr++] = _surrogate; _surrogate = NC; // No need to load more, already got one char } else { // Note: we'll try to avoid blocking as much as possible. As a // result, we only need to get 4 bytes for a full char. int left = (_length - _ptr); if (left < 4) { if (!loadMore(left)) { // (legal) EOF? // Ok if (but only if!) was at boundary if (left == 0) { return -1; } reportUnexpectedEOF(_length - _ptr, 4); } } } // 02-Jun-2017, tatu: Must ensure we don't try to read past buffer end: final int lastValidInputStart = (_length - 4); main_loop: while (outPtr < outEnd) { int ptr = _ptr; int hi, lo; if (_bigEndian) { hi = (_buffer[ptr] << 8) | (_buffer[ptr+1] & 0xFF); lo = ((_buffer[ptr+2] & 0xFF) << 8) | (_buffer[ptr+3] & 0xFF); } else { lo = (_buffer[ptr] & 0xFF) | ((_buffer[ptr+1] & 0xFF) << 8); hi = (_buffer[ptr+2] & 0xFF)| (_buffer[ptr+3] << 8); } _ptr += 4; // Does it need to be split to surrogates? // (also, we can and need to verify illegal chars) if (hi != 0) { // need to split into surrogates? hi &= 0xFFFF; // since it may be sign extended int ch = ((hi - 1) << 16) | lo; // ch -= 0x10000; to normalize starting with 0x0 if (hi > 0x10) { // last valid is 0x10FFFF reportInvalid(ch, outPtr-start, String.format(" (above 0x%08x)", LAST_VALID_UNICODE_CHAR)); } cbuf[outPtr++] = (char) (0xD800 + (ch >> 10)); // hmmh. can this ever be 0? (not legal, at least?) lo = (0xDC00 | (ch & 0x03FF)); // Room for second part? if (outPtr >= outEnd) { // nope _surrogate = (char) ch; break main_loop; } } cbuf[outPtr++] = (char) lo; if (_ptr > lastValidInputStart) { break main_loop; } } int actualLen = (outPtr - start); _charCount += actualLen; return actualLen; } /* /********************************************************** /* Internal methods /********************************************************** */ private void reportUnexpectedEOF(int gotBytes, int needed) throws IOException { int bytePos = _byteCount + gotBytes, charPos = _charCount; throw new CharConversionException("Unexpected EOF in the middle of a 4-byte UTF-32 char: got "+gotBytes+", needed "+needed+", at char #"+charPos+", byte #"+bytePos+")"); } private void reportInvalid(int value, int offset, String msg) throws IOException { int bytePos = _byteCount + _ptr - 1, charPos = _charCount + offset; throw new CharConversionException("Invalid UTF-32 character 0x"+Integer.toHexString(value)+msg+" at char #"+charPos+", byte #"+bytePos+")"); } /** * @param available Number of "unused" bytes in the input buffer * * @return True, if enough bytes were read to allow decoding of at least * one full character; false if EOF was encountered instead. */ private boolean loadMore(int available) throws IOException { _byteCount += (_length - available); // Bytes that need to be moved to the beginning of buffer? if (available > 0) { if (_ptr > 0) { System.arraycopy(_buffer, _ptr, _buffer, 0, available); _ptr = 0; } _length = available; } else { /* Ok; here we can actually reasonably expect an EOF, * so let's do a separate read right away: */ _ptr = 0; int count = (_in == null) ? -1 : _in.read(_buffer); if (count < 1) { _length = 0; if (count < 0) { // -1 if (_managedBuffers) { freeBuffers(); // to help GC? } return false; } // 0 count is no good; let's err out reportStrangeStream(); } _length = count; } /* Need at least 4 bytes; if we don't get that many, it's an * error. */ while (_length < 4) { int count = (_in == null) ? -1 : _in.read(_buffer, _length, _buffer.length - _length); if (count < 1) { if (count < 0) { // -1, EOF... no good! if (_managedBuffers) { freeBuffers(); // to help GC? } reportUnexpectedEOF(_length, 4); } // 0 count is no good; let's err out reportStrangeStream(); } _length += count; } return true; } /** * This method should be called along with (or instead of) normal * close. After calling this method, no further reads should be tried. * Method will try to recycle read buffers (if any). */ private void freeBuffers() { byte[] buf = _buffer; if (buf != null) { _buffer = null; _context.releaseReadIOBuffer(buf); } } private void reportBounds(char[] cbuf, int start, int len) throws IOException { throw new ArrayIndexOutOfBoundsException("read(buf,"+start+","+len+"), cbuf["+cbuf.length+"]"); } private void reportStrangeStream() throws IOException { throw new IOException("Strange I/O stream, returned 0 bytes on read"); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/UTF8Writer.java000066400000000000000000000313521356164247300315400ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.io.*; public final class UTF8Writer extends Writer { final static int SURR1_FIRST = 0xD800; final static int SURR1_LAST = 0xDBFF; final static int SURR2_FIRST = 0xDC00; final static int SURR2_LAST = 0xDFFF; final private IOContext _context; private OutputStream _out; private byte[] _outBuffer; final private int _outBufferEnd; private int _outPtr; /** * When outputting chars from BMP, surrogate pairs need to be coalesced. * To do this, both pairs must be known first; and since it is possible * pairs may be split, we need temporary storage for the first half */ private int _surrogate; public UTF8Writer(IOContext ctxt, OutputStream out) { _context = ctxt; _out = out; _outBuffer = ctxt.allocWriteEncodingBuffer(); /* Max. expansion for a single char (in unmodified UTF-8) is * 4 bytes (or 3 depending on how you view it -- 4 when recombining * surrogate pairs) */ _outBufferEnd = _outBuffer.length - 4; _outPtr = 0; } @Override public Writer append(char c) throws IOException { write(c); return this; } @Override public void close() throws IOException { if (_out != null) { if (_outPtr > 0) { _out.write(_outBuffer, 0, _outPtr); _outPtr = 0; } OutputStream out = _out; _out = null; byte[] buf = _outBuffer; if (buf != null) { _outBuffer = null; _context.releaseWriteEncodingBuffer(buf); } out.close(); /* Let's 'flush' orphan surrogate, no matter what; but only * after cleanly closing everything else. */ int code = _surrogate; _surrogate = 0; if (code > 0) { illegalSurrogate(code); } } } @Override public void flush() throws IOException { if (_out != null) { if (_outPtr > 0) { _out.write(_outBuffer, 0, _outPtr); _outPtr = 0; } _out.flush(); } } @Override public void write(char[] cbuf) throws IOException { write(cbuf, 0, cbuf.length); } @Override public void write(char[] cbuf, int off, int len) throws IOException { if (len < 2) { if (len == 1) { write(cbuf[off]); } return; } // First: do we have a leftover surrogate to deal with? if (_surrogate > 0) { char second = cbuf[off++]; --len; write(convertSurrogate(second)); // will have at least one more char } int outPtr = _outPtr; byte[] outBuf = _outBuffer; int outBufLast = _outBufferEnd; // has 4 'spare' bytes // All right; can just loop it nice and easy now: len += off; // len will now be the end of input buffer output_loop: for (; off < len; ) { /* First, let's ensure we can output at least 4 bytes * (longest UTF-8 encoded codepoint): */ if (outPtr >= outBufLast) { _out.write(outBuf, 0, outPtr); outPtr = 0; } int c = cbuf[off++]; // And then see if we have an Ascii char: if (c < 0x80) { // If so, can do a tight inner loop: outBuf[outPtr++] = (byte)c; // Let's calc how many ascii chars we can copy at most: int maxInCount = (len - off); int maxOutCount = (outBufLast - outPtr); if (maxInCount > maxOutCount) { maxInCount = maxOutCount; } maxInCount += off; ascii_loop: while (true) { if (off >= maxInCount) { // done with max. ascii seq continue output_loop; } c = cbuf[off++]; if (c >= 0x80) { break ascii_loop; } outBuf[outPtr++] = (byte) c; } } // Nope, multi-byte: if (c < 0x800) { // 2-byte outBuf[outPtr++] = (byte) (0xc0 | (c >> 6)); outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f)); } else { // 3 or 4 bytes // Surrogates? if (c < SURR1_FIRST || c > SURR2_LAST) { outBuf[outPtr++] = (byte) (0xe0 | (c >> 12)); outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f)); continue; } // Yup, a surrogate: if (c > SURR1_LAST) { // must be from first range _outPtr = outPtr; illegalSurrogate(c); } _surrogate = c; // and if so, followed by another from next range if (off >= len) { // unless we hit the end? break; } c = convertSurrogate(cbuf[off++]); if (c > 0x10FFFF) { // illegal in JSON as well as in XML _outPtr = outPtr; illegalSurrogate(c); } outBuf[outPtr++] = (byte) (0xf0 | (c >> 18)); outBuf[outPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f)); } } _outPtr = outPtr; } @Override public void write(int c) throws IOException { // First; do we have a left over surrogate? if (_surrogate > 0) { c = convertSurrogate(c); // If not, do we start with a surrogate? } else if (c >= SURR1_FIRST && c <= SURR2_LAST) { // Illegal to get second part without first: if (c > SURR1_LAST) { illegalSurrogate(c); } // First part just needs to be held for now _surrogate = c; return; } if (_outPtr >= _outBufferEnd) { // let's require enough room, first _out.write(_outBuffer, 0, _outPtr); _outPtr = 0; } if (c < 0x80) { // ascii _outBuffer[_outPtr++] = (byte) c; } else { int ptr = _outPtr; if (c < 0x800) { // 2-byte _outBuffer[ptr++] = (byte) (0xc0 | (c >> 6)); _outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f)); } else if (c <= 0xFFFF) { // 3 bytes _outBuffer[ptr++] = (byte) (0xe0 | (c >> 12)); _outBuffer[ptr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); _outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f)); } else { // 4 bytes if (c > 0x10FFFF) { // illegal illegalSurrogate(c); } _outBuffer[ptr++] = (byte) (0xf0 | (c >> 18)); _outBuffer[ptr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); _outBuffer[ptr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); _outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f)); } _outPtr = ptr; } } @Override public void write(String str) throws IOException { write(str, 0, str.length()); } @Override public void write(String str, int off, int len) throws IOException { if (len < 2) { if (len == 1) { write(str.charAt(off)); } return; } // First: do we have a leftover surrogate to deal with? if (_surrogate > 0) { char second = str.charAt(off++); --len; write(convertSurrogate(second)); // will have at least one more char (case of 1 char was checked earlier on) } int outPtr = _outPtr; byte[] outBuf = _outBuffer; int outBufLast = _outBufferEnd; // has 4 'spare' bytes // All right; can just loop it nice and easy now: len += off; // len will now be the end of input buffer output_loop: for (; off < len; ) { /* First, let's ensure we can output at least 4 bytes * (longest UTF-8 encoded codepoint): */ if (outPtr >= outBufLast) { _out.write(outBuf, 0, outPtr); outPtr = 0; } int c = str.charAt(off++); // And then see if we have an Ascii char: if (c < 0x80) { // If so, can do a tight inner loop: outBuf[outPtr++] = (byte)c; // Let's calc how many ascii chars we can copy at most: int maxInCount = (len - off); int maxOutCount = (outBufLast - outPtr); if (maxInCount > maxOutCount) { maxInCount = maxOutCount; } maxInCount += off; ascii_loop: while (true) { if (off >= maxInCount) { // done with max. ascii seq continue output_loop; } c = str.charAt(off++); if (c >= 0x80) { break ascii_loop; } outBuf[outPtr++] = (byte) c; } } // Nope, multi-byte: if (c < 0x800) { // 2-byte outBuf[outPtr++] = (byte) (0xc0 | (c >> 6)); outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f)); } else { // 3 or 4 bytes // Surrogates? if (c < SURR1_FIRST || c > SURR2_LAST) { outBuf[outPtr++] = (byte) (0xe0 | (c >> 12)); outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f)); continue; } // Yup, a surrogate: if (c > SURR1_LAST) { // must be from first range _outPtr = outPtr; illegalSurrogate(c); } _surrogate = c; // and if so, followed by another from next range if (off >= len) { // unless we hit the end? break; } c = convertSurrogate(str.charAt(off++)); if (c > 0x10FFFF) { // illegal, as per RFC 4627 _outPtr = outPtr; illegalSurrogate(c); } outBuf[outPtr++] = (byte) (0xf0 | (c >> 18)); outBuf[outPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f)); } } _outPtr = outPtr; } /* /********************************************************** /* Internal methods /********************************************************** */ /** * Method called to calculate UTF codepoint, from a surrogate pair. */ protected int convertSurrogate(int secondPart) throws IOException { int firstPart = _surrogate; _surrogate = 0; // Ok, then, is the second part valid? if (secondPart < SURR2_FIRST || secondPart > SURR2_LAST) { throw new IOException("Broken surrogate pair: first char 0x"+Integer.toHexString(firstPart)+", second 0x"+Integer.toHexString(secondPart)+"; illegal combination"); } return 0x10000 + ((firstPart - SURR1_FIRST) << 10) + (secondPart - SURR2_FIRST); } protected static void illegalSurrogate(int code) throws IOException { throw new IOException(illegalSurrogateDesc(code)); } protected static String illegalSurrogateDesc(int code) { if (code > 0x10FFFF) { // over max? return "Illegal character point (0x"+Integer.toHexString(code)+") to output; max is 0x10FFFF as per RFC 4627"; } if (code >= SURR1_FIRST) { if (code <= SURR1_LAST) { // Unmatched first part (closing without second part?) return "Unmatched first part of surrogate pair (0x"+Integer.toHexString(code)+")"; } return "Unmatched second part of surrogate pair (0x"+Integer.toHexString(code)+")"; } // should we ever get this? return "Illegal character point (0x"+Integer.toHexString(code)+") to output"; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/000077500000000000000000000000001356164247300273105ustar00rootroot00000000000000ByteSourceJsonBootstrapper.java000066400000000000000000000437301356164247300354260ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.format.InputAccessor; import com.fasterxml.jackson.core.format.MatchStrength; import com.fasterxml.jackson.core.io.*; import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer; import com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer; /** * This class is used to determine the encoding of byte stream * that is to contain JSON content. Rules are fairly simple, and * defined in JSON specification (RFC-4627 or newer), except * for BOM handling, which is a property of underlying * streams. */ public final class ByteSourceJsonBootstrapper { public final static byte UTF8_BOM_1 = (byte) 0xEF; public final static byte UTF8_BOM_2 = (byte) 0xBB; public final static byte UTF8_BOM_3 = (byte) 0xBF; /* /********************************************************** /* Configuration /********************************************************** */ private final IOContext _context; private final InputStream _in; /* /********************************************************** /* Input buffering /********************************************************** */ private final byte[] _inputBuffer; private int _inputPtr; private int _inputEnd; /** * Flag that indicates whether buffer above is to be recycled * after being used or not. */ private final boolean _bufferRecyclable; /* /********************************************************** /* Input location /********************************************************** */ /** * Current number of input units (bytes or chars) that were processed in * previous blocks, * before contents of current input buffer. *

* Note: includes possible BOMs, if those were part of the input. */ // private int _inputProcessed; /* /********************************************************** /* Data gathered /********************************************************** */ /** * Whether input has been detected to be in Big-Endian encoding or not. */ private boolean _bigEndian = true; private int _bytesPerChar; // 0 means "dunno yet" /* /********************************************************** /* Life-cycle /********************************************************** */ public ByteSourceJsonBootstrapper(IOContext ctxt, InputStream in) { _context = ctxt; _in = in; _inputBuffer = ctxt.allocReadIOBuffer(); _inputEnd = _inputPtr = 0; // _inputProcessed = 0; _bufferRecyclable = true; } public ByteSourceJsonBootstrapper(IOContext ctxt, byte[] inputBuffer, int inputStart, int inputLen) { _context = ctxt; _in = null; _inputBuffer = inputBuffer; _inputPtr = inputStart; _inputEnd = (inputStart + inputLen); // Need to offset this for correct location info // _inputProcessed = -inputStart; _bufferRecyclable = false; } /* /********************************************************** /* Encoding detection during bootstrapping /********************************************************** */ /** * Method that should be called after constructing an instace. * It will figure out encoding that content uses, to allow * for instantiating a proper scanner object. */ public JsonEncoding detectEncoding() throws IOException { boolean foundEncoding = false; // First things first: BOM handling /* Note: we can require 4 bytes to be read, since no * combination of BOM + valid JSON content can have * shorter length (shortest valid JSON content is single * digit char, but BOMs are chosen such that combination * is always at least 4 chars long) */ if (ensureLoaded(4)) { int quad = (_inputBuffer[_inputPtr] << 24) | ((_inputBuffer[_inputPtr+1] & 0xFF) << 16) | ((_inputBuffer[_inputPtr+2] & 0xFF) << 8) | (_inputBuffer[_inputPtr+3] & 0xFF); if (handleBOM(quad)) { foundEncoding = true; } else { /* If no BOM, need to auto-detect based on first char; * this works since it must be 7-bit ascii (wrt. unicode * compatible encodings, only ones JSON can be transferred * over) */ // UTF-32? if (checkUTF32(quad)) { foundEncoding = true; } else if (checkUTF16(quad >>> 16)) { foundEncoding = true; } } } else if (ensureLoaded(2)) { int i16 = ((_inputBuffer[_inputPtr] & 0xFF) << 8) | (_inputBuffer[_inputPtr+1] & 0xFF); if (checkUTF16(i16)) { foundEncoding = true; } } JsonEncoding enc; /* Not found yet? As per specs, this means it must be UTF-8. */ if (!foundEncoding) { enc = JsonEncoding.UTF8; } else { switch (_bytesPerChar) { case 1: enc = JsonEncoding.UTF8; break; case 2: enc = _bigEndian ? JsonEncoding.UTF16_BE : JsonEncoding.UTF16_LE; break; case 4: enc = _bigEndian ? JsonEncoding.UTF32_BE : JsonEncoding.UTF32_LE; break; default: throw new RuntimeException("Internal error"); // should never get here } } _context.setEncoding(enc); 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 /********************************************************** */ @SuppressWarnings("resource") public Reader constructReader() throws IOException { JsonEncoding enc = _context.getEncoding(); switch (enc.bits()) { case 8: // only in non-common case where we don't want to do direct mapping case 16: { // First: do we have a Stream? If not, need to create one: InputStream in = _in; if (in == null) { in = new ByteArrayInputStream(_inputBuffer, _inputPtr, _inputEnd); } else { /* Also, if we have any read but unused input (usually true), * need to merge that input in: */ if (_inputPtr < _inputEnd) { in = new MergedStream(_context, in, _inputBuffer, _inputPtr, _inputEnd); } } return new InputStreamReader(in, enc.getJavaName()); } case 32: return new UTF32Reader(_context, _in, _inputBuffer, _inputPtr, _inputEnd, _context.getEncoding().isBigEndian()); } throw new RuntimeException("Internal error"); // should never get here } public JsonParser constructParser(int parserFeatures, ObjectCodec codec, ByteQuadsCanonicalizer rootByteSymbols, CharsToNameCanonicalizer rootCharSymbols, int factoryFeatures) throws IOException { int prevInputPtr = _inputPtr; JsonEncoding enc = detectEncoding(); int bytesProcessed = _inputPtr - prevInputPtr; if (enc == JsonEncoding.UTF8) { /* 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)) { ByteQuadsCanonicalizer can = rootByteSymbols.makeChild(factoryFeatures); return new UTF8StreamJsonParser(_context, parserFeatures, _in, codec, can, _inputBuffer, _inputPtr, _inputEnd, bytesProcessed, _bufferRecyclable); } } return new ReaderBasedJsonParser(_context, parserFeatures, constructReader(), codec, rootCharSymbols.makeChild(factoryFeatures)); } /* /********************************************************** /* Encoding detection for data format auto-detection /********************************************************** */ /** * Current implementation is not as thorough as other functionality * ({@link com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper}); * supports UTF-8, for example. But it should work, for now, and can * be improved as necessary. */ public static MatchStrength hasJSONFormat(InputAccessor acc) throws IOException { // Ideally we should see "[" or "{"; but if not, we'll accept double-quote (String) // in future could also consider accepting non-standard matches? if (!acc.hasMoreBytes()) { return MatchStrength.INCONCLUSIVE; } byte b = acc.nextByte(); // Very first thing, a UTF-8 BOM? if (b == UTF8_BOM_1) { // yes, looks like UTF-8 BOM if (!acc.hasMoreBytes()) { return MatchStrength.INCONCLUSIVE; } if (acc.nextByte() != UTF8_BOM_2) { return MatchStrength.NO_MATCH; } if (!acc.hasMoreBytes()) { return MatchStrength.INCONCLUSIVE; } if (acc.nextByte() != UTF8_BOM_3) { return MatchStrength.NO_MATCH; } if (!acc.hasMoreBytes()) { return MatchStrength.INCONCLUSIVE; } b = acc.nextByte(); } // Then possible leading space int ch = skipSpace(acc, b); if (ch < 0) { return MatchStrength.INCONCLUSIVE; } // First, let's see if it looks like a structured type: if (ch == '{') { // JSON object? // Ideally we need to find either double-quote or closing bracket ch = skipSpace(acc); if (ch < 0) { return MatchStrength.INCONCLUSIVE; } if (ch == '"' || ch == '}') { return MatchStrength.SOLID_MATCH; } // ... should we allow non-standard? Let's not yet... can add if need be return MatchStrength.NO_MATCH; } MatchStrength strength; if (ch == '[') { ch = skipSpace(acc); if (ch < 0) { return MatchStrength.INCONCLUSIVE; } // closing brackets is easy; but for now, let's also accept opening... if (ch == ']' || ch == '[') { return MatchStrength.SOLID_MATCH; } return MatchStrength.SOLID_MATCH; } else { // plain old value is not very convincing... strength = MatchStrength.WEAK_MATCH; } if (ch == '"') { // string value return strength; } if (ch <= '9' && ch >= '0') { // number return strength; } if (ch == '-') { // negative number ch = skipSpace(acc); if (ch < 0) { return MatchStrength.INCONCLUSIVE; } return (ch <= '9' && ch >= '0') ? strength : MatchStrength.NO_MATCH; } // or one of literals if (ch == 'n') { // null return tryMatch(acc, "ull", strength); } if (ch == 't') { // true return tryMatch(acc, "rue", strength); } if (ch == 'f') { // false return tryMatch(acc, "alse", strength); } return MatchStrength.NO_MATCH; } private static MatchStrength tryMatch(InputAccessor acc, String matchStr, MatchStrength fullMatchStrength) throws IOException { for (int i = 0, len = matchStr.length(); i < len; ++i) { if (!acc.hasMoreBytes()) { return MatchStrength.INCONCLUSIVE; } if (acc.nextByte() != matchStr.charAt(i)) { return MatchStrength.NO_MATCH; } } return fullMatchStrength; } private static int skipSpace(InputAccessor acc) throws IOException { if (!acc.hasMoreBytes()) { return -1; } return skipSpace(acc, acc.nextByte()); } private static int skipSpace(InputAccessor acc, byte b) throws IOException { while (true) { int ch = (int) b & 0xFF; if (!(ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t')) { return ch; } if (!acc.hasMoreBytes()) { return -1; } b = acc.nextByte(); } } /* /********************************************************** /* Internal methods, parsing /********************************************************** */ /** * @return True if a BOM was succesfully found, and encoding * thereby recognized. */ private boolean handleBOM(int quad) throws IOException { /* Handling of (usually) optional BOM (required for * multi-byte formats); first 32-bit charsets: */ switch (quad) { case 0x0000FEFF: _bigEndian = true; _inputPtr += 4; _bytesPerChar = 4; return true; case 0xFFFE0000: // UCS-4, LE? _inputPtr += 4; _bytesPerChar = 4; _bigEndian = false; 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; if (msw == 0xFEFF) { // UTF-16, BE _inputPtr += 2; _bytesPerChar = 2; _bigEndian = true; return true; } if (msw == 0xFFFE) { // UTF-16, LE _inputPtr += 2; _bytesPerChar = 2; _bigEndian = false; return true; } // And if not, then UTF-8 BOM? if ((quad >>> 8) == 0xEFBBBF) { // UTF-8 _inputPtr += 3; _bytesPerChar = 1; _bigEndian = true; // doesn't really matter return true; } return false; } private boolean checkUTF32(int quad) throws IOException { /* Handling of (usually) optional BOM (required for * multi-byte formats); first 32-bit charsets: */ if ((quad >> 8) == 0) { // 0x000000?? -> UTF32-BE _bigEndian = true; } else if ((quad & 0x00FFFFFF) == 0) { // 0x??000000 -> UTF32-LE _bigEndian = false; } else if ((quad & ~0x00FF0000) == 0) { // 0x00??0000 -> UTF32-in-order reportWeirdUCS4("3412"); } else if ((quad & ~0x0000FF00) == 0) { // 0x0000??00 -> UTF32-in-order reportWeirdUCS4("2143"); } else { // Can not be valid UTF-32 encoded JSON... return false; } // Not BOM (just regular content), nothing to skip past: //_inputPtr += 4; _bytesPerChar = 4; return true; } private boolean checkUTF16(int i16) { if ((i16 & 0xFF00) == 0) { // UTF-16BE _bigEndian = true; } else if ((i16 & 0x00FF) == 0) { // UTF-16LE _bigEndian = false; } else { // nope, not UTF-16 return false; } // Not BOM (just regular content), nothing to skip past: //_inputPtr += 2; _bytesPerChar = 2; return true; } /* /********************************************************** /* Internal methods, problem reporting /********************************************************** */ private void reportWeirdUCS4(String type) throws IOException { throw new CharConversionException("Unsupported UCS-4 endianness ("+type+") detected"); } /* /********************************************************** /* Internal methods, raw input access /********************************************************** */ protected boolean ensureLoaded(int minimum) throws IOException { /* Let's assume here buffer has enough room -- this will always * be true for the limited used this method gets */ int gotten = (_inputEnd - _inputPtr); while (gotten < minimum) { int count; if (_in == null) { // block source count = -1; } else { count = _in.read(_inputBuffer, _inputEnd, _inputBuffer.length - _inputEnd); } if (count < 1) { return false; } _inputEnd += count; gotten += count; } return true; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/DupDetector.java000066400000000000000000000047441356164247300324060ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import java.util.*; import com.fasterxml.jackson.core.*; /** * Helper class used if * {@link com.fasterxml.jackson.core.JsonParser.Feature#STRICT_DUPLICATE_DETECTION} * is enabled. * Optimized to try to limit memory usage and processing overhead for smallest * entries, but without adding trashing (immutable objects would achieve optimal * memory usage but lead to significant number of discarded temp objects for * scopes with large number of entries). Another consideration is trying to limit * actual number of compiled classes as it contributes significantly to overall * jar size (due to linkage etc). * * @since 2.3 */ public class DupDetector { /** * We need to store a back-reference here to parser/generator. */ protected final Object _source; protected String _firstName; protected String _secondName; /** * Lazily constructed set of names already seen within this context. */ protected HashSet _seen; private DupDetector(Object src) { _source = src; } public static DupDetector rootDetector(JsonParser p) { return new DupDetector(p); } public static DupDetector rootDetector(JsonGenerator g) { return new DupDetector(g); } public DupDetector child() { return new DupDetector(_source); } public void reset() { _firstName = null; _secondName = null; _seen = null; } public JsonLocation findLocation() { // ugly but: if (_source instanceof JsonParser) { return ((JsonParser)_source).getCurrentLocation(); } // do generators have a way to provide Location? Apparently not... return null; } /** * @since 2.7 */ public Object getSource() { return _source; } public boolean isDup(String name) throws JsonParseException { if (_firstName == null) { _firstName = name; return false; } if (name.equals(_firstName)) { return true; } if (_secondName == null) { _secondName = name; return false; } if (name.equals(_secondName)) { return true; } if (_seen == null) { _seen = new HashSet(16); // 16 is default, seems reasonable _seen.add(_firstName); _seen.add(_secondName); } return !_seen.add(name); } } JsonGeneratorImpl.java000066400000000000000000000172211356164247300335010ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.base.GeneratorBase; import com.fasterxml.jackson.core.io.CharTypes; import com.fasterxml.jackson.core.io.CharacterEscapes; import com.fasterxml.jackson.core.io.IOContext; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.core.util.VersionUtil; /** * Intermediate base class shared by JSON-backed generators * like {@link UTF8JsonGenerator} and {@link WriterBasedJsonGenerator}. * * @since 2.1 */ public abstract class JsonGeneratorImpl extends GeneratorBase { /* /********************************************************** /* Constants /********************************************************** */ /** * This is the default set of escape codes, over 7-bit ASCII range * (first 128 character codes), used for single-byte UTF-8 characters. */ protected final static int[] sOutputEscapes = CharTypes.get7BitOutputEscapes(); /* /********************************************************** /* Configuration, basic I/O /********************************************************** */ final protected IOContext _ioContext; /* /********************************************************** /* Configuration, output escaping /********************************************************** */ /** * Currently active set of output escape code definitions (whether * and how to escape or not) for 7-bit ASCII range (first 128 * character codes). Defined separately to make potentially * customizable */ protected int[] _outputEscapes = sOutputEscapes; /** * Value between 128 (0x80) and 65535 (0xFFFF) that indicates highest * Unicode code point that will not need escaping; or 0 to indicate * that all characters can be represented without escaping. * Typically used to force escaping of some portion of character set; * for example to always escape non-ASCII characters (if value was 127). *

* NOTE: not all sub-classes make use of this setting. */ protected int _maximumNonEscapedChar; /** * Definition of custom character escapes to use for generators created * by this factory, if any. If null, standard data format specific * escapes are used. */ protected CharacterEscapes _characterEscapes; /* /********************************************************** /* Configuration, other /********************************************************** */ /** * Separator to use, if any, between root-level values. * * @since 2.1 */ protected SerializableString _rootValueSeparator = DefaultPrettyPrinter.DEFAULT_ROOT_VALUE_SEPARATOR; /** * Flag that is set if quoting is not to be added around * JSON Object property names. * * @since 2.7 */ protected boolean _cfgUnqNames; /* /********************************************************** /* Life-cycle /********************************************************** */ @SuppressWarnings("deprecation") public JsonGeneratorImpl(IOContext ctxt, int features, ObjectCodec codec) { super(features, codec); _ioContext = ctxt; if (Feature.ESCAPE_NON_ASCII.enabledIn(features)) { // inlined `setHighestNonEscapedChar()` _maximumNonEscapedChar = 127; } _cfgUnqNames = !Feature.QUOTE_FIELD_NAMES.enabledIn(features); } /* /********************************************************** /* Versioned /********************************************************** */ @Override public Version version() { return VersionUtil.versionFor(getClass()); } /* /********************************************************** /* Overridden configuration methods /********************************************************** */ @SuppressWarnings("deprecation") @Override public JsonGenerator enable(Feature f) { super.enable(f); if (f == Feature.QUOTE_FIELD_NAMES) { _cfgUnqNames = false; } return this; } @SuppressWarnings("deprecation") @Override public JsonGenerator disable(Feature f) { super.disable(f); if (f == Feature.QUOTE_FIELD_NAMES) { _cfgUnqNames = true; } return this; } @SuppressWarnings("deprecation") @Override protected void _checkStdFeatureChanges(int newFeatureFlags, int changedFeatures) { super._checkStdFeatureChanges(newFeatureFlags, changedFeatures); _cfgUnqNames = !Feature.QUOTE_FIELD_NAMES.enabledIn(newFeatureFlags); } @Override public JsonGenerator setHighestNonEscapedChar(int charCode) { _maximumNonEscapedChar = (charCode < 0) ? 0 : charCode; return this; } @Override public int getHighestEscapedChar() { return _maximumNonEscapedChar; } @Override public JsonGenerator setCharacterEscapes(CharacterEscapes esc) { _characterEscapes = esc; if (esc == null) { // revert to standard escapes _outputEscapes = sOutputEscapes; } else { _outputEscapes = esc.getEscapeCodesForAscii(); } return this; } /** * Method for accessing custom escapes factory uses for {@link JsonGenerator}s * it creates. */ @Override public CharacterEscapes getCharacterEscapes() { return _characterEscapes; } @Override public JsonGenerator setRootValueSeparator(SerializableString sep) { _rootValueSeparator = sep; return this; } /* /********************************************************** /* Partial API /********************************************************** */ // // Overrides just to make things final, to possibly help with inlining @Override public final void writeStringField(String fieldName, String value) throws IOException { 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())); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/JsonReadContext.java000066400000000000000000000141571356164247300332350ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import com.fasterxml.jackson.core.*; /** * Extension of {@link JsonStreamContext}, which implements * core methods needed, and also exposes * more complete API to parser implementation classes. */ public final class JsonReadContext extends JsonStreamContext { // // // Configuration /** * Parent context for this context; null for root context. */ protected final JsonReadContext _parent; // // // Optional duplicate detection protected DupDetector _dups; /* /********************************************************** /* Simple instance reuse slots; speeds up things /* a bit (10-15%) for docs with lots of small /* arrays/objects (for which allocation was /* visible in profile stack frames) /********************************************************** */ protected JsonReadContext _child; /* /********************************************************** /* Location/state information (minus source reference) /********************************************************** */ protected String _currentName; /** * @since 2.5 */ protected Object _currentValue; protected int _lineNr; protected int _columnNr; /* /********************************************************** /* Instance construction, config, reuse /********************************************************** */ public JsonReadContext(JsonReadContext parent, DupDetector dups, int type, int lineNr, int colNr) { super(); _parent = parent; _dups = dups; _type = type; _lineNr = lineNr; _columnNr = colNr; _index = -1; } protected void reset(int type, int lineNr, int colNr) { _type = type; _index = -1; _lineNr = lineNr; _columnNr = colNr; _currentName = null; _currentValue = null; if (_dups != null) { _dups.reset(); } } /* public void trackDups(JsonParser p) { _dups = DupDetector.rootDetector(p); } */ public JsonReadContext withDupDetector(DupDetector dups) { _dups = dups; return this; } @Override public Object getCurrentValue() { return _currentValue; } @Override public void setCurrentValue(Object v) { _currentValue = v; } /* /********************************************************** /* Factory methods /********************************************************** */ public static JsonReadContext createRootContext(int lineNr, int colNr, DupDetector dups) { return new JsonReadContext(null, dups, TYPE_ROOT, lineNr, colNr); } public static JsonReadContext createRootContext(DupDetector dups) { return new JsonReadContext(null, dups, TYPE_ROOT, 1, 0); } public JsonReadContext createChildArrayContext(int lineNr, int colNr) { JsonReadContext ctxt = _child; if (ctxt == null) { _child = ctxt = new JsonReadContext(this, (_dups == null) ? null : _dups.child(), TYPE_ARRAY, lineNr, colNr); } else { ctxt.reset(TYPE_ARRAY, lineNr, colNr); } return ctxt; } public JsonReadContext createChildObjectContext(int lineNr, int colNr) { JsonReadContext ctxt = _child; if (ctxt == null) { _child = ctxt = new JsonReadContext(this, (_dups == null) ? null : _dups.child(), TYPE_OBJECT, lineNr, colNr); return ctxt; } ctxt.reset(TYPE_OBJECT, lineNr, colNr); return ctxt; } /* /********************************************************** /* Abstract method implementations, overrides /********************************************************** */ @Override public String getCurrentName() { return _currentName; } // @since 2.9 @Override public boolean hasCurrentName() { return _currentName != null; } @Override public JsonReadContext getParent() { return _parent; } @Override public JsonLocation getStartLocation(Object srcRef) { // We don't keep track of offsets at this level (only reader does) long totalChars = -1L; return new JsonLocation(srcRef, totalChars, _lineNr, _columnNr); } /* /********************************************************** /* Extended API /********************************************************** */ /** * Method that can be used to both clear the accumulated references * (specifically value set with {@link #setCurrentValue(Object)}) * that should not be retained, and returns parent (as would * {@link #getParent()} do). Typically called when closing the active * context when encountering {@link JsonToken#END_ARRAY} or * {@link JsonToken#END_OBJECT}. * * @since 2.7 */ public JsonReadContext clearAndGetParent() { _currentValue = null; // could also clear the current name, but seems cheap enough to leave? return _parent; } public DupDetector getDupDetector() { return _dups; } /* /********************************************************** /* State changes /********************************************************** */ public boolean expectComma() { /* Assumption here is that we will be getting a value (at least * before calling this method again), and * so will auto-increment index to avoid having to do another call */ int ix = ++_index; // starts from -1 return (_type != TYPE_ROOT && ix > 0); } public void setCurrentName(String name) throws JsonProcessingException { _currentName = name; if (_dups != null) { _checkDup(_dups, name); } } private void _checkDup(DupDetector dd, String name) throws JsonProcessingException { if (dd.isDup(name)) { Object src = dd.getSource(); throw new JsonParseException(((src instanceof JsonParser) ? ((JsonParser) src) : null), "Duplicate field '"+name+"'"); } } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/JsonReadFeature.java000066400000000000000000000210311356164247300331710ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import com.fasterxml.jackson.core.*; /** * Token reader (parser) features specific to JSON backend. * Eventual replacement for JSON-specific {@link com.fasterxml.jackson.core.JsonParser.Feature}s. * * @since 2.10 */ public enum JsonReadFeature implements FormatFeature { // // // Support for non-standard data format constructs: comments /** * Feature that determines whether parser will allow use * of Java/C/C++ style comments (both '/'+'*' and * '//' varieties) within parsed content or not. *

* Since JSON specification does not mention comments as legal * construct, * this is a non-standard feature; however, in the wild * this is extensively used. As such, feature is * disabled by default for parsers and must be * explicitly enabled. */ ALLOW_JAVA_COMMENTS(false, JsonParser.Feature.ALLOW_COMMENTS), /** * Feature that determines whether parser will allow use * of YAML comments, ones starting with '#' and continuing * until the end of the line. This commenting style is common * with scripting languages as well. *

* Since JSON specification does not mention comments as legal * construct, * this is a non-standard feature. As such, feature is * disabled by default for parsers and must be * explicitly enabled. */ ALLOW_YAML_COMMENTS(false, JsonParser.Feature.ALLOW_YAML_COMMENTS), // // // Support for non-standard data format constructs: quoting/escaping /** * Feature that determines whether parser will allow use * of single quotes (apostrophe, character '\'') for * quoting Strings (names and String values). If so, * this is in addition to other acceptable markers. *

* Since JSON specification requires use of double quotes for * field names, * this is a non-standard feature, and as such disabled by default. */ ALLOW_SINGLE_QUOTES(false, JsonParser.Feature.ALLOW_SINGLE_QUOTES), /** * Feature that determines whether parser will allow use * of unquoted field names (which is allowed by Javascript, * but not by JSON specification). *

* Since JSON specification requires use of double quotes for * field names, * this is a non-standard feature, and as such disabled by default. */ ALLOW_UNQUOTED_FIELD_NAMES(false, JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES), /** * Feature that determines whether parser will allow * JSON Strings to contain unescaped control characters * (ASCII characters with value less than 32, including * tab and line feed characters) or not. * If feature is set false, an exception is thrown if such a * character is encountered. *

* Since JSON specification requires quoting for all control characters, * this is a non-standard feature, and as such disabled by default. */ @SuppressWarnings("deprecation") ALLOW_UNESCAPED_CONTROL_CHARS(false, JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS), /** * Feature that can be enabled to accept quoting of all character * using backslash quoting mechanism: if not enabled, only characters * that are explicitly listed by JSON specification can be thus * escaped (see JSON spec for small list of these characters) *

* Since JSON specification requires quoting for all control characters, * this is a non-standard feature, and as such disabled by default. */ @SuppressWarnings("deprecation") ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(false, JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER), // // // Support for non-standard data format constructs: number representations /** * Feature that determines whether parser will allow * JSON integral numbers to start with additional (ignorable) * zeroes (like: 000001). If enabled, no exception is thrown, and extra * nulls are silently ignored (and not included in textual representation * exposed via {@link JsonParser#getText}). *

* Since JSON specification does not allow leading zeroes, * this is a non-standard feature, and as such disabled by default. */ @SuppressWarnings("deprecation") ALLOW_LEADING_ZEROS_FOR_NUMBERS(false, JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS), /** * Feature that allows parser to recognize set of * "Not-a-Number" (NaN) tokens as legal floating number * values (similar to how many other data formats and * programming language source code allows it). * Specific subset contains values that * XML Schema * (see section 3.2.4.1, Lexical Representation) * allows (tokens are quoted contents, not including quotes): *

*

* Since JSON specification does not allow use of such values, * this is a non-standard feature, and as such disabled by default. */ @SuppressWarnings("deprecation") ALLOW_NON_NUMERIC_NUMBERS(false, JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS), // // // Support for non-standard data format constructs: array/value separators /** * 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. *

* For example, enabling this feature will represent a JSON array ["value1",,"value3",] * as ["value1", null, "value3", null] *

* Since the JSON specification does not allow missing values this is a non-compliant JSON * feature and is disabled by default. */ @SuppressWarnings("deprecation") ALLOW_MISSING_VALUES(false, JsonParser.Feature.ALLOW_MISSING_VALUES), /** * Feature that determines whether {@link JsonParser} will allow for a single trailing * comma following the final value (in an Array) or member (in an Object). These commas * will simply be ignored. *

* For example, when this feature is enabled, [true,true,] is equivalent to * [true, true] and {"a": true,} is equivalent to * {"a": true}. *

* When combined with ALLOW_MISSING_VALUES, this feature takes priority, and * the final trailing comma in an array declaration does not imply a missing * (null) value. For example, when both ALLOW_MISSING_VALUES * and ALLOW_TRAILING_COMMA are enabled, [true,true,] is * equivalent to [true, true], and [true,true,,] is equivalent to * [true, true, null]. *

* Since the JSON specification does not permit trailing commas, this is a non-standard * feature, and as such disabled by default. */ @SuppressWarnings("deprecation") ALLOW_TRAILING_COMMA(false, JsonParser.Feature.ALLOW_TRAILING_COMMA), ; final private boolean _defaultState; final private int _mask; /** * For backwards compatibility we may need to map to one of existing {@link JsonParser.Feature}s; * if so, this is the feature to enable/disable. */ final private JsonParser.Feature _mappedFeature; /** * Method that calculates bit set (flags) of all features that * are enabled by default. */ public static int collectDefaults() { int flags = 0; for (JsonReadFeature f : values()) { if (f.enabledByDefault()) { flags |= f.getMask(); } } return flags; } private JsonReadFeature(boolean defaultState, JsonParser.Feature mapTo) { _defaultState = defaultState; _mask = (1 << ordinal()); _mappedFeature = mapTo; } @Override public boolean enabledByDefault() { return _defaultState; } @Override public int getMask() { return _mask; } @Override public boolean enabledIn(int flags) { return (flags & _mask) != 0; } public JsonParser.Feature mappedFeature() { return _mappedFeature; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/JsonWriteContext.java000066400000000000000000000167561356164247300334630ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import com.fasterxml.jackson.core.*; /** * Extension of {@link JsonStreamContext}, which implements * core methods needed, and also exposes * more complete API to generator implementation classes. */ public class JsonWriteContext extends JsonStreamContext { // // // Return values for writeValue() public final static int STATUS_OK_AS_IS = 0; public final static int STATUS_OK_AFTER_COMMA = 1; public final static int STATUS_OK_AFTER_COLON = 2; public final static int STATUS_OK_AFTER_SPACE = 3; // in root context public final static int STATUS_EXPECT_VALUE = 4; public final static int STATUS_EXPECT_NAME = 5; /** * Parent context for this context; null for root context. */ protected final JsonWriteContext _parent; // // // Optional duplicate detection protected DupDetector _dups; /* /********************************************************** /* Simple instance reuse slots; speed up things a bit (10-15%) /* for docs with lots of small arrays/objects /********************************************************** */ protected JsonWriteContext _child; /* /********************************************************** /* Location/state information (minus source reference) /********************************************************** */ /** * Name of the field of which value is to be written; only * used for OBJECT contexts */ protected String _currentName; /** * @since 2.5 */ protected Object _currentValue; /** * Marker used to indicate that we just wrote a name, and * now expect a value to write */ protected boolean _gotName; /* /********************************************************** /* Life-cycle /********************************************************** */ protected JsonWriteContext(int type, JsonWriteContext parent, DupDetector dups) { super(); _type = type; _parent = parent; _dups = dups; _index = -1; } /* @since 2.10 */ protected JsonWriteContext(int type, JsonWriteContext parent, DupDetector dups, Object currValue) { super(); _type = type; _parent = parent; _dups = dups; _index = -1; _currentValue = currValue; } protected JsonWriteContext reset(int type) { _type = type; _index = -1; _currentName = null; _gotName = false; _currentValue = null; if (_dups != null) { _dups.reset(); } return this; } /* @since 2.10 */ protected JsonWriteContext reset(int type, Object currValue) { _type = type; _index = -1; _currentName = null; _gotName = false; _currentValue = currValue; if (_dups != null) { _dups.reset(); } return this; } public JsonWriteContext withDupDetector(DupDetector dups) { _dups = dups; return this; } @Override public Object getCurrentValue() { return _currentValue; } @Override public void setCurrentValue(Object v) { _currentValue = v; } /* /********************************************************** /* Factory methods /********************************************************** */ /** * @deprecated Since 2.3; use method that takes argument */ @Deprecated public static JsonWriteContext createRootContext() { return createRootContext(null); } public static JsonWriteContext createRootContext(DupDetector dd) { return new JsonWriteContext(TYPE_ROOT, null, dd); } public JsonWriteContext createChildArrayContext() { JsonWriteContext ctxt = _child; if (ctxt == null) { _child = ctxt = new JsonWriteContext(TYPE_ARRAY, this, (_dups == null) ? null : _dups.child()); return ctxt; } return ctxt.reset(TYPE_ARRAY); } /* @since 2.10 */ public JsonWriteContext createChildArrayContext(Object currValue) { JsonWriteContext ctxt = _child; if (ctxt == null) { _child = ctxt = new JsonWriteContext(TYPE_ARRAY, this, (_dups == null) ? null : _dups.child(), currValue); return ctxt; } return ctxt.reset(TYPE_ARRAY, currValue); } public JsonWriteContext createChildObjectContext() { JsonWriteContext ctxt = _child; if (ctxt == null) { _child = ctxt = new JsonWriteContext(TYPE_OBJECT, this, (_dups == null) ? null : _dups.child()); return ctxt; } return ctxt.reset(TYPE_OBJECT); } /* @since 2.10 */ public JsonWriteContext createChildObjectContext(Object currValue) { JsonWriteContext ctxt = _child; if (ctxt == null) { _child = ctxt = new JsonWriteContext(TYPE_OBJECT, this, (_dups == null) ? null : _dups.child(), currValue); return ctxt; } return ctxt.reset(TYPE_OBJECT, currValue); } @Override public final JsonWriteContext getParent() { return _parent; } @Override public final String getCurrentName() { return _currentName; } // @since 2.9 @Override public boolean hasCurrentName() { return _currentName != null; } /** * Method that can be used to both clear the accumulated references * (specifically value set with {@link #setCurrentValue(Object)}) * that should not be retained, and returns parent (as would * {@link #getParent()} do). Typically called when closing the active * context when encountering {@link JsonToken#END_ARRAY} or * {@link JsonToken#END_OBJECT}. * * @since 2.7 */ public JsonWriteContext clearAndGetParent() { _currentValue = null; // could also clear the current name, but seems cheap enough to leave? return _parent; } public DupDetector getDupDetector() { return _dups; } /** * Method that writer is to call before it writes a field name. * * @return Index of the field entry (0-based) */ public int writeFieldName(String name) throws JsonProcessingException { if ((_type != TYPE_OBJECT) || _gotName) { return STATUS_EXPECT_VALUE; } _gotName = true; _currentName = name; if (_dups != null) { _checkDup(_dups, name); } return (_index < 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_COMMA; } private final void _checkDup(DupDetector dd, String name) throws JsonProcessingException { if (dd.isDup(name)) { Object src = dd.getSource(); throw new JsonGenerationException("Duplicate field '"+name+"'", ((src instanceof JsonGenerator) ? ((JsonGenerator) src) : null)); } } public int writeValue() { // Most likely, object: if (_type == TYPE_OBJECT) { if (!_gotName) { return STATUS_EXPECT_NAME; } _gotName = false; ++_index; return STATUS_OK_AFTER_COLON; } // Ok, array? if (_type == TYPE_ARRAY) { int ix = _index; ++_index; return (ix < 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_COMMA; } // Nope, root context // No commas within root context, but need space ++_index; return (_index == 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_SPACE; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/JsonWriteFeature.java000066400000000000000000000120771356164247300334220ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import com.fasterxml.jackson.core.*; /** * Token writer features specific to JSON backend. * * @since 2.10 */ public enum JsonWriteFeature implements FormatFeature { // // // Support for non-standard data format constructs: comments // // Quoting/ecsaping-related features /** * Feature that determines whether JSON Object field names are * quoted using double-quotes, as specified by JSON specification * or not. Ability to disable quoting was added to support use * cases where they are not usually expected, which most commonly * occurs when used straight from Javascript. *

* Feature is enabled by default (since it is required by JSON specification). */ @SuppressWarnings("deprecation") QUOTE_FIELD_NAMES(true, JsonGenerator.Feature.QUOTE_FIELD_NAMES), /** * Feature that determines whether "NaN" ("not a number", that is, not * real number) float/double values are output as JSON strings. * The values checked are Double.Nan, * Double.POSITIVE_INFINITY and Double.NEGATIVE_INIFINTY (and * associated Float values). * If feature is disabled, these numbers are still output using * associated literal values, resulting in non-conforming * output. *

* Feature is enabled by default. */ @SuppressWarnings("deprecation") WRITE_NAN_AS_STRINGS(true, JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS), /** * Feature that forces all regular number values to be written as JSON Strings, * instead of as JSON Numbers. * Default state is 'false', meaning that Java numbers are to * be serialized using basic numeric representation but * if enabled all such numeric values are instead written out as * JSON Strings instead. *

* One use case is to avoid problems with Javascript limitations: * since Javascript standard specifies that all number handling * should be done using 64-bit IEEE 754 floating point values, * result being that some 64-bit integer values can not be * accurately represent (as mantissa is only 51 bit wide). *

* Feature is disabled by default. */ @SuppressWarnings("deprecation") WRITE_NUMBERS_AS_STRINGS(false, JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS), /** * Feature that specifies that all characters beyond 7-bit ASCII * range (i.e. code points of 128 and above) need to be output * using format-specific escapes (for JSON, backslash escapes), * if format uses escaping mechanisms (which is generally true * for textual formats but not for binary formats). *

* Feature is disabled by default. */ @SuppressWarnings("deprecation") ESCAPE_NON_ASCII(false, JsonGenerator.Feature.ESCAPE_NON_ASCII), //23-Nov-2015, tatu: for [core#223], if and when it gets implemented /* * Feature that specifies handling of UTF-8 content that contains * characters beyond BMP (Basic Multilingual Plane), which are * represented in UCS-2 (Java internal character encoding) as two * "surrogate" characters. If feature is enabled, these surrogate * pairs are separately escaped using backslash escapes; if disabled, * native output (4-byte UTF-8 sequence, or, with char-backed output * targets, writing of surrogates as is which is typically converted * by {@link java.io.Writer} into 4-byte UTF-8 sequence eventually) * is used. *

* Note that the original JSON specification suggests use of escaping; * but that this is not correct from standard UTF-8 handling perspective. * Because of two competing goals, this feature was added to allow either * behavior to be used, but defaulting to UTF-8 specification compliant * mode. *

* Feature is disabled by default. */ // ESCAPE_UTF8_SURROGATES(false, JsonGenerator.Feature.ESCAPE_UTF8_SURROGATES), ; final private boolean _defaultState; final private int _mask; /** * For backwards compatibility we may need to map to one of existing {@link JsonGenerator.Feature}s; * if so, this is the feature to enable/disable. */ final private JsonGenerator.Feature _mappedFeature; /** * Method that calculates bit set (flags) of all features that * are enabled by default. */ public static int collectDefaults() { int flags = 0; for (JsonWriteFeature f : values()) { if (f.enabledByDefault()) { flags |= f.getMask(); } } return flags; } private JsonWriteFeature(boolean defaultState, JsonGenerator.Feature mapTo) { _defaultState = defaultState; _mask = (1 << ordinal()); _mappedFeature = mapTo; } @Override public boolean enabledByDefault() { return _defaultState; } @Override public int getMask() { return _mask; } @Override public boolean enabledIn(int flags) { return (flags & _mask) != 0; } public JsonGenerator.Feature mappedFeature() { return _mappedFeature; } } PackageVersion.java.in000066400000000000000000000011071356164247300334010ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/jsonpackage @package@; import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.core.Versioned; import com.fasterxml.jackson.core.util.VersionUtil; /** * Automatically generated from PackageVersion.java.in during * packageVersion-generate execution of maven-replacer-plugin in * pom.xml. */ public final class PackageVersion implements Versioned { public final static Version VERSION = VersionUtil.parseVersion( "@projectversion@", "@projectgroupid@", "@projectartifactid@"); @Override public Version version() { return VERSION; } } ReaderBasedJsonParser.java000066400000000000000000003051471356164247300342560ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.*; 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.CharsToNameCanonicalizer; 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.Reader} to handle low-level character * conversion tasks. */ public class ReaderBasedJsonParser // final in 2.3, earlier extends ParserBase { @SuppressWarnings("deprecation") private final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask(); @SuppressWarnings("deprecation") private final static int FEAT_MASK_LEADING_ZEROS = Feature.ALLOW_NUMERIC_LEADING_ZEROS.getMask(); @SuppressWarnings("deprecation") private final static int FEAT_MASK_NON_NUM_NUMBERS = Feature.ALLOW_NON_NUMERIC_NUMBERS.getMask(); @SuppressWarnings("deprecation") private final static int FEAT_MASK_ALLOW_MISSING = Feature.ALLOW_MISSING_VALUES.getMask(); private final static int FEAT_MASK_ALLOW_SINGLE_QUOTES = Feature.ALLOW_SINGLE_QUOTES.getMask(); private final static int FEAT_MASK_ALLOW_UNQUOTED_NAMES = Feature.ALLOW_UNQUOTED_FIELD_NAMES.getMask(); private final static int FEAT_MASK_ALLOW_JAVA_COMMENTS = Feature.ALLOW_COMMENTS.getMask(); private final static int FEAT_MASK_ALLOW_YAML_COMMENTS = Feature.ALLOW_YAML_COMMENTS.getMask(); // Latin1 encoding is not supported, but we do use 8-bit subset for // pre-processing task, to simplify first pass, keep it fast. protected final static int[] _icLatin1 = CharTypes.getInputCodeLatin1(); /* /********************************************************** /* Input configuration /********************************************************** */ /** * Reader that can be used for reading more content, if one * buffer from input source, but in some cases pre-loaded buffer * is handed to the parser. */ protected Reader _reader; /** * Current buffer from which data is read; generally data is read into * buffer from input source. */ protected char[] _inputBuffer; /** * Flag that indicates whether the input buffer is recycable (and * needs to be returned to recycler once we are done) or not. *

* If it is not, it also means that parser can NOT modify underlying * buffer. */ protected boolean _bufferRecyclable; /* /********************************************************** /* Configuration /********************************************************** */ protected ObjectCodec _objectCodec; final protected CharsToNameCanonicalizer _symbols; final protected int _hashSeed; /* /********************************************************** /* Parsing state /********************************************************** */ /** * 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; /** * Value of {@link #_inputPtr} at the time when the first character of * name token was read. Used for calculating token location when requested; * combined with {@link #_currInputProcessed}, may be updated appropriately * as needed. * * @since 2.7 */ protected long _nameStartOffset; /** * @since 2.7 */ protected int _nameStartRow; /** * @since 2.7 */ protected int _nameStartCol; /* /********************************************************** /* Life-cycle /********************************************************** */ /** * Method called when caller wants to provide input buffer directly, * and it may or may not be recyclable use standard recycle context. * * @since 2.4 */ public ReaderBasedJsonParser(IOContext ctxt, int features, Reader r, ObjectCodec codec, CharsToNameCanonicalizer st, char[] inputBuffer, int start, int end, boolean bufferRecyclable) { super(ctxt, features); _reader = r; _inputBuffer = inputBuffer; _inputPtr = start; _inputEnd = end; _objectCodec = codec; _symbols = st; _hashSeed = st.hashSeed(); _bufferRecyclable = bufferRecyclable; } /** * Method called when input comes as a {@link java.io.Reader}, and buffer allocation * can be done using default mechanism. */ public ReaderBasedJsonParser(IOContext ctxt, int features, Reader r, ObjectCodec codec, CharsToNameCanonicalizer st) { super(ctxt, features); _reader = r; _inputBuffer = ctxt.allocTokenBuffer(); _inputPtr = 0; _inputEnd = 0; _objectCodec = codec; _symbols = st; _hashSeed = st.hashSeed(); _bufferRecyclable = true; } /* /********************************************************** /* Base method defs, overrides /********************************************************** */ @Override public ObjectCodec getCodec() { return _objectCodec; } @Override public void setCodec(ObjectCodec c) { _objectCodec = c; } @Override public int releaseBuffered(Writer w) throws IOException { int count = _inputEnd - _inputPtr; if (count < 1) { return 0; } // let's just advance ptr to end int origPtr = _inputPtr; w.write(_inputBuffer, origPtr, count); return count; } @Override public Object getInputSource() { return _reader; } @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, forToken); } } return _inputBuffer[_inputPtr++]; } @Override protected void _closeInput() throws IOException { /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close() * on the underlying Reader, unless we "own" it, or auto-closing * feature is enabled. * One downside is that when using our optimized * Reader (granted, we only do that for UTF-32...) this * means that buffer recycling won't work correctly. */ if (_reader != null) { if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_SOURCE)) { _reader.close(); } _reader = null; } } /** * 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 new symbols, if any _symbols.release(); // and release buffers, if they are recyclable ones if (_bufferRecyclable) { char[] buf = _inputBuffer; if (buf != null) { _inputBuffer = null; _ioContext.releaseTokenBuffer(buf); } } } /* /********************************************************** /* Low-level access, supporting /********************************************************** */ protected void _loadMoreGuaranteed() throws IOException { if (!_loadMore()) { _reportInvalidEOF(); } } protected boolean _loadMore() throws IOException { final int bufSize = _inputEnd; if (_reader != null) { int count = _reader.read(_inputBuffer, 0, _inputBuffer.length); if (count > 0) { _inputPtr = 0; _inputEnd = count; _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; 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 /********************************************************** */ /** * Method for accessing textual representation of the current event; * if no current event (before first call to {@link #nextToken}, or * after encountering end-of-input), returns null. * Method can be called for any event. */ @Override public final String getText() throws IOException { JsonToken t = _currToken; if (t == JsonToken.VALUE_STRING) { if (_tokenIncomplete) { _tokenIncomplete = false; _finishString(); // only strings can be incomplete } return _textBuffer.contentsAsString(); } 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 @Override public final String getValueAsString() throws IOException { if (_currToken == JsonToken.VALUE_STRING) { if (_tokenIncomplete) { _tokenIncomplete = false; _finishString(); // only strings can be incomplete } return _textBuffer.contentsAsString(); } if (_currToken == JsonToken.FIELD_NAME) { return getCurrentName(); } return super.getValueAsString(null); } // @since 2.1 @Override public final String getValueAsString(String defValue) throws IOException { if (_currToken == JsonToken.VALUE_STRING) { if (_tokenIncomplete) { _tokenIncomplete = false; _finishString(); // only strings can be incomplete } return _textBuffer.contentsAsString(); } if (_currToken == JsonToken.FIELD_NAME) { return getCurrentName(); } return super.getValueAsString(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 final 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 final int getTextLength() throws IOException { if (_currToken != null) { // null only before/after document switch (_currToken.id()) { case ID_FIELD_NAME: return _parsingContext.getCurrentName().length(); 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.size(); default: return _currToken.asCharArray().length; } } return 0; } @Override public final 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_EMBEDDED_OBJECT) && (_binaryValue != null)) { return _binaryValue; } if (_currToken != JsonToken.VALUE_STRING) { _reportError("Current token ("+_currToken+") not VALUE_STRING or VALUE_EMBEDDED_OBJECT, can not access as binary"); } // To ensure that we won't see inconsistent data, better clear up state 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 char ch; do { if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++]; } while (ch <= INT_SPACE); int bits = b64variant.decodeBase64Char(ch); if (bits < 0) { // reached the end, fair and square? if (ch == '"') { 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 if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++]; 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 if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++]; bits = b64variant.decodeBase64Char(ch); // First branch: can get padding (-> 1 byte) if (bits < 0) { if (bits != Base64Variant.BASE64_VALUE_PADDING) { // as per [JACKSON-631], could also just be 'missing' padding if (ch == '"') { decodedData >>= 4; buffer[outputPtr++] = (byte) decodedData; if (b64variant.usesPadding()) { --_inputPtr; // to keep parser state bit more consistent _handleBase64MissingPadding(b64variant); } break; } bits = _decodeBase64Escape(b64variant, ch, 2); } if (bits == Base64Variant.BASE64_VALUE_PADDING) { // Ok, must get padding if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++]; if (!b64variant.usesPaddingChar(ch)) { if (_decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) { 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 if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++]; bits = b64variant.decodeBase64Char(ch); if (bits < 0) { if (bits != Base64Variant.BASE64_VALUE_PADDING) { // as per [JACKSON-631], could also just be 'missing' padding if (ch == '"') { decodedData >>= 2; buffer[outputPtr++] = (byte) (decodedData >> 8); buffer[outputPtr++] = (byte) decodedData; if (b64variant.usesPadding()) { --_inputPtr; // to keep parser state bit more consistent _handleBase64MissingPadding(b64variant); } 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 /********************************************************** */ /** * @return Next token from the stream, if any found, or null * to indicate end-of-input */ @Override public final 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 = _skipWSOrEnd(); if (i < 0) { // end-of-input // Should actually close/release things // like input source, symbol table and recyclable buffers now. close(); return (_currToken = null); } // clear any data retained so far _binaryValue = null; // Closing scope? if (i == INT_RBRACKET || i == INT_RCURLY) { _closeScope(i); return _currToken; } // Nope: do we then expect a comma? if (_parsingContext.expectComma()) { i = _skipComma(i); // Was that a trailing comma? if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { if ((i == INT_RBRACKET) || (i == INT_RCURLY)) { _closeScope(i); return _currToken; } } } /* And should we now have a name? Always true for Object contexts, since * the intermediate 'expect-value' state is never retained. */ boolean inObject = _parsingContext.inObject(); if (inObject) { // First, field name itself: _updateNameLocation(); String name = (i == INT_QUOTE) ? _parseName() : _handleOddName(i); _parsingContext.setCurrentName(name); _currToken = JsonToken.FIELD_NAME; i = _skipColon(); } _updateLocation(); // Ok: we must have a value... what is it? JsonToken t; switch (i) { case '"': _tokenIncomplete = true; t = JsonToken.VALUE_STRING; break; case '[': if (!inObject) { _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); } t = JsonToken.START_ARRAY; break; case '{': if (!inObject) { _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); } t = JsonToken.START_OBJECT; break; case '}': // Error: } is not valid at this point; valid closers have // been handled earlier _reportUnexpectedChar(i, "expected a value"); case 't': _matchTrue(); t = JsonToken.VALUE_TRUE; break; case 'f': _matchFalse(); t = JsonToken.VALUE_FALSE; break; case 'n': _matchNull(); t = JsonToken.VALUE_NULL; break; case '-': /* 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. */ 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; default: t = _handleOddValue(i); break; } if (inObject) { _nextToken = t; return _currToken; } _currToken = t; return t; } private final JsonToken _nextAfterName() { _nameCopied = false; // need to invalidate if it was copied JsonToken t = _nextToken; _nextToken = null; // !!! 16-Nov-2015, tatu: TODO: fix [databind#37], copy next location to current here // 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, nextXxx() overrides /********************************************************** */ // Implemented since 2.7 @Override public boolean nextFieldName(SerializableString sstr) throws IOException { // // // Note: most of code below is copied from nextToken() _numTypesValid = NR_UNKNOWN; if (_currToken == JsonToken.FIELD_NAME) { _nextAfterName(); return false; } if (_tokenIncomplete) { _skipString(); } int i = _skipWSOrEnd(); if (i < 0) { close(); _currToken = null; return false; } _binaryValue = null; // Closing scope? if (i == INT_RBRACKET || i == INT_RCURLY) { _closeScope(i); return false; } if (_parsingContext.expectComma()) { i = _skipComma(i); // Was that a trailing comma? if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { if ((i == INT_RBRACKET) || (i == INT_RCURLY)) { _closeScope(i); return false; } } } if (!_parsingContext.inObject()) { _updateLocation(); _nextTokenNotInObject(i); return false; } _updateNameLocation(); if (i == INT_QUOTE) { // when doing literal match, must consider escaping: char[] nameChars = sstr.asQuotedChars(); final int len = nameChars.length; // Require 4 more bytes for faster skipping of colon that follows name if ((_inputPtr + len + 4) < _inputEnd) { // maybe... // first check length match by final int end = _inputPtr+len; if (_inputBuffer[end] == '"') { int offset = 0; int ptr = _inputPtr; while (true) { if (ptr == end) { // yes, match! _parsingContext.setCurrentName(sstr.getValue()); _isNextTokenNameYes(_skipColonFast(ptr+1)); return true; } if (nameChars[offset] != _inputBuffer[ptr]) { break; } ++offset; ++ptr; } } } } return _isNextTokenNameMaybe(i, sstr.getValue()); } @Override public String nextFieldName() throws IOException { // // // Note: this is almost a verbatim copy of nextToken() (minus comments) _numTypesValid = NR_UNKNOWN; if (_currToken == JsonToken.FIELD_NAME) { _nextAfterName(); return null; } if (_tokenIncomplete) { _skipString(); } int i = _skipWSOrEnd(); if (i < 0) { close(); _currToken = null; return null; } _binaryValue = null; if (i == INT_RBRACKET || i == INT_RCURLY) { _closeScope(i); return null; } if (_parsingContext.expectComma()) { i = _skipComma(i); if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { if ((i == INT_RBRACKET) || (i == INT_RCURLY)) { _closeScope(i); return null; } } } if (!_parsingContext.inObject()) { _updateLocation(); _nextTokenNotInObject(i); return null; } _updateNameLocation(); String name = (i == INT_QUOTE) ? _parseName() : _handleOddName(i); _parsingContext.setCurrentName(name); _currToken = JsonToken.FIELD_NAME; i = _skipColon(); _updateLocation(); if (i == INT_QUOTE) { _tokenIncomplete = true; _nextToken = JsonToken.VALUE_STRING; return name; } // Ok: we must have a value... what is it? 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': _matchFalse(); t = JsonToken.VALUE_FALSE; break; case 'n': _matchNull(); t = JsonToken.VALUE_NULL; break; case 't': _matchTrue(); t = JsonToken.VALUE_TRUE; break; case '[': t = JsonToken.START_ARRAY; break; case '{': t = JsonToken.START_OBJECT; break; default: t = _handleOddValue(i); break; } _nextToken = t; return name; } private final void _isNextTokenNameYes(int i) throws IOException { _currToken = JsonToken.FIELD_NAME; _updateLocation(); switch (i) { case '"': _tokenIncomplete = true; _nextToken = JsonToken.VALUE_STRING; return; case '[': _nextToken = JsonToken.START_ARRAY; return; case '{': _nextToken = JsonToken.START_OBJECT; return; case 't': _matchToken("true", 1); _nextToken = JsonToken.VALUE_TRUE; return; case 'f': _matchToken("false", 1); _nextToken = JsonToken.VALUE_FALSE; return; case 'n': _matchToken("null", 1); _nextToken = JsonToken.VALUE_NULL; return; case '-': _nextToken = _parseNegNumber(); return; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': _nextToken = _parsePosNumber(i); return; } _nextToken = _handleOddValue(i); } protected boolean _isNextTokenNameMaybe(int i, String nameToMatch) throws IOException { // // // and this is back to standard nextToken() String name = (i == INT_QUOTE) ? _parseName() : _handleOddName(i); _parsingContext.setCurrentName(name); _currToken = JsonToken.FIELD_NAME; i = _skipColon(); _updateLocation(); if (i == INT_QUOTE) { _tokenIncomplete = true; _nextToken = JsonToken.VALUE_STRING; return nameToMatch.equals(name); } // Ok: we must have a value... what is it? 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': _matchFalse(); t = JsonToken.VALUE_FALSE; break; case 'n': _matchNull(); t = JsonToken.VALUE_NULL; break; case 't': _matchTrue(); t = JsonToken.VALUE_TRUE; break; case '[': t = JsonToken.START_ARRAY; break; case '{': t = JsonToken.START_OBJECT; break; default: t = _handleOddValue(i); break; } _nextToken = t; return nameToMatch.equals(name); } 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)); /* * 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 ((_features & FEAT_MASK_ALLOW_MISSING) != 0) { --_inputPtr; return (_currToken = JsonToken.VALUE_NULL); } } return (_currToken = _handleOddValue(i)); } // note: identical to one in UTF8StreamJsonParser @Override public final String nextTextValue() throws IOException { 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; _finishString(); } 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; } // !!! TODO: optimize this case as well return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null; } // note: identical to one in Utf8StreamParser @Override public final int nextIntValue(int defaultValue) throws IOException { if (_currToken == JsonToken.FIELD_NAME) { _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; } // !!! TODO: optimize this case as well return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue; } // note: identical to one in Utf8StreamParser @Override public final long nextLongValue(long defaultValue) throws IOException { 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; } // !!! TODO: optimize this case as well return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue; } // note: identical to one in UTF8StreamJsonParser @Override public final Boolean nextBooleanValue() throws IOException { 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 != null) { int id = t.id(); if (id == ID_TRUE) return Boolean.TRUE; if (id == ID_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. *

* 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 final JsonToken _parsePosNumber(int ch) throws IOException { /* Although we will always be complete with respect to textual * representation (that is, all characters will be parsed), * actual conversion to a number is deferred. Thus, need to * note that no representations are valid yet */ int ptr = _inputPtr; int startPtr = ptr-1; // to include digit already read final int inputLen = _inputEnd; // One special case, leading zero(es): if (ch == INT_0) { return _parseNumber2(false, startPtr); } /* First, let's see if the whole number is contained within * the input buffer unsplit. This should be the common case; * and to simplify processing, we will just reparse contents * in the alternative case (number split on buffer boundary) */ int intLen = 1; // already got one // First let's get the obligatory integer part: int_loop: while (true) { if (ptr >= inputLen) { _inputPtr = startPtr; return _parseNumber2(false, startPtr); } ch = (int) _inputBuffer[ptr++]; if (ch < INT_0 || ch > INT_9) { break int_loop; } ++intLen; } if (ch == INT_PERIOD || ch == INT_e || ch == INT_E) { _inputPtr = ptr; return _parseFloat(ch, startPtr, ptr, false, intLen); } // Got it all: let's add to text buffer for parsing, access --ptr; // need to push back following separator _inputPtr = ptr; // As per #105, need separating space between root values; check here if (_parsingContext.inRoot()) { _verifyRootSpace(ch); } int len = ptr-startPtr; _textBuffer.resetWithShared(_inputBuffer, startPtr, len); return resetInt(false, intLen); } private final JsonToken _parseFloat(int ch, int startPtr, int ptr, boolean neg, int intLen) throws IOException { final int inputLen = _inputEnd; int fractLen = 0; // And then see if we get other parts if (ch == '.') { // yes, fraction fract_loop: while (true) { if (ptr >= inputLen) { return _parseNumber2(neg, startPtr); } ch = (int) _inputBuffer[ptr++]; if (ch < INT_0 || ch > INT_9) { break fract_loop; } ++fractLen; } // must be followed by sequence of ints, one minimum if (fractLen == 0) { reportUnexpectedNumberChar(ch, "Decimal point not followed by a digit"); } } int expLen = 0; if (ch == 'e' || ch == 'E') { // and/or exponent if (ptr >= inputLen) { _inputPtr = startPtr; return _parseNumber2(neg, startPtr); } // Sign indicator? ch = (int) _inputBuffer[ptr++]; if (ch == INT_MINUS || ch == INT_PLUS) { // yup, skip for now if (ptr >= inputLen) { _inputPtr = startPtr; return _parseNumber2(neg, startPtr); } ch = (int) _inputBuffer[ptr++]; } while (ch <= INT_9 && ch >= INT_0) { ++expLen; if (ptr >= inputLen) { _inputPtr = startPtr; return _parseNumber2(neg, startPtr); } ch = (int) _inputBuffer[ptr++]; } // must be followed by sequence of ints, one minimum if (expLen == 0) { reportUnexpectedNumberChar(ch, "Exponent indicator not followed by a digit"); } } --ptr; // need to push back following separator _inputPtr = ptr; // As per #105, need separating space between root values; check here if (_parsingContext.inRoot()) { _verifyRootSpace(ch); } int len = ptr-startPtr; _textBuffer.resetWithShared(_inputBuffer, startPtr, len); // And there we have it! return resetFloat(neg, intLen, fractLen, expLen); } protected final JsonToken _parseNegNumber() throws IOException { int ptr = _inputPtr; int startPtr = ptr-1; // to include sign/digit already read final int inputLen = _inputEnd; if (ptr >= inputLen) { return _parseNumber2(true, startPtr); } int ch = _inputBuffer[ptr++]; // First check: must have a digit to follow minus sign if (ch > INT_9 || ch < INT_0) { _inputPtr = ptr; return _handleInvalidNumberStart(ch, true); } // One special case, leading zero(es): if (ch == INT_0) { return _parseNumber2(true, startPtr); } int intLen = 1; // already got one // First let's get the obligatory integer part: int_loop: while (true) { if (ptr >= inputLen) { return _parseNumber2(true, startPtr); } ch = (int) _inputBuffer[ptr++]; if (ch < INT_0 || ch > INT_9) { break int_loop; } ++intLen; } if (ch == INT_PERIOD || ch == INT_e || ch == INT_E) { _inputPtr = ptr; return _parseFloat(ch, startPtr, ptr, true, intLen); } --ptr; _inputPtr = ptr; if (_parsingContext.inRoot()) { _verifyRootSpace(ch); } int len = ptr-startPtr; _textBuffer.resetWithShared(_inputBuffer, startPtr, len); return resetInt(true, intLen); } /** * Method called to parse a number, when the primary parse * method has failed to parse it, due to it being split on * buffer boundary. As a result code is very similar, except * that it has to explicitly copy contents to the text buffer * instead of just sharing the main input buffer. */ private final JsonToken _parseNumber2(boolean neg, int startPtr) throws IOException { _inputPtr = neg ? (startPtr+1) : startPtr; char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); int outPtr = 0; // Need to prepend sign? if (neg) { outBuf[outPtr++] = '-'; } // 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", JsonToken.VALUE_NUMBER_INT); if (c == '0') { c = _verifyNoLeadingZeroes(); } boolean eof = false; // Ok, first the obligatory integer part: int_loop: while (c >= '0' && c <= '9') { ++intLen; if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } outBuf[outPtr++] = c; if (_inputPtr >= _inputEnd && !_loadMore()) { // EOF is legal for main level int values c = CHAR_NULL; eof = true; break int_loop; } c = _inputBuffer[_inputPtr++]; } // Also, integer part is not optional if (intLen == 0) { return _handleInvalidNumberStart(c, neg); } int fractLen = 0; // And then see if we get other parts if (c == '.') { // yes, fraction if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } outBuf[outPtr++] = c; fract_loop: while (true) { if (_inputPtr >= _inputEnd && !_loadMore()) { eof = true; break fract_loop; } c = _inputBuffer[_inputPtr++]; if (c < INT_0 || c > INT_9) { break fract_loop; } ++fractLen; if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } outBuf[outPtr++] = 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 == 'e' || c == 'E') { // exponent? if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } outBuf[outPtr++] = c; // Not optional, can require that we get one more char c = (_inputPtr < _inputEnd) ? _inputBuffer[_inputPtr++] : getNextChar("expected a digit for number exponent"); // Sign indicator? if (c == '-' || c == '+') { if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } outBuf[outPtr++] = c; // Likewise, non optional: c = (_inputPtr < _inputEnd) ? _inputBuffer[_inputPtr++] : getNextChar("expected a digit for number exponent"); } exp_loop: while (c <= INT_9 && c >= INT_0) { ++expLen; if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } outBuf[outPtr++] = c; if (_inputPtr >= _inputEnd && !_loadMore()) { eof = true; break exp_loop; } c = _inputBuffer[_inputPtr++]; } // 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 if (!eof) { --_inputPtr; if (_parsingContext.inRoot()) { _verifyRootSpace(c); } } _textBuffer.setCurrentLength(outPtr); // And there we have it! return reset(neg, intLen, fractLen, expLen); } /** * Method called when we have seen one zero, and want to ensure * it is not followed by another */ private final char _verifyNoLeadingZeroes() throws IOException { // Fast case first: if (_inputPtr < _inputEnd) { char ch = _inputBuffer[_inputPtr]; // if not followed by a number (probably '.'); return zero as is, to be included if (ch < '0' || ch > '9') { return '0'; } } // and offline the less common case return _verifyNLZ2(); } private char _verifyNLZ2() throws IOException { if (_inputPtr >= _inputEnd && !_loadMore()) { return '0'; } char ch = _inputBuffer[_inputPtr]; if (ch < '0' || ch > '9') { return '0'; } if ((_features & FEAT_MASK_LEADING_ZEROS) == 0) { 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) ++_inputPtr; // Leading zero to be skipped if (ch == INT_0) { while (_inputPtr < _inputEnd || _loadMore()) { ch = _inputBuffer[_inputPtr]; if (ch < '0' || ch > '9') { // followed by non-number; retain one zero return '0'; } ++_inputPtr; // skip previous zero if (ch != '0') { // followed by other number; return break; } } } return ch; } /** * Method called if expected numeric value (due to leading sign) does not * look like a number */ protected JsonToken _handleInvalidNumberStart(int ch, boolean negative) throws IOException { if (ch == 'I') { if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOFInValue(JsonToken.VALUE_NUMBER_INT); } } ch = _inputBuffer[_inputPtr++]; if (ch == 'N') { String match = negative ? "-INF" :"+INF"; _matchToken(match, 3); if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { return resetAsNaN(match, negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY); } _reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); } else if (ch == 'n') { String match = negative ? "-Infinity" :"+Infinity"; _matchToken(match, 3); if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { return resetAsNaN(match, negative ? 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; } /** * Method called to ensure that a root-value is followed by a space * token. *

* NOTE: caller MUST ensure there is at least one character available; * and that input pointer is AT given char (not past) */ private final void _verifyRootSpace(int ch) throws IOException { // caller had pushed it back, before calling; reset ++_inputPtr; switch (ch) { case ' ': case '\t': return; case '\r': _skipCR(); return; case '\n': ++_currInputRow; _currInputRowStart = _inputPtr; return; } _reportMissingRootWS(ch); } /* /********************************************************** /* Internal methods, secondary parsing /********************************************************** */ protected final String _parseName() throws IOException { // First: let's try to see if we have a simple name: one that does // not cross input buffer boundary, and does not contain escape sequences. int ptr = _inputPtr; int hash = _hashSeed; final int[] codes = _icLatin1; while (ptr < _inputEnd) { int ch = _inputBuffer[ptr]; if (ch < codes.length && codes[ch] != 0) { if (ch == '"') { int start = _inputPtr; _inputPtr = ptr+1; // to skip the quote return _symbols.findSymbol(_inputBuffer, start, ptr - start, hash); } break; } hash = (hash * CharsToNameCanonicalizer.HASH_MULT) + ch; ++ptr; } int start = _inputPtr; _inputPtr = ptr; return _parseName2(start, hash, INT_QUOTE); } private String _parseName2(int startPtr, int hash, int endChar) throws IOException { _textBuffer.resetWithShared(_inputBuffer, startPtr, (_inputPtr - startPtr)); /* Output pointers; calls will also ensure that the buffer is * not shared and has room for at least one more char. */ char[] outBuf = _textBuffer.getCurrentSegment(); int outPtr = _textBuffer.getCurrentSegmentSize(); while (true) { if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOF(" in field name", JsonToken.FIELD_NAME); } } char c = _inputBuffer[_inputPtr++]; int i = (int) c; if (i <= INT_BACKSLASH) { if (i == INT_BACKSLASH) { /* Although chars outside of BMP are to be escaped as * an UTF-16 surrogate pair, does that affect decoding? * For now let's assume it does not. */ c = _decodeEscaped(); } else if (i <= endChar) { if (i == endChar) { break; } if (i < INT_SPACE) { _throwUnquotedSpace(i, "name"); } } } hash = (hash * CharsToNameCanonicalizer.HASH_MULT) + c; // Ok, let's add char to output: outBuf[outPtr++] = c; // Need more room? if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } } _textBuffer.setCurrentLength(outPtr); { TextBuffer tb = _textBuffer; char[] buf = tb.getTextBuffer(); int start = tb.getTextOffset(); int len = tb.size(); return _symbols.findSymbol(buf, start, len, hash); } } /** * 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 expection; but * in non-standard modes may be able to parse name. */ protected String _handleOddName(int i) throws IOException { // [JACKSON-173]: allow single quotes if (i == '\'' && (_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { return _parseAposName(); } // [JACKSON-69]: allow unquoted names if feature enabled: if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) { _reportUnexpectedChar(i, "was expecting double-quote to start field name"); } final int[] codes = CharTypes.getInputCodeLatin1JsNames(); final int maxCode = codes.length; // Also: first char must be a valid name char, but NOT be number boolean firstOk; if (i < maxCode) { // identifier, or a number ([Issue#102]) firstOk = (codes[i] == 0); } else { firstOk = Character.isJavaIdentifierPart((char) i); } if (!firstOk) { _reportUnexpectedChar(i, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name"); } int ptr = _inputPtr; int hash = _hashSeed; final int inputLen = _inputEnd; if (ptr < inputLen) { do { int ch = _inputBuffer[ptr]; if (ch < maxCode) { if (codes[ch] != 0) { int start = _inputPtr-1; // -1 to bring back first char _inputPtr = ptr; return _symbols.findSymbol(_inputBuffer, start, ptr - start, hash); } } else if (!Character.isJavaIdentifierPart((char) ch)) { int start = _inputPtr-1; // -1 to bring back first char _inputPtr = ptr; return _symbols.findSymbol(_inputBuffer, start, ptr - start, hash); } hash = (hash * CharsToNameCanonicalizer.HASH_MULT) + ch; ++ptr; } while (ptr < inputLen); } int start = _inputPtr-1; _inputPtr = ptr; return _handleOddName2(start, hash, codes); } protected String _parseAposName() throws IOException { // Note: mostly copy of_parseFieldName int ptr = _inputPtr; int hash = _hashSeed; final int inputLen = _inputEnd; if (ptr < inputLen) { final int[] codes = _icLatin1; final int maxCode = codes.length; do { int ch = _inputBuffer[ptr]; if (ch == '\'') { int start = _inputPtr; _inputPtr = ptr+1; // to skip the quote return _symbols.findSymbol(_inputBuffer, start, ptr - start, hash); } if (ch < maxCode && codes[ch] != 0) { break; } hash = (hash * CharsToNameCanonicalizer.HASH_MULT) + ch; ++ptr; } while (ptr < inputLen); } int start = _inputPtr; _inputPtr = ptr; return _parseName2(start, hash, '\''); } /** * Method for handling cases where first non-space character * of an expected value token is not legal for standard JSON content. */ protected JsonToken _handleOddValue(int i) throws IOException { // Most likely an error, unless we are to allow single-quote-strings switch (i) { case '\'': /* 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 */ if ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { 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 ((_features & FEAT_MASK_ALLOW_MISSING) != 0) { --_inputPtr; return JsonToken.VALUE_NULL; } break; case 'N': _matchToken("NaN", 1); if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { 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 ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { 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 if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOFInValue(JsonToken.VALUE_NUMBER_INT); } } return _handleInvalidNumberStart(_inputBuffer[_inputPtr++], false); } // [core#77] Try to decode most likely token if (Character.isJavaIdentifierStart(i)) { _reportInvalidToken(""+((char) i), _validJsonTokenList()); } // but if it doesn't look like a token: _reportUnexpectedChar(i, "expected a valid value "+_validJsonValueList()); return null; } protected JsonToken _handleApos() throws IOException { char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); int outPtr = _textBuffer.getCurrentSegmentSize(); while (true) { if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOF(": was expecting closing quote for a string value", JsonToken.VALUE_STRING); } } char c = _inputBuffer[_inputPtr++]; int i = (int) c; if (i <= '\\') { if (i == '\\') { /* Although chars outside of BMP are to be escaped as * an UTF-16 surrogate pair, does that affect decoding? * For now let's assume it does not. */ c = _decodeEscaped(); } else if (i <= '\'') { if (i == '\'') { break; } if (i < INT_SPACE) { _throwUnquotedSpace(i, "string value"); } } } // Need more room? if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } // Ok, let's add char to output: outBuf[outPtr++] = c; } _textBuffer.setCurrentLength(outPtr); return JsonToken.VALUE_STRING; } private String _handleOddName2(int startPtr, int hash, int[] codes) throws IOException { _textBuffer.resetWithShared(_inputBuffer, startPtr, (_inputPtr - startPtr)); char[] outBuf = _textBuffer.getCurrentSegment(); int outPtr = _textBuffer.getCurrentSegmentSize(); final int maxCode = codes.length; while (true) { if (_inputPtr >= _inputEnd) { if (!_loadMore()) { // acceptable for now (will error out later) break; } } char c = _inputBuffer[_inputPtr]; int i = (int) c; if (i < maxCode) { if (codes[i] != 0) { break; } } else if (!Character.isJavaIdentifierPart(c)) { break; } ++_inputPtr; hash = (hash * CharsToNameCanonicalizer.HASH_MULT) + i; // Ok, let's add char to output: outBuf[outPtr++] = c; // Need more room? if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } } _textBuffer.setCurrentLength(outPtr); { TextBuffer tb = _textBuffer; char[] buf = tb.getTextBuffer(); int start = tb.getTextOffset(); int len = tb.size(); return _symbols.findSymbol(buf, start, len, hash); } } @Override protected final void _finishString() throws IOException { /* First: let's try to see if we have simple String value: one * that does not cross input buffer boundary, and does not * contain escape sequences. */ int ptr = _inputPtr; final int inputLen = _inputEnd; if (ptr < inputLen) { final int[] codes = _icLatin1; final int maxCode = codes.length; do { int ch = _inputBuffer[ptr]; if (ch < maxCode && codes[ch] != 0) { if (ch == '"') { _textBuffer.resetWithShared(_inputBuffer, _inputPtr, (ptr-_inputPtr)); _inputPtr = ptr+1; // Yes, we got it all return; } break; } ++ptr; } while (ptr < inputLen); } // Either ran out of input, or bumped into an escape sequence... _textBuffer.resetWithCopy(_inputBuffer, _inputPtr, (ptr-_inputPtr)); _inputPtr = ptr; _finishString2(); } protected void _finishString2() throws IOException { char[] outBuf = _textBuffer.getCurrentSegment(); int outPtr = _textBuffer.getCurrentSegmentSize(); final int[] codes = _icLatin1; final int maxCode = codes.length; while (true) { if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOF(": was expecting closing quote for a string value", JsonToken.VALUE_STRING); } } char c = _inputBuffer[_inputPtr++]; int i = (int) c; if (i < maxCode && codes[i] != 0) { if (i == INT_QUOTE) { break; } else if (i == INT_BACKSLASH) { /* Although chars outside of BMP are to be escaped as * an UTF-16 surrogate pair, does that affect decoding? * For now let's assume it does not. */ c = _decodeEscaped(); } else if (i < INT_SPACE) { _throwUnquotedSpace(i, "string value"); } // anything else? } // Need more room? if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } // Ok, let's add char to output: outBuf[outPtr++] = 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 final void _skipString() throws IOException { _tokenIncomplete = false; int inPtr = _inputPtr; int inLen = _inputEnd; char[] inBuf = _inputBuffer; while (true) { if (inPtr >= inLen) { _inputPtr = inPtr; if (!_loadMore()) { _reportInvalidEOF(": was expecting closing quote for a string value", JsonToken.VALUE_STRING); } inPtr = _inputPtr; inLen = _inputEnd; } char c = inBuf[inPtr++]; int i = (int) c; if (i <= INT_BACKSLASH) { if (i == INT_BACKSLASH) { // Although chars outside of BMP are to be escaped as an UTF-16 surrogate pair, // does that affect decoding? For now let's assume it does not. _inputPtr = inPtr; /*c = */ _decodeEscaped(); inPtr = _inputPtr; inLen = _inputEnd; } else if (i <= INT_QUOTE) { if (i == INT_QUOTE) { _inputPtr = inPtr; break; } if (i < INT_SPACE) { _inputPtr = inPtr; _throwUnquotedSpace(i, "string value"); } } } } } /* /********************************************************** /* Internal methods, other parsing /********************************************************** */ /** * We actually need to check the character value here * (to see if we have \n following \r). */ protected final void _skipCR() throws IOException { if (_inputPtr < _inputEnd || _loadMore()) { if (_inputBuffer[_inputPtr] == '\n') { ++_inputPtr; } } ++_currInputRow; _currInputRowStart = _inputPtr; } private final int _skipColon() throws IOException { if ((_inputPtr + 4) >= _inputEnd) { return _skipColon2(false); } char c = _inputBuffer[_inputPtr]; if (c == ':') { // common case, no leading space int i = _inputBuffer[++_inputPtr]; if (i > INT_SPACE) { // nor trailing if (i == INT_SLASH || i == INT_HASH) { return _skipColon2(true); } ++_inputPtr; return i; } if (i == INT_SPACE || i == INT_TAB) { i = (int) _inputBuffer[++_inputPtr]; if (i > INT_SPACE) { if (i == INT_SLASH || i == INT_HASH) { return _skipColon2(true); } ++_inputPtr; return i; } } return _skipColon2(true); // true -> skipped colon } if (c == ' ' || c == '\t') { c = _inputBuffer[++_inputPtr]; } if (c == ':') { int i = _inputBuffer[++_inputPtr]; if (i > INT_SPACE) { if (i == INT_SLASH || i == INT_HASH) { return _skipColon2(true); } ++_inputPtr; return i; } if (i == INT_SPACE || i == INT_TAB) { i = (int) _inputBuffer[++_inputPtr]; if (i > INT_SPACE) { if (i == INT_SLASH || i == INT_HASH) { return _skipColon2(true); } ++_inputPtr; return i; } } return _skipColon2(true); } return _skipColon2(false); } private final int _skipColon2(boolean gotColon) throws IOException { while (_inputPtr < _inputEnd || _loadMore()) { int i = (int) _inputBuffer[_inputPtr++]; 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; continue; } if (i < INT_SPACE) { if (i == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } _reportInvalidEOF(" within/between "+_parsingContext.typeDesc()+" entries", null); return -1; } // Variant called when we know there's at least 4 more bytes available private final int _skipColonFast(int ptr) throws IOException { int i = (int) _inputBuffer[ptr++]; if (i == INT_COLON) { // common case, no leading space i = _inputBuffer[ptr++]; if (i > INT_SPACE) { // nor trailing if (i != INT_SLASH && i != INT_HASH) { _inputPtr = ptr; return i; } } else if (i == INT_SPACE || i == INT_TAB) { i = (int) _inputBuffer[ptr++]; if (i > INT_SPACE) { if (i != INT_SLASH && i != INT_HASH) { _inputPtr = ptr; return i; } } } _inputPtr = ptr-1; return _skipColon2(true); // true -> skipped colon } if (i == INT_SPACE || i == INT_TAB) { i = _inputBuffer[ptr++]; } boolean gotColon = (i == INT_COLON); if (gotColon) { i = _inputBuffer[ptr++]; if (i > INT_SPACE) { if (i != INT_SLASH && i != INT_HASH) { _inputPtr = ptr; return i; } } else if (i == INT_SPACE || i == INT_TAB) { i = (int) _inputBuffer[ptr++]; if (i > INT_SPACE) { if (i != INT_SLASH && i != INT_HASH) { _inputPtr = ptr; return i; } } } } _inputPtr = ptr-1; return _skipColon2(gotColon); } // Primary loop: no reloading, comment handling private final int _skipComma(int i) throws IOException { if (i != INT_COMMA) { _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries"); } while (_inputPtr < _inputEnd) { i = (int) _inputBuffer[_inputPtr++]; if (i > INT_SPACE) { if (i == INT_SLASH || i == INT_HASH) { --_inputPtr; return _skipAfterComma2(); } return i; } if (i < INT_SPACE) { if (i == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } return _skipAfterComma2(); } private final int _skipAfterComma2() throws IOException { while (_inputPtr < _inputEnd || _loadMore()) { int i = (int) _inputBuffer[_inputPtr++]; if (i > INT_SPACE) { if (i == INT_SLASH) { _skipComment(); continue; } if (i == INT_HASH) { if (_skipYAMLComment()) { continue; } } return i; } if (i < INT_SPACE) { if (i == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } throw _constructError("Unexpected end-of-input within/between "+_parsingContext.typeDesc()+" entries"); } private final int _skipWSOrEnd() throws IOException { // 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()) { return _eofAsNextChar(); } } int i = _inputBuffer[_inputPtr++]; if (i > INT_SPACE) { if (i == INT_SLASH || i == INT_HASH) { --_inputPtr; return _skipWSOrEnd2(); } return i; } if (i != INT_SPACE) { if (i == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } while (_inputPtr < _inputEnd) { i = (int) _inputBuffer[_inputPtr++]; if (i > INT_SPACE) { if (i == INT_SLASH || i == INT_HASH) { --_inputPtr; return _skipWSOrEnd2(); } return i; } if (i != INT_SPACE) { if (i == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } return _skipWSOrEnd2(); } private int _skipWSOrEnd2() throws IOException { while (true) { if (_inputPtr >= _inputEnd) { if (!_loadMore()) { // We ran out of input... return _eofAsNextChar(); } } int i = (int) _inputBuffer[_inputPtr++]; if (i > INT_SPACE) { if (i == INT_SLASH) { _skipComment(); continue; } if (i == INT_HASH) { if (_skipYAMLComment()) { continue; } } return i; } else if (i != INT_SPACE) { if (i == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } } private void _skipComment() throws IOException { if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) { _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", null); } char c = _inputBuffer[_inputPtr++]; if (c == '/') { _skipLine(); } else if (c == '*') { _skipCComment(); } else { _reportUnexpectedChar(c, "was expecting either '*' or '/' for a comment"); } } private void _skipCComment() throws IOException { // Ok: need the matching '*/' while ((_inputPtr < _inputEnd) || _loadMore()) { int i = (int) _inputBuffer[_inputPtr++]; if (i <= '*') { if (i == '*') { // end? if ((_inputPtr >= _inputEnd) && !_loadMore()) { break; } if (_inputBuffer[_inputPtr] == INT_SLASH) { ++_inputPtr; return; } continue; } if (i < INT_SPACE) { if (i == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } } _reportInvalidEOF(" in a comment", null); } private boolean _skipYAMLComment() throws IOException { if ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) == 0) { return false; } _skipLine(); return true; } private void _skipLine() throws IOException { // Ok: need to find EOF or linefeed while ((_inputPtr < _inputEnd) || _loadMore()) { int i = (int) _inputBuffer[_inputPtr++]; if (i < INT_SPACE) { if (i == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; break; } else if (i == INT_CR) { _skipCR(); break; } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } } @Override protected char _decodeEscaped() throws IOException { if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOF(" in character escape sequence", JsonToken.VALUE_STRING); } } char c = _inputBuffer[_inputPtr++]; switch ((int) 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 c; case 'u': // and finally hex-escaped break; default: return _handleUnrecognizedCharacterEscape(c); } // Ok, a hex escape. Need 4 characters int value = 0; for (int i = 0; i < 4; ++i) { if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOF(" in character escape sequence", JsonToken.VALUE_STRING); } } int ch = (int) _inputBuffer[_inputPtr++]; 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; } private final void _matchTrue() throws IOException { int ptr = _inputPtr; if ((ptr + 3) < _inputEnd) { final char[] b = _inputBuffer; if (b[ptr] == 'r' && b[++ptr] == 'u' && b[++ptr] == 'e') { char c = b[++ptr]; if (c < '0' || c == ']' || c == '}') { // expected/allowed chars _inputPtr = ptr; return; } } } // buffer boundary, or problem, offline _matchToken("true", 1); } private final void _matchFalse() throws IOException { int ptr = _inputPtr; if ((ptr + 4) < _inputEnd) { final char[] b = _inputBuffer; if (b[ptr] == 'a' && b[++ptr] == 'l' && b[++ptr] == 's' && b[++ptr] == 'e') { char c = b[++ptr]; if (c < '0' || c == ']' || c == '}') { // expected/allowed chars _inputPtr = ptr; return; } } } // buffer boundary, or problem, offline _matchToken("false", 1); } private final void _matchNull() throws IOException { int ptr = _inputPtr; if ((ptr + 3) < _inputEnd) { final char[] b = _inputBuffer; if (b[ptr] == 'u' && b[++ptr] == 'l' && b[++ptr] == 'l') { char c = b[++ptr]; if (c < '0' || c == ']' || c == '}') { // expected/allowed chars _inputPtr = ptr; return; } } } // buffer boundary, or problem, offline _matchToken("null", 1); } /** * Helper method for checking whether input matches expected token */ protected final void _matchToken(String matchStr, int i) throws IOException { final int len = matchStr.length(); if ((_inputPtr + len) >= _inputEnd) { _matchToken2(matchStr, i); return; } do { if (_inputBuffer[_inputPtr] != matchStr.charAt(i)) { _reportInvalidToken(matchStr.substring(0, i)); } ++_inputPtr; } while (++i < len); int ch = _inputBuffer[_inputPtr]; if (ch >= '0' && ch != ']' && ch != '}') { // expected/allowed chars _checkMatchEnd(matchStr, i, ch); } } private final void _matchToken2(String matchStr, int i) throws IOException { final int len = matchStr.length(); do { if (((_inputPtr >= _inputEnd) && !_loadMore()) || (_inputBuffer[_inputPtr] != matchStr.charAt(i))) { _reportInvalidToken(matchStr.substring(0, i)); } ++_inputPtr; } while (++i < len); // but let's also ensure we either get EOF, or non-alphanum char... if (_inputPtr >= _inputEnd && !_loadMore()) { return; } int ch = _inputBuffer[_inputPtr]; if (ch >= '0' && ch != ']' && ch != '}') { // expected/allowed chars _checkMatchEnd(matchStr, i, ch); } } private final void _checkMatchEnd(String matchStr, int i, int c) throws IOException { // but actually only alphanums are problematic char ch = (char) c; if (Character.isJavaIdentifierPart(ch)) { _reportInvalidToken(matchStr.substring(0, i)); } } /* /********************************************************** /* Binary access /********************************************************** */ /** * Efficient handling for incremental parsing of base64-encoded * textual content. */ @SuppressWarnings("resource") protected byte[] _decodeBase64(Base64Variant b64variant) throws IOException { ByteArrayBuilder builder = _getByteArrayBuilder(); //main_loop: while (true) { // first, we'll skip preceding white space, if any char ch; do { if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++]; } while (ch <= INT_SPACE); int bits = b64variant.decodeBase64Char(ch); if (bits < 0) { if (ch == '"') { // reached the end, fair and square? 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 if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++]; 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 if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++]; bits = b64variant.decodeBase64Char(ch); // First branch: can get padding (-> 1 byte) if (bits < 0) { if (bits != Base64Variant.BASE64_VALUE_PADDING) { // as per [JACKSON-631], could also just be 'missing' padding if (ch == '"') { decodedData >>= 4; builder.append(decodedData); if (b64variant.usesPadding()) { --_inputPtr; // to keep parser state bit more consistent _handleBase64MissingPadding(b64variant); } return builder.toByteArray(); } bits = _decodeBase64Escape(b64variant, ch, 2); } if (bits == Base64Variant.BASE64_VALUE_PADDING) { // Ok, must get more padding chars, then if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++]; if (!b64variant.usesPaddingChar(ch)) { if (_decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) { 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; } // otherwise we got escaped other char, to be processed below } // Nope, 2 or 3 bytes decodedData = (decodedData << 6) | bits; // fourth and last base64 char; can be padding, but not ws if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++]; bits = b64variant.decodeBase64Char(ch); if (bits < 0) { if (bits != Base64Variant.BASE64_VALUE_PADDING) { // as per [JACKSON-631], could also just be 'missing' padding if (ch == '"') { decodedData >>= 2; builder.appendTwoBytes(decodedData); if (b64variant.usesPadding()) { --_inputPtr; // to keep parser state bit more consistent _handleBase64MissingPadding(b64variant); } 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 we got escaped other char, to be processed below } // otherwise, our triplet is now complete decodedData = (decodedData << 6) | bits; builder.appendThreeBytes(decodedData); } } /* /********************************************************** /* Internal methods, location updating (refactored in 2.7) /********************************************************** */ @Override public JsonLocation getTokenLocation() { if (_currToken == JsonToken.FIELD_NAME) { long total = _currInputProcessed + (_nameStartOffset-1); return new JsonLocation(_getSourceReference(), -1L, total, _nameStartRow, _nameStartCol); } return new JsonLocation(_getSourceReference(), -1L, _tokenInputTotal-1, _tokenInputRow, _tokenInputCol); } @Override public JsonLocation getCurrentLocation() { int col = _inputPtr - _currInputRowStart + 1; // 1-based return new JsonLocation(_getSourceReference(), -1L, _currInputProcessed + _inputPtr, _currInputRow, col); } // @since 2.7 private final void _updateLocation() { int ptr = _inputPtr; _tokenInputTotal = _currInputProcessed + ptr; _tokenInputRow = _currInputRow; _tokenInputCol = ptr - _currInputRowStart; } // @since 2.7 private final void _updateNameLocation() { int ptr = _inputPtr; _nameStartOffset = ptr; _nameStartRow = _currInputRow; _nameStartCol = ptr - _currInputRowStart; } /* /********************************************************** /* Error reporting /********************************************************** */ protected void _reportInvalidToken(String matchedPart) throws IOException { _reportInvalidToken(matchedPart, _validJsonTokenList()); } protected void _reportInvalidToken(String matchedPart, String msg) throws IOException { /* 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. */ StringBuilder sb = new StringBuilder(matchedPart); while ((_inputPtr < _inputEnd) || _loadMore()) { char c = _inputBuffer[_inputPtr]; if (!Character.isJavaIdentifierPart(c)) { break; } ++_inputPtr; sb.append(c); if (sb.length() >= MAX_ERROR_TOKEN_LENGTH) { sb.append("..."); break; } } _reportError("Unrecognized token '%s': was expecting %s", sb, msg); } /* /********************************************************** /* Internal methods, other /********************************************************** */ private void _closeScope(int i) throws JsonParseException { if (i == INT_RBRACKET) { _updateLocation(); if (!_parsingContext.inArray()) { _reportMismatchedEndMarker(i, '}'); } _parsingContext = _parsingContext.clearAndGetParent(); _currToken = JsonToken.END_ARRAY; } if (i == INT_RCURLY) { _updateLocation(); if (!_parsingContext.inObject()) { _reportMismatchedEndMarker(i, ']'); } _parsingContext = _parsingContext.clearAndGetParent(); _currToken = JsonToken.END_OBJECT; } } } UTF8DataInputJsonParser.java000066400000000000000000003061201356164247300344450ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/jsonpackage 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. *

* 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: *

* * @since 2.8 */ public class UTF8DataInputJsonParser extends ParserBase { final static byte BYTE_LF = (byte) '\n'; @SuppressWarnings("deprecation") private final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask(); @SuppressWarnings("deprecation") private final static int FEAT_MASK_LEADING_ZEROS = Feature.ALLOW_NUMERIC_LEADING_ZEROS.getMask(); @SuppressWarnings("deprecation") private final static int FEAT_MASK_NON_NUM_NUMBERS = Feature.ALLOW_NON_NUMERIC_NUMBERS.getMask(); @SuppressWarnings("deprecation") private final static int FEAT_MASK_ALLOW_MISSING = Feature.ALLOW_MISSING_VALUES.getMask(); private final static int FEAT_MASK_ALLOW_SINGLE_QUOTES = Feature.ALLOW_SINGLE_QUOTES.getMask(); private final static int FEAT_MASK_ALLOW_UNQUOTED_NAMES = Feature.ALLOW_UNQUOTED_FIELD_NAMES.getMask(); private final static int FEAT_MASK_ALLOW_JAVA_COMMENTS = Feature.ALLOW_COMMENTS.getMask(); private final static int FEAT_MASK_ALLOW_YAML_COMMENTS = Feature.ALLOW_YAML_COMMENTS.getMask(); // 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 * ObjectMapper, 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 == INT_QUOTE) { decodedData >>= 4; buffer[outputPtr++] = (byte) decodedData; if (b64variant.usesPadding()) { _handleBase64MissingPadding(b64variant); } break; } bits = _decodeBase64Escape(b64variant, ch, 2); } if (bits == Base64Variant.BASE64_VALUE_PADDING) { // Ok, must get padding ch = _inputData.readUnsignedByte(); if (!b64variant.usesPaddingChar(ch)) { if ((ch != INT_BACKSLASH) || _decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) { 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 == INT_QUOTE) { decodedData >>= 2; buffer[outputPtr++] = (byte) (decodedData >> 8); buffer[outputPtr++] = (byte) decodedData; if (b64variant.usesPadding()) { _handleBase64MissingPadding(b64variant); } 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 { if (_closed) { return null; } /* 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 = _skipWSOrEnd(); if (i < 0) { // end-of-input // Close/release things like input source, symbol table and recyclable buffers close(); return (_currToken = null); } // clear any data retained so far _binaryValue = null; _tokenInputRow = _currInputRow; // Closing scope? if (i == INT_RBRACKET || i == INT_RCURLY) { _closeScope(i); return _currToken; } // 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(); // Was that a trailing comma? if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { if (i == INT_RBRACKET || i == INT_RCURLY) { _closeScope(i); return _currToken; } } } /* And should we now have a name? Always true for * Object contexts, since 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 || i == INT_RCURLY) { _closeScope(i); 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(); // Was that a trailing comma? if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { if (i == INT_RBRACKET || i == INT_RCURLY) { _closeScope(i); return null; } } } if (!_parsingContext.inObject()) { _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. *

* 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 ((_features & FEAT_MASK_LEADING_ZEROS) == 0) { 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. *

* 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 == '\'' && (_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { return _parseAposName(); } if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) { 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 ((_features & FEAT_MASK_ALLOW_MISSING) != 0) { // _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 ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { return _handleApos(); } break; case 'N': _matchToken("NaN", 1); if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { 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 ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { 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), _validJsonTokenList()); } // but if it doesn't look like a token: _reportUnexpectedChar(c, "expected a valid value "+_validJsonValueList()); 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 ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { 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(); } } /** * Alternative to {@link #_skipWS} that handles possible {@link EOFException} * caused by trying to read past the end of {@link InputData}. * * @since 2.9 */ private final int _skipWSOrEnd() throws IOException { int i = _nextByte; if (i < 0) { try { i = _inputData.readUnsignedByte(); } catch (EOFException e) { return _eofAsNextChar(); } } else { _nextByte = -1; } while (true) { if (i > INT_SPACE) { if (i == INT_SLASH || i == INT_HASH) { return _skipWSComment(i); } return i; } else { // 06-May-2016, tatu: Could verify validity of WS, but for now why bother. // ... but line number is useful thingy if (i == INT_CR || i == INT_LF) { ++_currInputRow; } } try { i = _inputData.readUnsignedByte(); } catch (EOFException e) { return _eofAsNextChar(); } } } private final int _skipWSComment(int i) throws IOException { while (true) { 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 ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) { _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 ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) == 0) { 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 minus 0x10000; 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, _validJsonTokenList()); } 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 == INT_QUOTE) { decodedData >>= 4; builder.append(decodedData); if (b64variant.usesPadding()) { _handleBase64MissingPadding(b64variant); } return builder.toByteArray(); } bits = _decodeBase64Escape(b64variant, ch, 2); } if (bits == Base64Variant.BASE64_VALUE_PADDING) { ch = _inputData.readUnsignedByte(); if (!b64variant.usesPaddingChar(ch)) { if ((ch != INT_BACKSLASH) || _decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) { 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 == INT_QUOTE) { decodedData >>= 2; builder.appendTwoBytes(decodedData); if (b64variant.usesPadding()) { _handleBase64MissingPadding(b64variant); } 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() { return new JsonLocation(_getSourceReference(), -1L, -1L, _tokenInputRow, -1); } @Override public JsonLocation getCurrentLocation() { return new JsonLocation(_getSourceReference(), -1L, -1L, _currInputRow, -1); } /* /********************************************************** /* Internal methods, other /********************************************************** */ private void _closeScope(int i) throws JsonParseException { if (i == INT_RBRACKET) { if (!_parsingContext.inArray()) { _reportMismatchedEndMarker(i, '}'); } _parsingContext = _parsingContext.clearAndGetParent(); _currToken = JsonToken.END_ARRAY; } if (i == INT_RCURLY) { if (!_parsingContext.inObject()) { _reportMismatchedEndMarker(i, ']'); } _parsingContext = _parsingContext.clearAndGetParent(); _currToken = JsonToken.END_OBJECT; } } /** * Helper method needed to fix [Issue#148], masking of 0x00 character */ private final static int pad(int q, int bytes) { return (bytes == 4) ? q : (q | (-1 << (bytes << 3))); } } UTF8JsonGenerator.java000066400000000000000000002243361356164247300333350ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.*; import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.CharTypes; import com.fasterxml.jackson.core.io.CharacterEscapes; import com.fasterxml.jackson.core.io.IOContext; import com.fasterxml.jackson.core.io.NumberOutput; public class UTF8JsonGenerator extends JsonGeneratorImpl { private final static byte BYTE_u = (byte) 'u'; private final static byte BYTE_0 = (byte) '0'; private final static byte BYTE_LBRACKET = (byte) '['; private final static byte BYTE_RBRACKET = (byte) ']'; private final static byte BYTE_LCURLY = (byte) '{'; private final static byte BYTE_RCURLY = (byte) '}'; private final static byte BYTE_BACKSLASH = (byte) '\\'; private final static byte BYTE_COMMA = (byte) ','; private final static byte BYTE_COLON = (byte) ':'; // intermediate copies only made up to certain length... private final static int MAX_BYTES_TO_BUFFER = 512; private final static byte[] HEX_CHARS = CharTypes.copyHexBytes(); private final static byte[] NULL_BYTES = { 'n', 'u', 'l', 'l' }; private final static byte[] TRUE_BYTES = { 't', 'r', 'u', 'e' }; private final static byte[] FALSE_BYTES = { 'f', 'a', 'l', 's', 'e' }; /* /********************************************************** /* Configuration /********************************************************** */ /** * Underlying output stream used for writing JSON content. */ final protected OutputStream _outputStream; /** * Character used for quoting JSON Object property names * and String values. * * @since 2.8 */ protected byte _quoteChar; /* /********************************************************** /* Output buffering /********************************************************** */ /** * Intermediate buffer in which contents are buffered before * being written using {@link #_outputStream}. */ protected byte[] _outputBuffer; /** * Pointer to the position right beyond the last character to output * (end marker; may be past the buffer) */ protected int _outputTail; /** * End marker of the output buffer; one past the last valid position * within the buffer. */ protected final int _outputEnd; /** * Maximum number of chars that we know will always fit * in the output buffer after escaping */ protected final int _outputMaxContiguous; /** * Intermediate buffer in which characters of a String are copied * before being encoded. */ protected char[] _charBuffer; /** * Length of _charBuffer */ protected final int _charBufferLength; /** * 6 character temporary buffer allocated if needed, for constructing * escape sequences */ protected byte[] _entityBuffer; /** * Flag that indicates whether the output buffer is recycable (and * needs to be returned to recycler once we are done) or not. */ protected boolean _bufferRecyclable; /* /********************************************************** /* Life-cycle /********************************************************** */ /** * @since 2.10 */ @SuppressWarnings("deprecation") public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec, OutputStream out, char quoteChar) { super(ctxt, features, codec); _outputStream = out; _quoteChar = (byte) quoteChar; if (quoteChar != '"') { // since 2.10 _outputEscapes = CharTypes.get7BitOutputEscapes(quoteChar); } _bufferRecyclable = true; _outputBuffer = ctxt.allocWriteEncodingBuffer(); _outputEnd = _outputBuffer.length; /* To be exact, each char can take up to 6 bytes when escaped (Unicode * escape with backslash, 'u' and 4 hex digits); but to avoid fluctuation, * we will actually round down to only do up to 1/8 number of chars */ _outputMaxContiguous = _outputEnd >> 3; _charBuffer = ctxt.allocConcatBuffer(); _charBufferLength = _charBuffer.length; // By default we use this feature to determine additional quoting if (isEnabled(Feature.ESCAPE_NON_ASCII)) { setHighestNonEscapedChar(127); } } /** * @since 2.10 */ public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec, OutputStream out, char quoteChar, byte[] outputBuffer, int outputOffset, boolean bufferRecyclable) { super(ctxt, features, codec); _outputStream = out; _quoteChar = (byte) quoteChar; if (quoteChar != '"') { // since 2.10 _outputEscapes = CharTypes.get7BitOutputEscapes(quoteChar); } _bufferRecyclable = bufferRecyclable; _outputTail = outputOffset; _outputBuffer = outputBuffer; _outputEnd = _outputBuffer.length; // up to 6 bytes per char (see above), rounded up to 1/8 _outputMaxContiguous = (_outputEnd >> 3); _charBuffer = ctxt.allocConcatBuffer(); _charBufferLength = _charBuffer.length; } @Deprecated // since 2.10 public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec, OutputStream out) { this(ctxt, features, codec, out, JsonFactory.DEFAULT_QUOTE_CHAR); } @Deprecated // since 2.10 public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec, OutputStream out, byte[] outputBuffer, int outputOffset, boolean bufferRecyclable) { this(ctxt, features, codec, out, JsonFactory.DEFAULT_QUOTE_CHAR, outputBuffer, outputOffset, bufferRecyclable); } /* /********************************************************** /* Overridden configuration methods /********************************************************** */ @Override public Object getOutputTarget() { return _outputStream; } @Override public int getOutputBuffered() { // Assuming tail is always valid, set to 0 on close return _outputTail; } /* /********************************************************** /* Overridden methods /********************************************************** */ @Override public void writeFieldName(String name) throws IOException { if (_cfgPrettyPrinter != null) { _writePPFieldName(name); return; } final int status = _writeContext.writeFieldName(name); if (status == JsonWriteContext.STATUS_EXPECT_VALUE) { _reportError("Can not write a field name, expecting a value"); } if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) { // need comma if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_COMMA; } /* To support [JACKSON-46], we'll do this: * (Question: should quoting of spaces (etc) still be enabled?) */ if (_cfgUnqNames) { _writeStringSegments(name, false); return; } final int len = name.length(); // Does it fit in buffer? if (len > _charBufferLength) { // no, offline _writeStringSegments(name, true); return; } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; // But as one segment, or multiple? if (len <= _outputMaxContiguous) { if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space _flushBuffer(); } _writeStringSegment(name, 0, len); } else { _writeStringSegments(name, 0, len); } // and closing quotes; need room for one more char: if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } @Override public void writeFieldName(SerializableString name) throws IOException { if (_cfgPrettyPrinter != null) { _writePPFieldName(name); return; } final int status = _writeContext.writeFieldName(name.getValue()); if (status == JsonWriteContext.STATUS_EXPECT_VALUE) { _reportError("Can not write a field name, expecting a value"); } if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_COMMA; } if (_cfgUnqNames) { _writeUnq(name); return; } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; int len = name.appendQuotedUTF8(_outputBuffer, _outputTail); if (len < 0) { // couldn't append, bit longer processing _writeBytes(name.asQuotedUTF8()); } else { _outputTail += len; } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } private final void _writeUnq(SerializableString name) throws IOException { int len = name.appendQuotedUTF8(_outputBuffer, _outputTail); if (len < 0) { _writeBytes(name.asQuotedUTF8()); } else { _outputTail += len; } } /* /********************************************************** /* Output method implementations, structural /********************************************************** */ @Override public final void writeStartArray() throws IOException { _verifyValueWrite("start an array"); _writeContext = _writeContext.createChildArrayContext(); if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeStartArray(this); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_LBRACKET; } } @Override // since 2.10 public void writeStartArray(int size) throws IOException { _verifyValueWrite("start an array"); _writeContext = _writeContext.createChildArrayContext(); if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeStartArray(this); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_LBRACKET; } } @Override public final void writeEndArray() throws IOException { if (!_writeContext.inArray()) { _reportError("Current context not Array but "+_writeContext.typeDesc()); } if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount()); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_RBRACKET; } _writeContext = _writeContext.clearAndGetParent(); } @Override public final void writeStartObject() throws IOException { _verifyValueWrite("start an object"); _writeContext = _writeContext.createChildObjectContext(); if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeStartObject(this); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_LCURLY; } } @Override // since 2.8 public void writeStartObject(Object forValue) throws IOException { _verifyValueWrite("start an object"); JsonWriteContext ctxt = _writeContext.createChildObjectContext(forValue); _writeContext = ctxt; 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 Object but "+_writeContext.typeDesc()); } if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount()); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_RCURLY; } _writeContext = _writeContext.clearAndGetParent(); } /** * Specialized version of _writeFieldName, off-lined * to keep the "fast path" as simple (and hopefully fast) as possible. */ protected final void _writePPFieldName(String name) throws IOException { int status = _writeContext.writeFieldName(name); if (status == JsonWriteContext.STATUS_EXPECT_VALUE) { _reportError("Can not write a field name, expecting a value"); } if ((status == JsonWriteContext.STATUS_OK_AFTER_COMMA)) { _cfgPrettyPrinter.writeObjectEntrySeparator(this); } else { _cfgPrettyPrinter.beforeObjectEntries(this); } if (_cfgUnqNames) { _writeStringSegments(name, false); return; } final int len = name.length(); if (len > _charBufferLength) { _writeStringSegments(name, true); return; } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; name.getChars(0, len, _charBuffer, 0); // But as one segment, or multiple? if (len <= _outputMaxContiguous) { if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space _flushBuffer(); } _writeStringSegment(_charBuffer, 0, len); } else { _writeStringSegments(_charBuffer, 0, len); } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } protected final void _writePPFieldName(SerializableString name) throws IOException { final int status = _writeContext.writeFieldName(name.getValue()); if (status == JsonWriteContext.STATUS_EXPECT_VALUE) { _reportError("Can not write a field name, expecting a value"); } if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) { _cfgPrettyPrinter.writeObjectEntrySeparator(this); } else { _cfgPrettyPrinter.beforeObjectEntries(this); } final boolean addQuotes = !_cfgUnqNames; // standard if (addQuotes) { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } int len = name.appendQuotedUTF8(_outputBuffer, _outputTail); if (len < 0) { _writeBytes(name.asQuotedUTF8()); } else { _outputTail += len; } if (addQuotes) { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } } /* /********************************************************** /* Output method implementations, textual /********************************************************** */ @Override public void writeString(String text) throws IOException { _verifyValueWrite(WRITE_STRING); if (text == null) { _writeNull(); return; } // First: if we can't guarantee it all fits, quoted, within output, offline final int len = text.length(); if (len > _outputMaxContiguous) { // nope: off-line handling _writeStringSegments(text, true); return; } if ((_outputTail + len) >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; _writeStringSegment(text, 0, len); // we checked space already above if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } @Override public void writeString(Reader reader, int len) throws IOException { _verifyValueWrite(WRITE_STRING); if (reader == null) { _reportError("null reader"); } int toRead = (len >= 0) ? len : Integer.MAX_VALUE; final char[] buf = _charBuffer; // Add leading quote if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; // read while (toRead > 0){ int toReadNow = Math.min(toRead, buf.length); int numRead = reader.read(buf, 0, toReadNow); if(numRead <= 0){ break; } if ((_outputTail + len) >= _outputEnd) { _flushBuffer(); } _writeStringSegments(buf, 0, numRead); //decrease tracker toRead -= numRead; } // Add trailing quote if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; if (toRead > 0 && len >= 0){ _reportError("Didn't read enough from reader"); } } @Override public void writeString(char[] text, int offset, int len) throws IOException { _verifyValueWrite(WRITE_STRING); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; // One or multiple segments? if (len <= _outputMaxContiguous) { if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space _flushBuffer(); } _writeStringSegment(text, offset, len); } else { _writeStringSegments(text, offset, len); } // And finally, closing quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } @Override public final void writeString(SerializableString text) throws IOException { _verifyValueWrite(WRITE_STRING); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; int len = text.appendQuotedUTF8(_outputBuffer, _outputTail); if (len < 0) { _writeBytes(text.asQuotedUTF8()); } else { _outputTail += len; } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } @Override public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException { _verifyValueWrite(WRITE_STRING); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; _writeBytes(text, offset, length); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } @Override public void writeUTF8String(byte[] text, int offset, int len) throws IOException { _verifyValueWrite(WRITE_STRING); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; // One or multiple segments? if (len <= _outputMaxContiguous) { _writeUTF8Segment(text, offset, len); } else { _writeUTF8Segments(text, offset, len); } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } /* /********************************************************** /* Output method implementations, unprocessed ("raw") /********************************************************** */ @Override public void writeRaw(String text) throws IOException { final int len = text.length(); final char[] buf = _charBuffer; if (len <= buf.length) { text.getChars(0, len, buf, 0); writeRaw(buf, 0, len); } else { writeRaw(text, 0, len); } } @Override public void writeRaw(String text, int offset, int len) throws IOException { final char[] buf = _charBuffer; final int cbufLen = buf.length; // minor optimization: see if we can just get and copy if (len <= cbufLen) { text.getChars(offset, offset+len, buf, 0); writeRaw(buf, 0, len); return; } // If not, need segmented approach. For speed, let's also use input buffer // size that is guaranteed to fit in output buffer; each char can expand to // at most 3 bytes, so at most 1/3 of buffer size. final int maxChunk = Math.min(cbufLen, (_outputEnd >> 2) + (_outputEnd >> 4)); // == (1/4 + 1/16) == 5/16 final int maxBytes = maxChunk * 3; while (len > 0) { int len2 = Math.min(maxChunk, len); text.getChars(offset, offset+len2, buf, 0); if ((_outputTail + maxBytes) > _outputEnd) { _flushBuffer(); } // If this is NOT the last segment and if the last character looks like // split surrogate second half, drop it // 21-Mar-2017, tatu: Note that we could check for either `len` or `len2`; // point here is really that we only "punt" surrogate if it is NOT the // only character left; otherwise we'd end up with a poison pill if the // very last character was unpaired first-surrogate if (len2 > 1) { char ch = buf[len2-1]; if ((ch >= SURR1_FIRST) && (ch <= SURR1_LAST)) { --len2; } } _writeRawSegment(buf, 0, len2); offset += len2; len -= len2; } } @Override public void writeRaw(SerializableString text) throws IOException { int len = text.appendUnquotedUTF8(_outputBuffer, _outputTail); if (len < 0) { _writeBytes(text.asUnquotedUTF8()); } else { _outputTail += len; } } // since 2.5 @Override public void writeRawValue(SerializableString text) throws IOException { _verifyValueWrite(WRITE_RAW); int len = text.appendUnquotedUTF8(_outputBuffer, _outputTail); if (len < 0) { _writeBytes(text.asUnquotedUTF8()); } else { _outputTail += len; } } // @TODO: rewrite for speed... @Override public final void writeRaw(char[] cbuf, int offset, int len) throws IOException { // First: if we have 3 x charCount spaces, we know it'll fit just fine { int len3 = len+len+len; if ((_outputTail + len3) > _outputEnd) { // maybe we could flush? if (_outputEnd < len3) { // wouldn't be enough... _writeSegmentedRaw(cbuf, offset, len); return; } // yes, flushing brings enough space _flushBuffer(); } } len += offset; // now marks the end // Note: here we know there is enough room, hence no output boundary checks main_loop: while (offset < len) { inner_loop: while (true) { int ch = (int) cbuf[offset]; if (ch > 0x7F) { break inner_loop; } _outputBuffer[_outputTail++] = (byte) ch; if (++offset >= len) { break main_loop; } } char ch = cbuf[offset++]; if (ch < 0x800) { // 2-byte? _outputBuffer[_outputTail++] = (byte) (0xc0 | (ch >> 6)); _outputBuffer[_outputTail++] = (byte) (0x80 | (ch & 0x3f)); } else { offset = _outputRawMultiByteChar(ch, cbuf, offset, len); } } } @Override public void writeRaw(char ch) throws IOException { if ((_outputTail + 3) >= _outputEnd) { _flushBuffer(); } final byte[] bbuf = _outputBuffer; if (ch <= 0x7F) { bbuf[_outputTail++] = (byte) ch; } else if (ch < 0x800) { // 2-byte? bbuf[_outputTail++] = (byte) (0xc0 | (ch >> 6)); bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f)); } else { /*offset =*/ _outputRawMultiByteChar(ch, null, 0, 0); } } /** * Helper method called when it is possible that output of raw section * to output may cross buffer boundary */ private final void _writeSegmentedRaw(char[] cbuf, int offset, int len) throws IOException { final int end = _outputEnd; final byte[] bbuf = _outputBuffer; final int inputEnd = offset + len; main_loop: while (offset < inputEnd) { inner_loop: while (true) { int ch = (int) cbuf[offset]; if (ch >= 0x80) { break inner_loop; } // !!! TODO: fast(er) writes (roll input, output checks in one) if (_outputTail >= end) { _flushBuffer(); } bbuf[_outputTail++] = (byte) ch; if (++offset >= inputEnd) { break main_loop; } } if ((_outputTail + 3) >= _outputEnd) { _flushBuffer(); } char ch = cbuf[offset++]; if (ch < 0x800) { // 2-byte? bbuf[_outputTail++] = (byte) (0xc0 | (ch >> 6)); bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f)); } else { offset = _outputRawMultiByteChar(ch, cbuf, offset, inputEnd); } } } /** * Helper method that is called for segmented write of raw content * when explicitly outputting a segment of longer thing. * Caller has to take care of ensuring there's no split surrogate * pair at the end (that is, last char can not be first part of a * surrogate char pair). * * @since 2.8.2 */ private void _writeRawSegment(char[] cbuf, int offset, int end) throws IOException { main_loop: while (offset < end) { inner_loop: while (true) { int ch = (int) cbuf[offset]; if (ch > 0x7F) { break inner_loop; } _outputBuffer[_outputTail++] = (byte) ch; if (++offset >= end) { break main_loop; } } char ch = cbuf[offset++]; if (ch < 0x800) { // 2-byte? _outputBuffer[_outputTail++] = (byte) (0xc0 | (ch >> 6)); _outputBuffer[_outputTail++] = (byte) (0x80 | (ch & 0x3f)); } else { offset = _outputRawMultiByteChar(ch, cbuf, offset, end); } } } /* /********************************************************** /* Output method implementations, base64-encoded binary /********************************************************** */ @Override public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException, JsonGenerationException { _verifyValueWrite(WRITE_BINARY); // Starting quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; _writeBinary(b64variant, data, offset, offset+len); // and closing quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } @Override public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) throws IOException, JsonGenerationException { _verifyValueWrite(WRITE_BINARY); // Starting quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; byte[] encodingBuffer = _ioContext.allocBase64Buffer(); int bytes; try { if (dataLength < 0) { // length unknown bytes = _writeBinary(b64variant, data, encodingBuffer); } else { int missing = _writeBinary(b64variant, data, encodingBuffer, dataLength); if (missing > 0) { _reportError("Too few bytes available: missing "+missing+" bytes (out of "+dataLength+")"); } bytes = dataLength; } } finally { _ioContext.releaseBase64Buffer(encodingBuffer); } // and closing quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; return bytes; } /* /********************************************************** /* Output method implementations, primitive /********************************************************** */ @Override public void writeNumber(short s) throws IOException { _verifyValueWrite(WRITE_NUMBER); // up to 5 digits and possible minus sign if ((_outputTail + 6) >= _outputEnd) { _flushBuffer(); } if (_cfgNumbersAsStrings) { _writeQuotedShort(s); return; } _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail); } private final void _writeQuotedShort(short s) throws IOException { if ((_outputTail + 8) >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail); _outputBuffer[_outputTail++] = _quoteChar; } @Override public void writeNumber(int i) throws IOException { _verifyValueWrite(WRITE_NUMBER); // up to 10 digits and possible minus sign if ((_outputTail + 11) >= _outputEnd) { _flushBuffer(); } if (_cfgNumbersAsStrings) { _writeQuotedInt(i); return; } _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail); } private final void _writeQuotedInt(int i) throws IOException { if ((_outputTail + 13) >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail); _outputBuffer[_outputTail++] = _quoteChar; } @Override public void writeNumber(long l) throws IOException { _verifyValueWrite(WRITE_NUMBER); if (_cfgNumbersAsStrings) { _writeQuotedLong(l); return; } if ((_outputTail + 21) >= _outputEnd) { // up to 20 digits, minus sign _flushBuffer(); } _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail); } private final void _writeQuotedLong(long l) throws IOException { if ((_outputTail + 23) >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail); _outputBuffer[_outputTail++] = _quoteChar; } @Override public void writeNumber(BigInteger value) throws IOException { _verifyValueWrite(WRITE_NUMBER); if (value == null) { _writeNull(); } else if (_cfgNumbersAsStrings) { _writeQuotedRaw(value.toString()); } else { writeRaw(value.toString()); } } @SuppressWarnings("deprecation") @Override public void writeNumber(double d) throws IOException { if (_cfgNumbersAsStrings || (NumberOutput.notFinite(d) && Feature.QUOTE_NON_NUMERIC_NUMBERS.enabledIn(_features))) { writeString(String.valueOf(d)); return; } // What is the max length for doubles? 40 chars? _verifyValueWrite(WRITE_NUMBER); writeRaw(String.valueOf(d)); } @SuppressWarnings("deprecation") @Override public void writeNumber(float f) throws IOException { if (_cfgNumbersAsStrings || (NumberOutput.notFinite(f) && Feature.QUOTE_NON_NUMERIC_NUMBERS.enabledIn(_features))) { writeString(String.valueOf(f)); return; } // What is the max length for floats? _verifyValueWrite(WRITE_NUMBER); writeRaw(String.valueOf(f)); } @Override public void writeNumber(BigDecimal value) throws IOException { // Don't really know max length for big decimal, no point checking _verifyValueWrite(WRITE_NUMBER); if (value == null) { _writeNull(); } else if (_cfgNumbersAsStrings) { _writeQuotedRaw(_asString(value)); } else { writeRaw(_asString(value)); } } @Override public void writeNumber(String encodedValue) throws IOException { _verifyValueWrite(WRITE_NUMBER); if (_cfgNumbersAsStrings) { _writeQuotedRaw(encodedValue); } else { writeRaw(encodedValue); } } private final void _writeQuotedRaw(String value) throws IOException { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; writeRaw(value); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } @Override public void writeBoolean(boolean state) throws IOException { _verifyValueWrite(WRITE_BOOLEAN); if ((_outputTail + 5) >= _outputEnd) { _flushBuffer(); } byte[] keyword = state ? TRUE_BYTES : FALSE_BYTES; int len = keyword.length; System.arraycopy(keyword, 0, _outputBuffer, _outputTail, len); _outputTail += len; } @Override public void writeNull() throws IOException { _verifyValueWrite(WRITE_NULL); _writeNull(); } /* /********************************************************** /* Implementations for other methods /********************************************************** */ @Override protected final void _verifyValueWrite(String typeMsg) throws IOException { final int status = _writeContext.writeValue(); if (_cfgPrettyPrinter != null) { // Otherwise, pretty printer knows what to do... _verifyPrettyValueWrite(typeMsg, status); return; } byte b; switch (status) { case JsonWriteContext.STATUS_OK_AS_IS: default: return; 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_EXPECT_NAME: _reportCantWriteValueExpectName(typeMsg); return; } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = b; } /* /********************************************************** /* Low-level output handling /********************************************************** */ @Override public void flush() throws IOException { _flushBuffer(); if (_outputStream != null) { if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) { _outputStream.flush(); } } } @Override public void close() throws IOException { super.close(); /* 05-Dec-2008, tatu: To add [JACKSON-27], need to close open * scopes. */ // First: let's see that we still have buffers... if ((_outputBuffer != null) && isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) { while (true) { JsonStreamContext ctxt = getOutputContext(); if (ctxt.inArray()) { writeEndArray(); } else if (ctxt.inObject()) { writeEndObject(); } else { break; } } } _flushBuffer(); _outputTail = 0; // just to ensure we don't think there's anything buffered /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close() * on the underlying Reader, unless we "own" it, or auto-closing * feature is enabled. * One downside: when using UTF8Writer, underlying buffer(s) * may not be properly recycled if we don't close the writer. */ if (_outputStream != null) { if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) { _outputStream.close(); } else if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) { // If we can't close it, we should at least flush _outputStream.flush(); } } // Internal buffer(s) generator has can now be released as well _releaseBuffers(); } @Override protected void _releaseBuffers() { byte[] buf = _outputBuffer; if (buf != null && _bufferRecyclable) { _outputBuffer = null; _ioContext.releaseWriteEncodingBuffer(buf); } char[] cbuf = _charBuffer; if (cbuf != null) { _charBuffer = null; _ioContext.releaseConcatBuffer(cbuf); } } /* /********************************************************** /* Internal methods, low-level writing, raw bytes /********************************************************** */ private final void _writeBytes(byte[] bytes) throws IOException { final int len = bytes.length; if ((_outputTail + len) > _outputEnd) { _flushBuffer(); // still not enough? if (len > MAX_BYTES_TO_BUFFER) { _outputStream.write(bytes, 0, len); return; } } System.arraycopy(bytes, 0, _outputBuffer, _outputTail, len); _outputTail += len; } private final void _writeBytes(byte[] bytes, int offset, int len) throws IOException { if ((_outputTail + len) > _outputEnd) { _flushBuffer(); // still not enough? if (len > MAX_BYTES_TO_BUFFER) { _outputStream.write(bytes, offset, len); return; } } System.arraycopy(bytes, offset, _outputBuffer, _outputTail, len); _outputTail += len; } /* /********************************************************** /* Internal methods, mid-level writing, String segments /********************************************************** */ /** * Method called when String to write is long enough not to fit * completely in temporary copy buffer. If so, we will actually * copy it in small enough chunks so it can be directly fed * to single-segment writes (instead of maximum slices that * would fit in copy buffer) */ private final void _writeStringSegments(String text, boolean addQuotes) throws IOException { if (addQuotes) { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } int left = text.length(); int offset = 0; while (left > 0) { int len = Math.min(_outputMaxContiguous, left); if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space _flushBuffer(); } _writeStringSegment(text, offset, len); offset += len; left -= len; } if (addQuotes) { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } } /** * Method called when character sequence to write is long enough that * its maximum encoded and escaped form is not guaranteed to fit in * the output buffer. If so, we will need to choose smaller output * chunks to write at a time. */ private final void _writeStringSegments(char[] cbuf, int offset, int totalLen) throws IOException { do { int len = Math.min(_outputMaxContiguous, totalLen); if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space _flushBuffer(); } _writeStringSegment(cbuf, offset, len); offset += len; totalLen -= len; } while (totalLen > 0); } private final void _writeStringSegments(String text, int offset, int totalLen) throws IOException { do { int len = Math.min(_outputMaxContiguous, totalLen); if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space _flushBuffer(); } _writeStringSegment(text, offset, len); offset += len; totalLen -= len; } while (totalLen > 0); } /* /********************************************************** /* Internal methods, low-level writing, text segments /********************************************************** */ /** * This method called when the string content is already in * a char buffer, and its maximum total encoded and escaped length * can not exceed size of the output buffer. * Caller must ensure that there is enough space in output buffer, * assuming case of all non-escaped ASCII characters, as well as * potentially enough space for other cases (but not necessarily flushed) */ private final void _writeStringSegment(char[] cbuf, int offset, int len) throws IOException { // note: caller MUST ensure (via flushing) there's room for ASCII only // Fast+tight loop for ASCII-only, no-escaping-needed output len += offset; // becomes end marker, then int outputPtr = _outputTail; final byte[] outputBuffer = _outputBuffer; final int[] escCodes = _outputEscapes; while (offset < len) { int ch = cbuf[offset]; // note: here we know that (ch > 0x7F) will cover case of escaping non-ASCII too: if (ch > 0x7F || escCodes[ch] != 0) { break; } outputBuffer[outputPtr++] = (byte) ch; ++offset; } _outputTail = outputPtr; if (offset < len) { if (_characterEscapes != null) { _writeCustomStringSegment2(cbuf, offset, len); } else if (_maximumNonEscapedChar == 0) { _writeStringSegment2(cbuf, offset, len); } else { _writeStringSegmentASCII2(cbuf, offset, len); } } } private final void _writeStringSegment(String text, int offset, int len) throws IOException { // note: caller MUST ensure (via flushing) there's room for ASCII only // Fast+tight loop for ASCII-only, no-escaping-needed output len += offset; // becomes end marker, then int outputPtr = _outputTail; final byte[] outputBuffer = _outputBuffer; final int[] escCodes = _outputEscapes; while (offset < len) { int ch = text.charAt(offset); // note: here we know that (ch > 0x7F) will cover case of escaping non-ASCII too: if (ch > 0x7F || escCodes[ch] != 0) { break; } outputBuffer[outputPtr++] = (byte) ch; ++offset; } _outputTail = outputPtr; if (offset < len) { if (_characterEscapes != null) { _writeCustomStringSegment2(text, offset, len); } else if (_maximumNonEscapedChar == 0) { _writeStringSegment2(text, offset, len); } else { _writeStringSegmentASCII2(text, offset, len); } } } /** * Secondary method called when content contains characters to escape, * and/or multi-byte UTF-8 characters. */ private final void _writeStringSegment2(final char[] cbuf, int offset, final int end) throws IOException { // Ok: caller guarantees buffer can have room; but that may require flushing: if ((_outputTail + 6 * (end - offset)) > _outputEnd) { _flushBuffer(); } int outputPtr = _outputTail; final byte[] outputBuffer = _outputBuffer; final int[] escCodes = _outputEscapes; while (offset < end) { int ch = cbuf[offset++]; if (ch <= 0x7F) { if (escCodes[ch] == 0) { outputBuffer[outputPtr++] = (byte) ch; continue; } int escape = escCodes[ch]; if (escape > 0) { // 2-char escape, fine outputBuffer[outputPtr++] = BYTE_BACKSLASH; outputBuffer[outputPtr++] = (byte) escape; } else { // ctrl-char, 6-byte escape... outputPtr = _writeGenericEscape(ch, outputPtr); } continue; } if (ch <= 0x7FF) { // fine, just needs 2 byte output outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6)); outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f)); } else { outputPtr = _outputMultiByteChar(ch, outputPtr); } } _outputTail = outputPtr; } private final void _writeStringSegment2(final String text, int offset, final int end) throws IOException { if ((_outputTail + 6 * (end - offset)) > _outputEnd) { _flushBuffer(); } int outputPtr = _outputTail; final byte[] outputBuffer = _outputBuffer; final int[] escCodes = _outputEscapes; while (offset < end) { int ch = text.charAt(offset++); if (ch <= 0x7F) { if (escCodes[ch] == 0) { outputBuffer[outputPtr++] = (byte) ch; continue; } int escape = escCodes[ch]; if (escape > 0) { // 2-char escape, fine outputBuffer[outputPtr++] = BYTE_BACKSLASH; outputBuffer[outputPtr++] = (byte) escape; } else { // ctrl-char, 6-byte escape... outputPtr = _writeGenericEscape(ch, outputPtr); } continue; } if (ch <= 0x7FF) { // fine, just needs 2 byte output outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6)); outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f)); } else { outputPtr = _outputMultiByteChar(ch, outputPtr); } } _outputTail = outputPtr; } /* /********************************************************** /* Internal methods, low-level writing, text segment /* with additional escaping (ASCII or such) /********************************************************** */ /** * Same as _writeStringSegment2(char[], ...) _outputEnd) { _flushBuffer(); } int outputPtr = _outputTail; final byte[] outputBuffer = _outputBuffer; final int[] escCodes = _outputEscapes; final int maxUnescaped = _maximumNonEscapedChar; while (offset < end) { int ch = cbuf[offset++]; if (ch <= 0x7F) { if (escCodes[ch] == 0) { outputBuffer[outputPtr++] = (byte) ch; continue; } int escape = escCodes[ch]; if (escape > 0) { // 2-char escape, fine outputBuffer[outputPtr++] = BYTE_BACKSLASH; outputBuffer[outputPtr++] = (byte) escape; } else { // ctrl-char, 6-byte escape... outputPtr = _writeGenericEscape(ch, outputPtr); } continue; } if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars: outputPtr = _writeGenericEscape(ch, outputPtr); continue; } if (ch <= 0x7FF) { // fine, just needs 2 byte output outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6)); outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f)); } else { outputPtr = _outputMultiByteChar(ch, outputPtr); } } _outputTail = outputPtr; } private final void _writeStringSegmentASCII2(final String text, int offset, final int end) throws IOException { // Ok: caller guarantees buffer can have room; but that may require flushing: if ((_outputTail + 6 * (end - offset)) > _outputEnd) { _flushBuffer(); } int outputPtr = _outputTail; final byte[] outputBuffer = _outputBuffer; final int[] escCodes = _outputEscapes; final int maxUnescaped = _maximumNonEscapedChar; while (offset < end) { int ch = text.charAt(offset++); if (ch <= 0x7F) { if (escCodes[ch] == 0) { outputBuffer[outputPtr++] = (byte) ch; continue; } int escape = escCodes[ch]; if (escape > 0) { // 2-char escape, fine outputBuffer[outputPtr++] = BYTE_BACKSLASH; outputBuffer[outputPtr++] = (byte) escape; } else { // ctrl-char, 6-byte escape... outputPtr = _writeGenericEscape(ch, outputPtr); } continue; } if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars: outputPtr = _writeGenericEscape(ch, outputPtr); continue; } if (ch <= 0x7FF) { // fine, just needs 2 byte output outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6)); outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f)); } else { outputPtr = _outputMultiByteChar(ch, outputPtr); } } _outputTail = outputPtr; } /* /********************************************************** /* Internal methods, low-level writing, text segment /* with fully custom escaping (and possibly escaping of non-ASCII /********************************************************** */ /** * Same as _writeStringSegmentASCII2(char[], ...) _outputEnd) { _flushBuffer(); } int outputPtr = _outputTail; final byte[] outputBuffer = _outputBuffer; final int[] escCodes = _outputEscapes; // may or may not have this limit final int maxUnescaped = (_maximumNonEscapedChar <= 0) ? 0xFFFF : _maximumNonEscapedChar; final CharacterEscapes customEscapes = _characterEscapes; // non-null while (offset < end) { int ch = cbuf[offset++]; if (ch <= 0x7F) { if (escCodes[ch] == 0) { outputBuffer[outputPtr++] = (byte) ch; continue; } int escape = escCodes[ch]; if (escape > 0) { // 2-char escape, fine outputBuffer[outputPtr++] = BYTE_BACKSLASH; outputBuffer[outputPtr++] = (byte) escape; } else if (escape == CharacterEscapes.ESCAPE_CUSTOM) { SerializableString esc = customEscapes.getEscapeSequence(ch); if (esc == null) { _reportError("Invalid custom escape definitions; custom escape not found for character code 0x" +Integer.toHexString(ch)+", although was supposed to have one"); } outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset); } else { // ctrl-char, 6-byte escape... outputPtr = _writeGenericEscape(ch, outputPtr); } continue; } if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars: outputPtr = _writeGenericEscape(ch, outputPtr); continue; } SerializableString esc = customEscapes.getEscapeSequence(ch); if (esc != null) { outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset); continue; } if (ch <= 0x7FF) { // fine, just needs 2 byte output outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6)); outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f)); } else { outputPtr = _outputMultiByteChar(ch, outputPtr); } } _outputTail = outputPtr; } private final void _writeCustomStringSegment2(final String text, int offset, final int end) throws IOException { // Ok: caller guarantees buffer can have room; but that may require flushing: if ((_outputTail + 6 * (end - offset)) > _outputEnd) { _flushBuffer(); } int outputPtr = _outputTail; final byte[] outputBuffer = _outputBuffer; final int[] escCodes = _outputEscapes; // may or may not have this limit final int maxUnescaped = (_maximumNonEscapedChar <= 0) ? 0xFFFF : _maximumNonEscapedChar; final CharacterEscapes customEscapes = _characterEscapes; // non-null while (offset < end) { int ch = text.charAt(offset++); if (ch <= 0x7F) { if (escCodes[ch] == 0) { outputBuffer[outputPtr++] = (byte) ch; continue; } int escape = escCodes[ch]; if (escape > 0) { // 2-char escape, fine outputBuffer[outputPtr++] = BYTE_BACKSLASH; outputBuffer[outputPtr++] = (byte) escape; } else if (escape == CharacterEscapes.ESCAPE_CUSTOM) { SerializableString esc = customEscapes.getEscapeSequence(ch); if (esc == null) { _reportError("Invalid custom escape definitions; custom escape not found for character code 0x" +Integer.toHexString(ch)+", although was supposed to have one"); } outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset); } else { // ctrl-char, 6-byte escape... outputPtr = _writeGenericEscape(ch, outputPtr); } continue; } if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars: outputPtr = _writeGenericEscape(ch, outputPtr); continue; } SerializableString esc = customEscapes.getEscapeSequence(ch); if (esc != null) { outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset); continue; } if (ch <= 0x7FF) { // fine, just needs 2 byte output outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6)); outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f)); } else { outputPtr = _outputMultiByteChar(ch, outputPtr); } } _outputTail = outputPtr; } private final int _writeCustomEscape(byte[] outputBuffer, int outputPtr, SerializableString esc, int remainingChars) throws IOException, JsonGenerationException { byte[] raw = esc.asUnquotedUTF8(); // must be escaped at this point, shouldn't double-quote int len = raw.length; if (len > 6) { // may violate constraints we have, do offline return _handleLongCustomEscape(outputBuffer, outputPtr, _outputEnd, raw, remainingChars); } // otherwise will fit without issues, so: System.arraycopy(raw, 0, outputBuffer, outputPtr, len); return (outputPtr + len); } private final int _handleLongCustomEscape(byte[] outputBuffer, int outputPtr, int outputEnd, byte[] raw, int remainingChars) throws IOException, JsonGenerationException { int len = raw.length; if ((outputPtr + len) > outputEnd) { _outputTail = outputPtr; _flushBuffer(); outputPtr = _outputTail; if (len > outputBuffer.length) { // very unlikely, but possible... _outputStream.write(raw, 0, len); return outputPtr; } System.arraycopy(raw, 0, outputBuffer, outputPtr, len); outputPtr += len; } // but is the invariant still obeyed? If not, flush once more if ((outputPtr + 6 * remainingChars) > outputEnd) { _flushBuffer(); return _outputTail; } return outputPtr; } /* /********************************************************** /* Internal methods, low-level writing, "raw UTF-8" segments /********************************************************** */ /** * Method called when UTF-8 encoded (but NOT yet escaped!) content is not guaranteed * to fit in the output buffer after escaping; as such, we just need to * chunk writes. */ private final void _writeUTF8Segments(byte[] utf8, int offset, int totalLen) throws IOException, JsonGenerationException { do { int len = Math.min(_outputMaxContiguous, totalLen); _writeUTF8Segment(utf8, offset, len); offset += len; totalLen -= len; } while (totalLen > 0); } private final void _writeUTF8Segment(byte[] utf8, final int offset, final int len) throws IOException, JsonGenerationException { // fast loop to see if escaping is needed; don't copy, just look final int[] escCodes = _outputEscapes; for (int ptr = offset, end = offset + len; ptr < end; ) { // 28-Feb-2011, tatu: escape codes just cover 7-bit range, so: int ch = utf8[ptr++]; if ((ch >= 0) && escCodes[ch] != 0) { _writeUTF8Segment2(utf8, offset, len); return; } } // yes, fine, just copy the sucker if ((_outputTail + len) > _outputEnd) { // enough room or need to flush? _flushBuffer(); // but yes once we flush (caller guarantees length restriction) } System.arraycopy(utf8, offset, _outputBuffer, _outputTail, len); _outputTail += len; } private final void _writeUTF8Segment2(final byte[] utf8, int offset, int len) throws IOException, JsonGenerationException { int outputPtr = _outputTail; // Ok: caller guarantees buffer can have room; but that may require flushing: if ((outputPtr + (len * 6)) > _outputEnd) { _flushBuffer(); outputPtr = _outputTail; } final byte[] outputBuffer = _outputBuffer; final int[] escCodes = _outputEscapes; len += offset; // so 'len' becomes 'end' while (offset < len) { byte b = utf8[offset++]; int ch = b; if (ch < 0 || escCodes[ch] == 0) { outputBuffer[outputPtr++] = b; continue; } int escape = escCodes[ch]; if (escape > 0) { // 2-char escape, fine outputBuffer[outputPtr++] = BYTE_BACKSLASH; outputBuffer[outputPtr++] = (byte) escape; } else { // ctrl-char, 6-byte escape... outputPtr = _writeGenericEscape(ch, outputPtr); } } _outputTail = outputPtr; } /* /********************************************************** /* Internal methods, low-level writing, base64 encoded /********************************************************** */ protected final void _writeBinary(Base64Variant b64variant, byte[] input, int inputPtr, final int inputEnd) throws IOException, JsonGenerationException { // Encoding is by chunks of 3 input, 4 output chars, so: int safeInputEnd = inputEnd - 3; // Let's also reserve room for possible (and quoted) lf char each round int safeOutputEnd = _outputEnd - 6; int chunksBeforeLF = b64variant.getMaxLineLength() >> 2; // Ok, first we loop through all full triplets of data: while (inputPtr <= safeInputEnd) { if (_outputTail > safeOutputEnd) { // need to flush _flushBuffer(); } // First, mash 3 bytes into lsb of 32-bit int int b24 = ((int) input[inputPtr++]) << 8; b24 |= ((int) input[inputPtr++]) & 0xFF; b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF); _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail); if (--chunksBeforeLF <= 0) { // note: must quote in JSON value _outputBuffer[_outputTail++] = '\\'; _outputBuffer[_outputTail++] = 'n'; chunksBeforeLF = b64variant.getMaxLineLength() >> 2; } } // And then we may have 1 or 2 leftover bytes to encode int inputLeft = inputEnd - inputPtr; // 0, 1 or 2 if (inputLeft > 0) { // yes, but do we have room for output? if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but... _flushBuffer(); } int b24 = ((int) input[inputPtr++]) << 16; if (inputLeft == 2) { b24 |= (((int) input[inputPtr++]) & 0xFF) << 8; } _outputTail = b64variant.encodeBase64Partial(b24, inputLeft, _outputBuffer, _outputTail); } } // write-method called when length is definitely known protected final int _writeBinary(Base64Variant b64variant, InputStream data, byte[] readBuffer, int bytesLeft) throws IOException, JsonGenerationException { int inputPtr = 0; int inputEnd = 0; int lastFullOffset = -3; // Let's also reserve room for possible (and quoted) LF char each round int safeOutputEnd = _outputEnd - 6; int chunksBeforeLF = b64variant.getMaxLineLength() >> 2; while (bytesLeft > 2) { // main loop for full triplets if (inputPtr > lastFullOffset) { inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft); inputPtr = 0; if (inputEnd < 3) { // required to try to read to have at least 3 bytes break; } lastFullOffset = inputEnd-3; } if (_outputTail > safeOutputEnd) { // need to flush _flushBuffer(); } int b24 = ((int) readBuffer[inputPtr++]) << 8; b24 |= ((int) readBuffer[inputPtr++]) & 0xFF; b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF); bytesLeft -= 3; _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail); if (--chunksBeforeLF <= 0) { _outputBuffer[_outputTail++] = '\\'; _outputBuffer[_outputTail++] = 'n'; chunksBeforeLF = b64variant.getMaxLineLength() >> 2; } } // And then we may have 1 or 2 leftover bytes to encode if (bytesLeft > 0) { inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft); inputPtr = 0; if (inputEnd > 0) { // yes, but do we have room for output? if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but... _flushBuffer(); } int b24 = ((int) readBuffer[inputPtr++]) << 16; int amount; if (inputPtr < inputEnd) { b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8; amount = 2; } else { amount = 1; } _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail); bytesLeft -= amount; } } return bytesLeft; } // write method when length is unknown protected final int _writeBinary(Base64Variant b64variant, InputStream data, byte[] readBuffer) throws IOException, JsonGenerationException { int inputPtr = 0; int inputEnd = 0; int lastFullOffset = -3; int bytesDone = 0; // Let's also reserve room for possible (and quoted) LF char each round int safeOutputEnd = _outputEnd - 6; int chunksBeforeLF = b64variant.getMaxLineLength() >> 2; // Ok, first we loop through all full triplets of data: while (true) { if (inputPtr > lastFullOffset) { // need to load more inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, readBuffer.length); inputPtr = 0; if (inputEnd < 3) { // required to try to read to have at least 3 bytes break; } lastFullOffset = inputEnd-3; } if (_outputTail > safeOutputEnd) { // need to flush _flushBuffer(); } // First, mash 3 bytes into lsb of 32-bit int int b24 = ((int) readBuffer[inputPtr++]) << 8; b24 |= ((int) readBuffer[inputPtr++]) & 0xFF; b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF); bytesDone += 3; _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail); if (--chunksBeforeLF <= 0) { _outputBuffer[_outputTail++] = '\\'; _outputBuffer[_outputTail++] = 'n'; chunksBeforeLF = b64variant.getMaxLineLength() >> 2; } } // And then we may have 1 or 2 leftover bytes to encode if (inputPtr < inputEnd) { // yes, but do we have room for output? if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but... _flushBuffer(); } int b24 = ((int) readBuffer[inputPtr++]) << 16; int amount = 1; if (inputPtr < inputEnd) { b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8; amount = 2; } bytesDone += amount; _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail); } return bytesDone; } private final int _readMore(InputStream in, byte[] readBuffer, int inputPtr, int inputEnd, int maxRead) throws IOException { // anything to shift to front? int i = 0; while (inputPtr < inputEnd) { readBuffer[i++] = readBuffer[inputPtr++]; } inputPtr = 0; inputEnd = i; maxRead = Math.min(maxRead, readBuffer.length); do { int length = maxRead - inputEnd; if (length == 0) { break; } int count = in.read(readBuffer, inputEnd, length); if (count < 0) { return inputEnd; } inputEnd += count; } while (inputEnd < 3); return inputEnd; } /* /********************************************************** /* Internal methods, character escapes/encoding /********************************************************** */ /** * Method called to output a character that is beyond range of * 1- and 2-byte UTF-8 encodings, when outputting "raw" * text (meaning it is not to be escaped or quoted) */ private final int _outputRawMultiByteChar(int ch, char[] cbuf, int inputOffset, int inputEnd) throws IOException { // Let's handle surrogates gracefully (as 4 byte output): if (ch >= SURR1_FIRST) { if (ch <= SURR2_LAST) { // yes, outside of BMP // Do we have second part? if (inputOffset >= inputEnd || cbuf == null) { // nope... have to note down _reportError(String.format( "Split surrogate on writeRaw() input (last character): first character 0x%4x", ch)); } _outputSurrogates(ch, cbuf[inputOffset]); return inputOffset+1; } } final byte[] bbuf = _outputBuffer; bbuf[_outputTail++] = (byte) (0xe0 | (ch >> 12)); bbuf[_outputTail++] = (byte) (0x80 | ((ch >> 6) & 0x3f)); bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f)); return inputOffset; } protected final void _outputSurrogates(int surr1, int surr2) throws IOException { int c = _decodeSurrogate(surr1, surr2); if ((_outputTail + 4) > _outputEnd) { _flushBuffer(); } final byte[] bbuf = _outputBuffer; bbuf[_outputTail++] = (byte) (0xf0 | (c >> 18)); bbuf[_outputTail++] = (byte) (0x80 | ((c >> 12) & 0x3f)); bbuf[_outputTail++] = (byte) (0x80 | ((c >> 6) & 0x3f)); bbuf[_outputTail++] = (byte) (0x80 | (c & 0x3f)); } /** * * @param ch * @param outputPtr Position within output buffer to append multi-byte in * * @return New output position after appending * * @throws IOException */ private final int _outputMultiByteChar(int ch, int outputPtr) throws IOException { byte[] bbuf = _outputBuffer; if (ch >= SURR1_FIRST && ch <= SURR2_LAST) { // yes, outside of BMP; add an escape // 23-Nov-2015, tatu: As per [core#223], may or may not want escapes; // it would be added here... but as things are, we do not have proper // access yet... // if (Feature.ESCAPE_UTF8_SURROGATES.enabledIn(_features)) { bbuf[outputPtr++] = BYTE_BACKSLASH; bbuf[outputPtr++] = BYTE_u; bbuf[outputPtr++] = HEX_CHARS[(ch >> 12) & 0xF]; bbuf[outputPtr++] = HEX_CHARS[(ch >> 8) & 0xF]; bbuf[outputPtr++] = HEX_CHARS[(ch >> 4) & 0xF]; bbuf[outputPtr++] = HEX_CHARS[ch & 0xF]; // } else { ... } } else { bbuf[outputPtr++] = (byte) (0xe0 | (ch >> 12)); bbuf[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f)); bbuf[outputPtr++] = (byte) (0x80 | (ch & 0x3f)); } return outputPtr; } private final void _writeNull() throws IOException { if ((_outputTail + 4) >= _outputEnd) { _flushBuffer(); } System.arraycopy(NULL_BYTES, 0, _outputBuffer, _outputTail, 4); _outputTail += 4; } /** * Method called to write a generic Unicode escape for given character. * * @param charToEscape Character to escape using escape sequence (\\uXXXX) */ private int _writeGenericEscape(int charToEscape, int outputPtr) throws IOException { final byte[] bbuf = _outputBuffer; bbuf[outputPtr++] = BYTE_BACKSLASH; bbuf[outputPtr++] = BYTE_u; if (charToEscape > 0xFF) { int hi = (charToEscape >> 8) & 0xFF; bbuf[outputPtr++] = HEX_CHARS[hi >> 4]; bbuf[outputPtr++] = HEX_CHARS[hi & 0xF]; charToEscape &= 0xFF; } else { bbuf[outputPtr++] = BYTE_0; bbuf[outputPtr++] = BYTE_0; } // We know it's a control char, so only the last 2 chars are non-0 bbuf[outputPtr++] = HEX_CHARS[charToEscape >> 4]; bbuf[outputPtr++] = HEX_CHARS[charToEscape & 0xF]; return outputPtr; } protected final void _flushBuffer() throws IOException { int len = _outputTail; if (len > 0) { _outputTail = 0; _outputStream.write(_outputBuffer, 0, len); } } } UTF8StreamJsonParser.java000066400000000000000000004027331356164247300340160ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.*; 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.InputStream} as the input source. */ public class UTF8StreamJsonParser extends ParserBase { final static byte BYTE_LF = (byte) '\n'; @SuppressWarnings("deprecation") private final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask(); @SuppressWarnings("deprecation") private final static int FEAT_MASK_LEADING_ZEROS = Feature.ALLOW_NUMERIC_LEADING_ZEROS.getMask(); @SuppressWarnings("deprecation") private final static int FEAT_MASK_NON_NUM_NUMBERS = Feature.ALLOW_NON_NUMERIC_NUMBERS.getMask(); @SuppressWarnings("deprecation") private final static int FEAT_MASK_ALLOW_MISSING = Feature.ALLOW_MISSING_VALUES.getMask(); private final static int FEAT_MASK_ALLOW_SINGLE_QUOTES = Feature.ALLOW_SINGLE_QUOTES.getMask(); private final static int FEAT_MASK_ALLOW_UNQUOTED_NAMES = Feature.ALLOW_UNQUOTED_FIELD_NAMES.getMask(); private final static int FEAT_MASK_ALLOW_JAVA_COMMENTS = Feature.ALLOW_COMMENTS.getMask(); private final static int FEAT_MASK_ALLOW_YAML_COMMENTS = Feature.ALLOW_YAML_COMMENTS.getMask(); // 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 * ObjectMapper, 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; /** * Value of {@link #_inputPtr} at the time when the first character of * name token was read. Used for calculating token location when requested; * combined with {@link #_currInputProcessed}, may be updated appropriately * as needed. * * @since 2.7 */ protected int _nameStartOffset; /** * @since 2.7 */ protected int _nameStartRow; /** * @since 2.7 */ protected int _nameStartCol; /* /********************************************************** /* Input buffering (from former 'StreamBasedParserBase') /********************************************************** */ protected InputStream _inputStream; /* /********************************************************** /* Current input data /********************************************************** */ /** * Current buffer from which data is read; generally data is read into * buffer from input source, but in some cases pre-loaded buffer * is handed to the parser. */ protected byte[] _inputBuffer; /** * Flag that indicates whether the input buffer is recycable (and * needs to be returned to recycler once we are done) or not. *

* If it is not, it also means that parser can NOT modify underlying * buffer. */ protected boolean _bufferRecyclable; /* /********************************************************** /* Life-cycle /********************************************************** */ /** * @deprecated Since 2.10 */ @Deprecated public UTF8StreamJsonParser(IOContext ctxt, int features, InputStream in, ObjectCodec codec, ByteQuadsCanonicalizer sym, byte[] inputBuffer, int start, int end, boolean bufferRecyclable) { this(ctxt, features, in, codec, sym, inputBuffer, start, end, 0, bufferRecyclable); } public UTF8StreamJsonParser(IOContext ctxt, int features, InputStream in, ObjectCodec codec, ByteQuadsCanonicalizer sym, byte[] inputBuffer, int start, int end, int bytesPreProcessed, boolean bufferRecyclable) { super(ctxt, features); _inputStream = in; _objectCodec = codec; _symbols = sym; _inputBuffer = inputBuffer; _inputPtr = start; _inputEnd = end; _currInputRowStart = start - bytesPreProcessed; // If we have offset, need to omit that from byte offset, so: _currInputProcessed = -start + bytesPreProcessed; _bufferRecyclable = bufferRecyclable; } @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 { int count = _inputEnd - _inputPtr; if (count < 1) { return 0; } // let's just advance ptr to end int origPtr = _inputPtr; out.write(_inputBuffer, origPtr, count); return count; } @Override public Object getInputSource() { return _inputStream; } /* /********************************************************** /* Overrides, low-level reading /********************************************************** */ protected final boolean _loadMore() throws IOException { final int bufSize = _inputEnd; if (_inputStream != null) { int space = _inputBuffer.length; if (space == 0) { // only occurs when we've been closed return false; } int count = _inputStream.read(_inputBuffer, 0, space); if (count > 0) { _inputPtr = 0; _inputEnd = count; _currInputProcessed += _inputEnd; _currInputRowStart -= _inputEnd; // 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; return true; } // End of input _closeInput(); // Should never return 0, so let's fail if (count == 0) { throw new IOException("InputStream.read() returned 0 characters when trying to read "+_inputBuffer.length+" bytes"); } } return false; } @Override protected void _closeInput() throws IOException { // We are not to call close() on the underlying InputStream // unless we "own" it, or auto-closing feature is enabled. if (_inputStream != null) { if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_SOURCE)) { _inputStream.close(); } _inputStream = null; } } /** * 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(); if (_bufferRecyclable) { byte[] buf = _inputBuffer; if (buf != null) { // Let's not set it to null; this way should get slightly more meaningful // error messages in case someone closes parser indirectly, without realizing. _inputBuffer = NO_BYTES; _ioContext.releaseReadIOBuffer(buf); } } } /* /********************************************************** /* 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 // 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 @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); } // @since 2.1 @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); } // since 2.6 @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); } // since 2.6 @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 != null) { // null only before/after document switch (_currToken.id()) { case ID_FIELD_NAME: return _parsingContext.getCurrentName().length(); 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.size(); default: 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 { if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = (int) _inputBuffer[_inputPtr++] & 0xFF; } 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 if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++] & 0xFF; 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 if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++] & 0xFF; bits = b64variant.decodeBase64Char(ch); // First branch: can get padding (-> 1 byte) if (bits < 0) { if (bits != Base64Variant.BASE64_VALUE_PADDING) { // as per [JACKSON-631], could also just be 'missing' padding if (ch == INT_QUOTE) { decodedData >>= 4; buffer[outputPtr++] = (byte) decodedData; if (b64variant.usesPadding()) { --_inputPtr; // to keep parser state bit more consistent _handleBase64MissingPadding(b64variant); } break; } bits = _decodeBase64Escape(b64variant, ch, 2); } if (bits == Base64Variant.BASE64_VALUE_PADDING) { // Ok, must get padding if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++] & 0xFF; if (!b64variant.usesPaddingChar(ch)) { if (_decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) { 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 if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++] & 0xFF; bits = b64variant.decodeBase64Char(ch); if (bits < 0) { if (bits != Base64Variant.BASE64_VALUE_PADDING) { // as per [JACKSON-631], could also just be 'missing' padding if (ch == INT_QUOTE) { decodedData >>= 2; buffer[outputPtr++] = (byte) (decodedData >> 8); buffer[outputPtr++] = (byte) decodedData; if (b64variant.usesPadding()) { --_inputPtr; // to keep parser state bit more consistent _handleBase64MissingPadding(b64variant); } 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 = _skipWSOrEnd(); if (i < 0) { // end-of-input // Close/release things like input source, symbol table and recyclable buffers close(); return (_currToken = null); } // clear any data retained so far _binaryValue = null; // Closing scope? if (i == INT_RBRACKET) { _closeArrayScope(); return (_currToken = JsonToken.END_ARRAY); } if (i == INT_RCURLY) { _closeObjectScope(); 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(); // Was that a trailing comma? if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { if ((i == INT_RBRACKET) || (i == INT_RCURLY)) { return _closeScope(i); } } } /* And should we now have a name? Always true for Object contexts * since the intermediate 'expect-value' state is never retained. */ if (!_parsingContext.inObject()) { _updateLocation(); return _nextTokenNotInObject(i); } // So first parse the field name itself: _updateNameLocation(); String n = _parseName(i); _parsingContext.setCurrentName(n); _currToken = JsonToken.FIELD_NAME; i = _skipColon(); _updateLocation(); // 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': _matchFalse(); t = JsonToken.VALUE_FALSE; break; case 'n': _matchNull(); t = JsonToken.VALUE_NULL; break; case 't': _matchTrue(); 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': _matchTrue(); return (_currToken = JsonToken.VALUE_TRUE); case 'f': _matchFalse(); return (_currToken = JsonToken.VALUE_FALSE); case 'n': _matchNull(); 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 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': 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; // !!! 16-Nov-2015, tatu: TODO: fix [databind#37], copy next location to current here // 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 /********************************************************** */ @Override public boolean nextFieldName(SerializableString str) throws IOException { // // // Note: most of code below is copied from nextToken() _numTypesValid = NR_UNKNOWN; if (_currToken == JsonToken.FIELD_NAME) { // can't have name right after name _nextAfterName(); return false; } if (_tokenIncomplete) { _skipString(); } int i = _skipWSOrEnd(); if (i < 0) { // end-of-input close(); _currToken = null; return false; } _binaryValue = null; // Closing scope? if (i == INT_RBRACKET) { _closeArrayScope(); _currToken = JsonToken.END_ARRAY; return false; } if (i == INT_RCURLY) { _closeObjectScope(); _currToken = JsonToken.END_OBJECT; return false; } // 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(); // Was that a trailing comma? if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { if ((i == INT_RBRACKET) || (i == INT_RCURLY)) { _closeScope(i); return false; } } } if (!_parsingContext.inObject()) { _updateLocation(); _nextTokenNotInObject(i); return false; } // // // This part differs, name parsing _updateNameLocation(); if (i == INT_QUOTE) { // when doing literal match, must consider escaping: byte[] nameBytes = str.asQuotedUTF8(); final int len = nameBytes.length; // 22-May-2014, tatu: Actually, let's require 4 more bytes for faster skipping // of colon that follows name if ((_inputPtr + len + 4) < _inputEnd) { // maybe... // first check length match by final int end = _inputPtr+len; if (_inputBuffer[end] == INT_QUOTE) { int offset = 0; int ptr = _inputPtr; while (true) { if (ptr == end) { // yes, match! _parsingContext.setCurrentName(str.getValue()); i = _skipColonFast(ptr+1); _isNextTokenNameYes(i); return true; } if (nameBytes[offset] != _inputBuffer[ptr]) { break; } ++offset; ++ptr; } } } } return _isNextTokenNameMaybe(i, str); } @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 = _skipWSOrEnd(); if (i < 0) { close(); _currToken = null; return null; } _binaryValue = null; if (i == INT_RBRACKET) { _closeArrayScope(); _currToken = JsonToken.END_ARRAY; return null; } if (i == INT_RCURLY) { _closeObjectScope(); _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(); // Was that a trailing comma? if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { if ((i == INT_RBRACKET) || (i == INT_RCURLY)) { _closeScope(i); return null; } } } if (!_parsingContext.inObject()) { _updateLocation(); _nextTokenNotInObject(i); return null; } _updateNameLocation(); final String nameStr = _parseName(i); _parsingContext.setCurrentName(nameStr); _currToken = JsonToken.FIELD_NAME; i = _skipColon(); _updateLocation(); 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': _matchFalse(); t = JsonToken.VALUE_FALSE; break; case 'n': _matchNull(); t = JsonToken.VALUE_NULL; break; case 't': _matchTrue(); 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; } // Variant called when we know there's at least 4 more bytes available private final int _skipColonFast(int ptr) throws IOException { int i = _inputBuffer[ptr++]; if (i == INT_COLON) { // common case, no leading space i = _inputBuffer[ptr++]; if (i > INT_SPACE) { // nor trailing if (i != INT_SLASH && i != INT_HASH) { _inputPtr = ptr; return i; } } else if (i == INT_SPACE || i == INT_TAB) { i = (int) _inputBuffer[ptr++]; if (i > INT_SPACE) { if (i != INT_SLASH && i != INT_HASH) { _inputPtr = ptr; return i; } } } _inputPtr = ptr-1; return _skipColon2(true); // true -> skipped colon } if (i == INT_SPACE || i == INT_TAB) { i = _inputBuffer[ptr++]; } if (i == INT_COLON) { i = _inputBuffer[ptr++]; if (i > INT_SPACE) { if (i != INT_SLASH && i != INT_HASH) { _inputPtr = ptr; return i; } } else if (i == INT_SPACE || i == INT_TAB) { i = (int) _inputBuffer[ptr++]; if (i > INT_SPACE) { if (i != INT_SLASH && i != INT_HASH) { _inputPtr = ptr; return i; } } } _inputPtr = ptr-1; return _skipColon2(true); } _inputPtr = ptr-1; return _skipColon2(false); } private final void _isNextTokenNameYes(int i) throws IOException { _currToken = JsonToken.FIELD_NAME; _updateLocation(); switch (i) { case '"': _tokenIncomplete = true; _nextToken = JsonToken.VALUE_STRING; return; case '[': _nextToken = JsonToken.START_ARRAY; return; case '{': _nextToken = JsonToken.START_OBJECT; return; case 't': _matchTrue(); _nextToken = JsonToken.VALUE_TRUE; return; case 'f': _matchFalse(); _nextToken = JsonToken.VALUE_FALSE; return; case 'n': _matchNull(); _nextToken = JsonToken.VALUE_NULL; return; case '-': _nextToken = _parseNegNumber(); return; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': _nextToken = _parsePosNumber(i); return; } _nextToken = _handleUnexpectedValue(i); } private final boolean _isNextTokenNameMaybe(int i, SerializableString str) throws IOException { // // // and this is back to standard nextToken() String n = _parseName(i); _parsingContext.setCurrentName(n); final boolean match = n.equals(str.getValue()); _currToken = JsonToken.FIELD_NAME; i = _skipColon(); _updateLocation(); // 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 match; } JsonToken t; switch (i) { case '[': t = JsonToken.START_ARRAY; break; case '{': t = JsonToken.START_OBJECT; break; case 't': _matchTrue(); t = JsonToken.VALUE_TRUE; break; case 'f': _matchFalse(); t = JsonToken.VALUE_FALSE; break; case 'n': _matchNull(); t = JsonToken.VALUE_NULL; break; 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; default: t = _handleUnexpectedValue(i); } _nextToken = t; return match; } @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; } // !!! TODO: optimize this case as well 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; } // !!! TODO: optimize this case as well 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; } // !!! TODO: optimize this case as well 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. *

* 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(); // One special case: if first char is 0, must not be followed by a digit if (c == INT_0) { c = _verifyNoLeadingZeroes(); } // Ok: we can first just add digit we saw first: outBuf[0] = (char) c; int intLen = 1; int outPtr = 1; // And then figure out how far we can read without further checks // for either input or output final int end = Math.min(_inputEnd, _inputPtr + outBuf.length - 1); // 1 == outPtr // With this, we have a nice and tight loop: while (true) { if (_inputPtr >= end) { // split across boundary, offline return _parseNumber2(outBuf, outPtr, false, intLen); } c = (int) _inputBuffer[_inputPtr++] & 0xFF; if (c < INT_0 || c > INT_9) { break; } ++intLen; outBuf[outPtr++] = (char) c; } if (c == INT_PERIOD || c == INT_e || c == INT_E) { return _parseFloat(outBuf, outPtr, c, false, intLen); } --_inputPtr; // to push back trailing char (comma etc) _textBuffer.setCurrentLength(outPtr); // As per #105, need separating space between root values; check here if (_parsingContext.inRoot()) { _verifyRootSpace(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++] = '-'; // Must have something after sign too if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } int c = (int) _inputBuffer[_inputPtr++] & 0xFF; // Note: must be followed by a digit if (c <= INT_0) { // One special case: if first char is 0, must not be followed by a digit if (c != INT_0) { return _handleInvalidNumberStart(c, true); } c = _verifyNoLeadingZeroes(); } else if (c > INT_9) { return _handleInvalidNumberStart(c, true); } // Ok: we can first just add digit we saw first: outBuf[outPtr++] = (char) c; int intLen = 1; // And then figure out how far we can read without further checks // for either input or output final int end = Math.min(_inputEnd, _inputPtr + outBuf.length - outPtr); // With this, we have a nice and tight loop: while (true) { if (_inputPtr >= end) { // Long enough to be split across boundary, so: return _parseNumber2(outBuf, outPtr, true, intLen); } c = (int) _inputBuffer[_inputPtr++] & 0xFF; if (c < INT_0 || c > INT_9) { break; } ++intLen; outBuf[outPtr++] = (char) c; } if (c == INT_PERIOD || c == INT_e || c == INT_E) { return _parseFloat(outBuf, outPtr, c, true, intLen); } --_inputPtr; // to push back trailing char (comma etc) _textBuffer.setCurrentLength(outPtr); // As per #105, need separating space between root values; check here if (_parsingContext.inRoot()) { _verifyRootSpace(c); } // And there we have it! return resetInt(true, intLen); } /** * Method called to handle parsing when input is split across buffer boundary * (or output is longer than segment used to store it) */ private final JsonToken _parseNumber2(char[] outBuf, int outPtr, boolean negative, int intPartLength) throws IOException { // Ok, parse the rest while (true) { if (_inputPtr >= _inputEnd && !_loadMore()) { _textBuffer.setCurrentLength(outPtr); return resetInt(negative, intPartLength); } int c = (int) _inputBuffer[_inputPtr++] & 0xFF; if (c > INT_9 || c < INT_0) { if (c == INT_PERIOD || c == INT_e || c == INT_E) { return _parseFloat(outBuf, outPtr, c, negative, intPartLength); } break; } if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } outBuf[outPtr++] = (char) c; ++intPartLength; } --_inputPtr; // to push back trailing char (comma etc) _textBuffer.setCurrentLength(outPtr); // As per #105, need separating space between root values; check here if (_parsingContext.inRoot()) { _verifyRootSpace(_inputBuffer[_inputPtr] & 0xFF); } // And there we have it! return resetInt(negative, intPartLength); } /** * Method called when we have seen one zero, and want to ensure * it is not followed by another */ private final int _verifyNoLeadingZeroes() throws IOException { // Ok to have plain "0" if (_inputPtr >= _inputEnd && !_loadMore()) { return INT_0; } int ch = _inputBuffer[_inputPtr] & 0xFF; // if not followed by a number (probably '.'); return zero as is, to be included if (ch < INT_0 || ch > INT_9) { return INT_0; } // [JACKSON-358]: we may want to allow them, after all... if ((_features & FEAT_MASK_LEADING_ZEROS) == 0) { 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) ++_inputPtr; // Leading zero to be skipped if (ch == INT_0) { 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; } ++_inputPtr; // skip previous zeroes if (ch != INT_0) { // followed by other number; return break; } } } return ch; } private final JsonToken _parseFloat(char[] outBuf, int outPtr, int c, boolean negative, int integerPartLength) throws IOException { int fractLen = 0; boolean eof = false; // And then see if we get other parts if (c == INT_PERIOD) { // yes, fraction if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } outBuf[outPtr++] = (char) c; fract_loop: while (true) { if (_inputPtr >= _inputEnd && !_loadMore()) { eof = true; break fract_loop; } c = (int) _inputBuffer[_inputPtr++] & 0xFF; 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; // Not optional, can require that we get one more char if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } c = (int) _inputBuffer[_inputPtr++] & 0xFF; // Sign indicator? if (c == '-' || c == '+') { if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } outBuf[outPtr++] = (char) c; // Likewise, non optional: if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } c = (int) _inputBuffer[_inputPtr++] & 0xFF; } exp_loop: while (c >= INT_0 && c <= INT_9) { ++expLen; if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } outBuf[outPtr++] = (char) c; if (_inputPtr >= _inputEnd && !_loadMore()) { eof = true; break exp_loop; } c = (int) _inputBuffer[_inputPtr++] & 0xFF; } // 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 if (!eof) { --_inputPtr; // As per [core#105], need separating space between root values; check here if (_parsingContext.inRoot()) { _verifyRootSpace(c); } } _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. *

* NOTE: caller MUST ensure there is at least one character available; * and that input pointer is AT given char (not past) */ private final void _verifyRootSpace(int ch) throws IOException { // caller had pushed it back, before calling; reset ++_inputPtr; // TODO? Handle UTF-8 char decoding for error reporting switch (ch) { case ' ': case '\t': return; case '\r': _skipCR(); return; case '\n': ++_currInputRow; _currInputRowStart = _inputPtr; return; } _reportMissingRootWS(ch); } /* /********************************************************** /* Internal methods, secondary parsing /********************************************************** */ protected final String _parseName(int i) throws IOException { if (i != INT_QUOTE) { return _handleOddName(i); } // First: can we optimize out bounds checks? if ((_inputPtr + 13) > _inputEnd) { // Need up to 12 chars, plus one trailing (quote) return slowParseName(); } // 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 byte[] input = _inputBuffer; final int[] codes = _icLatin1; int q = input[_inputPtr++] & 0xFF; if (codes[q] == 0) { i = input[_inputPtr++] & 0xFF; if (codes[i] == 0) { q = (q << 8) | i; i = input[_inputPtr++] & 0xFF; if (codes[i] == 0) { q = (q << 8) | i; i = input[_inputPtr++] & 0xFF; if (codes[i] == 0) { q = (q << 8) | i; i = input[_inputPtr++] & 0xFF; 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 } protected final String parseMediumName(int q2) throws IOException { final byte[] input = _inputBuffer; final int[] codes = _icLatin1; // Ok, got 5 name bytes so far int i = input[_inputPtr++] & 0xFF; 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 = input[_inputPtr++] & 0xFF; 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 = input[_inputPtr++] & 0xFF; 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 = input[_inputPtr++] & 0xFF; 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); } /** * @since 2.6 */ protected final String parseMediumName2(int q3, final int q2) throws IOException { final byte[] input = _inputBuffer; final int[] codes = _icLatin1; // Got 9 name bytes so far int i = input[_inputPtr++] & 0xFF; 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 = input[_inputPtr++] & 0xFF; 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 = input[_inputPtr++] & 0xFF; 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 = input[_inputPtr++] & 0xFF; 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); } protected 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 byte[] input = _inputBuffer; final int[] codes = _icLatin1; int qlen = 3; while ((_inputPtr + 4) <= _inputEnd) { int i = input[_inputPtr++] & 0xFF; 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 = input[_inputPtr++] & 0xFF; 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 = input[_inputPtr++] & 0xFF; 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 = input[_inputPtr++] & 0xFF; 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; } /* Let's offline if we hit buffer boundary (otherwise would * need to [try to] align input, which is bit complicated * and may not always be possible) */ return parseEscapedName(_quadBuffer, qlen, 0, q, 0); } /** * Method called when not even first 8 bytes are guaranteed * to come consecutively. Happens rarely, so this is offlined; * plus we'll also do full checks for escaping etc. */ protected String slowParseName() throws IOException { if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOF(": was expecting closing '\"' for name", JsonToken.FIELD_NAME); } } int i = _inputBuffer[_inputPtr++] & 0xFF; if (i == INT_QUOTE) { // special case, "" return ""; } return parseEscapedName(_quadBuffer, 0, 0, i, 0); } 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 hence is offlined to a separate method. */ protected final String parseEscapedName(int[] quads, int qlen, int currQuad, int ch, int currQuadBytes) throws IOException { // 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; } if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOF(" in field name", JsonToken.FIELD_NAME); } } ch = _inputBuffer[_inputPtr++] & 0xFF; } if (currQuadBytes > 0) { if (qlen >= quads.length) { _quadBuffer = quads = growArrayBy(quads, quads.length); } quads[qlen++] = _padLastQuad(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 { // First: may allow single quotes if (ch == INT_APOS && (_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { return _parseAposName(); } // Allow unquoted names if feature enabled: if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) { 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; } if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOF(" in field name", JsonToken.FIELD_NAME); } } ch = _inputBuffer[_inputPtr] & 0xFF; if (codes[ch] != 0) { break; } ++_inputPtr; } 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 support [JACKSON-173]. 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 { if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOF(": was expecting closing '\'' for field name", JsonToken.FIELD_NAME); } } int ch = _inputBuffer[_inputPtr++] & 0xFF; if (ch == INT_APOS) { // 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 == INT_APOS) { break; } // additional check to skip handling of double-quotes if ((codes[ch] != 0) && (ch != '"')) { if (ch != '\\') { // Unquoted white space? // As per [JACKSON-208], call can now return: _throwUnquotedSpace(ch, "name"); } else { // Nope, escape sequence ch = _decodeEscaped(); } // as per main code, inefficient but will have to do 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; } if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOF(" in field name", JsonToken.FIELD_NAME); } } ch = _inputBuffer[_inputPtr++] & 0xFF; } if (currQuadBytes > 0) { if (qlen >= quads.length) { _quadBuffer = quads = growArrayBy(quads, quads.length); } quads[qlen++] = _padLastQuad(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 = _padLastQuad(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 = _padLastQuad(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 = _padLastQuad(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] = _padLastQuad(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++] = _padLastQuad(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); } /** * Helper method needed to fix [jackson-core#148], masking of 0x00 character */ private final static int _padLastQuad(int q, int bytes) { return (bytes == 4) ? q : (q | (-1 << (bytes << 3))); } /* /********************************************************** /* Internal methods, String value parsing /********************************************************** */ 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(); ptr = _inputPtr; } int outPtr = 0; char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); final int[] codes = _icUTF8; final int max = Math.min(_inputEnd, (ptr + outBuf.length)); final byte[] inputBuffer = _inputBuffer; while (ptr < max) { int c = (int) inputBuffer[ptr] & 0xFF; if (codes[c] != 0) { if (c == INT_QUOTE) { _inputPtr = ptr+1; _textBuffer.setCurrentLength(outPtr); return; } break; } ++ptr; outBuf[outPtr++] = (char) c; } _inputPtr = ptr; _finishString2(outBuf, outPtr); } /** * @since 2.6 */ protected String _finishAndReturnString() throws IOException { // First, single tight loop for ASCII content, not split across input buffer boundary: int ptr = _inputPtr; if (ptr >= _inputEnd) { _loadMoreGuaranteed(); ptr = _inputPtr; } int outPtr = 0; char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); final int[] codes = _icUTF8; final int max = Math.min(_inputEnd, (ptr + outBuf.length)); final byte[] inputBuffer = _inputBuffer; while (ptr < max) { int c = (int) inputBuffer[ptr] & 0xFF; if (codes[c] != 0) { if (c == INT_QUOTE) { _inputPtr = ptr+1; return _textBuffer.setCurrentAndReturn(outPtr); } break; } ++ptr; outBuf[outPtr++] = (char) c; } _inputPtr = ptr; _finishString2(outBuf, outPtr); return _textBuffer.contentsAsString(); } private final void _finishString2(char[] outBuf, int outPtr) throws IOException { int c; // Here we do want to do full decoding, hence: final int[] codes = _icUTF8; final byte[] inputBuffer = _inputBuffer; main_loop: while (true) { // Then the tight ASCII non-funny-char loop: ascii_loop: while (true) { int ptr = _inputPtr; if (ptr >= _inputEnd) { _loadMoreGuaranteed(); ptr = _inputPtr; } if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } final int max = Math.min(_inputEnd, (ptr + (outBuf.length - outPtr))); while (ptr < max) { c = (int) inputBuffer[ptr++] & 0xFF; if (codes[c] != 0) { _inputPtr = ptr; break ascii_loop; } outBuf[outPtr++] = (char) c; } _inputPtr = ptr; } // 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 if ((_inputEnd - _inputPtr) >= 2) { c = _decodeUtf8_3fast(c); } else { 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) { // As per [JACKSON-208], call can now return: _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; } // 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; final byte[] inputBuffer = _inputBuffer; main_loop: while (true) { int c; ascii_loop: while (true) { int ptr = _inputPtr; int max = _inputEnd; if (ptr >= max) { _loadMoreGuaranteed(); ptr = _inputPtr; max = _inputEnd; } while (ptr < max) { c = (int) inputBuffer[ptr++] & 0xFF; if (codes[c] != 0) { _inputPtr = ptr; break ascii_loop; } } _inputPtr = ptr; } // 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(c); 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) { /* This check proceeds only if `Feature.ALLOW_MISSING_VALUES` is enabled; * it is for missing values. In case 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 parsing continues. * 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 ((_features & FEAT_MASK_ALLOW_MISSING) != 0) { --_inputPtr; 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 ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { return _handleApos(); } break; case 'N': _matchToken("NaN", 1); if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { 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 ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { 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 if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOFInValue(JsonToken.VALUE_NUMBER_INT); } } return _handleInvalidNumberStart(_inputBuffer[_inputPtr++] & 0xFF, false); } // [core#77] Try to decode most likely token if (Character.isJavaIdentifierStart(c)) { _reportInvalidToken(""+((char) c), _validJsonTokenList()); } // but if it doesn't look like a token: _reportUnexpectedChar(c, "expected a valid value "+_validJsonValueList()); 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; final byte[] inputBuffer = _inputBuffer; main_loop: while (true) { // Then the tight ascii non-funny-char loop: ascii_loop: while (true) { if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } int max = _inputEnd; { int max2 = _inputPtr + (outBuf.length - outPtr); if (max2 < max) { max = max2; } } while (_inputPtr < max) { c = (int) inputBuffer[_inputPtr++] & 0xFF; if (c == INT_APOS || codes[c] != 0) { break ascii_loop; } outBuf[outPtr++] = (char) c; } } // Ok: end marker, escape or multi-byte? if (c == INT_APOS) { 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 if ((_inputEnd - _inputPtr) >= 2) { c = _decodeUtf8_3fast(c); } else { 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; } /* /********************************************************** /* Internal methods, well-known token decoding /********************************************************** */ /** * 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') { if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOFInValue(JsonToken.VALUE_NUMBER_FLOAT); // possibly? } } ch = _inputBuffer[_inputPtr++]; String match; if (ch == 'N') { match = neg ? "-INF" :"+INF"; } else if (ch == 'n') { match = neg ? "-Infinity" :"+Infinity"; } else { break; } _matchToken(match, 3); if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) { return resetAsNaN(match, neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY); } _reportError("Non-standard token '%s': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow", match); } reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value"); return null; } // NOTE: first character already decoded protected final void _matchTrue() throws IOException { int ptr = _inputPtr; if ((ptr + 3) < _inputEnd) { byte[] buf = _inputBuffer; if ((buf[ptr++] == 'r') && (buf[ptr++] == 'u') && (buf[ptr++] == 'e')) { int ch = buf[ptr] & 0xFF; if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars _inputPtr = ptr; return; } } } _matchToken2("true", 1); } protected final void _matchFalse() throws IOException { int ptr = _inputPtr; if ((ptr + 4) < _inputEnd) { byte[] buf = _inputBuffer; if ((buf[ptr++] == 'a') && (buf[ptr++] == 'l') && (buf[ptr++] == 's') && (buf[ptr++] == 'e')) { int ch = buf[ptr] & 0xFF; if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars _inputPtr = ptr; return; } } } _matchToken2("false", 1); } protected final void _matchNull() throws IOException { int ptr = _inputPtr; if ((ptr + 3) < _inputEnd) { byte[] buf = _inputBuffer; if ((buf[ptr++] == 'u') && (buf[ptr++] == 'l') && (buf[ptr++] == 'l')) { int ch = buf[ptr] & 0xFF; if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars _inputPtr = ptr; return; } } } _matchToken2("null", 1); } protected final void _matchToken(String matchStr, int i) throws IOException { final int len = matchStr.length(); if ((_inputPtr + len) >= _inputEnd) { _matchToken2(matchStr, i); return; } do { if (_inputBuffer[_inputPtr] != matchStr.charAt(i)) { _reportInvalidToken(matchStr.substring(0, i)); } ++_inputPtr; } while (++i < len); int ch = _inputBuffer[_inputPtr] & 0xFF; if (ch >= '0' && ch != ']' && ch != '}') { // expected/allowed chars _checkMatchEnd(matchStr, i, ch); } } private final void _matchToken2(String matchStr, int i) throws IOException { final int len = matchStr.length(); do { if (((_inputPtr >= _inputEnd) && !_loadMore()) || (_inputBuffer[_inputPtr] != matchStr.charAt(i))) { _reportInvalidToken(matchStr.substring(0, i)); } ++_inputPtr; } while (++i < len); // but let's also ensure we either get EOF, or non-alphanum char... if (_inputPtr >= _inputEnd && !_loadMore()) { return; } int ch = _inputBuffer[_inputPtr] & 0xFF; if (ch >= '0' && ch != ']' && ch != '}') { // expected/allowed chars _checkMatchEnd(matchStr, i, 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(matchStr.substring(0, i)); } } /* /********************************************************** /* Internal methods, ws skipping, escape/unescape /********************************************************** */ private final int _skipWS() throws IOException { while (_inputPtr < _inputEnd) { int i = _inputBuffer[_inputPtr++] & 0xFF; if (i > INT_SPACE) { if (i == INT_SLASH || i == INT_HASH) { --_inputPtr; return _skipWS2(); } return i; } if (i != INT_SPACE) { if (i == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } return _skipWS2(); } private final int _skipWS2() throws IOException { while (_inputPtr < _inputEnd || _loadMore()) { int i = _inputBuffer[_inputPtr++] & 0xFF; if (i > INT_SPACE) { if (i == INT_SLASH) { _skipComment(); continue; } if (i == INT_HASH) { if (_skipYAMLComment()) { continue; } } return i; } if (i != INT_SPACE) { if (i == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } throw _constructError("Unexpected end-of-input within/between "+_parsingContext.typeDesc()+" entries"); } private final int _skipWSOrEnd() throws IOException { // 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()) { return _eofAsNextChar(); } } int i = _inputBuffer[_inputPtr++] & 0xFF; if (i > INT_SPACE) { if (i == INT_SLASH || i == INT_HASH) { --_inputPtr; return _skipWSOrEnd2(); } return i; } if (i != INT_SPACE) { if (i == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } while (_inputPtr < _inputEnd) { i = _inputBuffer[_inputPtr++] & 0xFF; if (i > INT_SPACE) { if (i == INT_SLASH || i == INT_HASH) { --_inputPtr; return _skipWSOrEnd2(); } return i; } if (i != INT_SPACE) { if (i == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } return _skipWSOrEnd2(); } private final int _skipWSOrEnd2() throws IOException { while ((_inputPtr < _inputEnd) || _loadMore()) { int i = _inputBuffer[_inputPtr++] & 0xFF; if (i > INT_SPACE) { if (i == INT_SLASH) { _skipComment(); continue; } if (i == INT_HASH) { if (_skipYAMLComment()) { continue; } } return i; } else if (i != INT_SPACE) { if (i == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } // We ran out of input... return _eofAsNextChar(); } private final int _skipColon() throws IOException { if ((_inputPtr + 4) >= _inputEnd) { return _skipColon2(false); } // Fast path: colon with optional single-space/tab before and/or after: int i = _inputBuffer[_inputPtr]; if (i == INT_COLON) { // common case, no leading space i = _inputBuffer[++_inputPtr]; if (i > INT_SPACE) { // nor trailing if (i == INT_SLASH || i == INT_HASH) { return _skipColon2(true); } ++_inputPtr; return i; } if (i == INT_SPACE || i == INT_TAB) { i = (int) _inputBuffer[++_inputPtr]; if (i > INT_SPACE) { if (i == INT_SLASH || i == INT_HASH) { return _skipColon2(true); } ++_inputPtr; return i; } } return _skipColon2(true); // true -> skipped colon } if (i == INT_SPACE || i == INT_TAB) { i = _inputBuffer[++_inputPtr]; } if (i == INT_COLON) { i = _inputBuffer[++_inputPtr]; if (i > INT_SPACE) { if (i == INT_SLASH || i == INT_HASH) { return _skipColon2(true); } ++_inputPtr; return i; } if (i == INT_SPACE || i == INT_TAB) { i = (int) _inputBuffer[++_inputPtr]; if (i > INT_SPACE) { if (i == INT_SLASH || i == INT_HASH) { return _skipColon2(true); } ++_inputPtr; return i; } } return _skipColon2(true); } return _skipColon2(false); } private final int _skipColon2(boolean gotColon) throws IOException { while (_inputPtr < _inputEnd || _loadMore()) { int i = _inputBuffer[_inputPtr++] & 0xFF; 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 if (i != INT_SPACE) { if (i == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } _reportInvalidEOF(" within/between "+_parsingContext.typeDesc()+" entries", null); return -1; } private final void _skipComment() throws IOException { if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) { _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", null); } int c = _inputBuffer[_inputPtr++] & 0xFF; if (c == INT_SLASH) { _skipLine(); } else if (c == INT_ASTERISK) { _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(); // Ok: need the matching '*/' main_loop: while ((_inputPtr < _inputEnd) || _loadMore()) { int i = (int) _inputBuffer[_inputPtr++] & 0xFF; int code = codes[i]; if (code != 0) { switch (code) { case '*': if (_inputPtr >= _inputEnd && !_loadMore()) { break main_loop; } if (_inputBuffer[_inputPtr] == INT_SLASH) { ++_inputPtr; return; } break; case INT_LF: ++_currInputRow; _currInputRowStart = _inputPtr; break; case INT_CR: _skipCR(); break; case 2: // 2-byte UTF _skipUtf8_2(); break; case 3: // 3-byte UTF _skipUtf8_3(); break; case 4: // 4-byte UTF _skipUtf8_4(i); break; default: // e.g. -1 // Is this good enough error message? _reportInvalidChar(i); } } } _reportInvalidEOF(" in a comment", null); } private final boolean _skipYAMLComment() throws IOException { if ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) == 0) { 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 ((_inputPtr < _inputEnd) || _loadMore()) { int i = (int) _inputBuffer[_inputPtr++] & 0xFF; int code = codes[i]; if (code != 0) { switch (code) { case INT_LF: ++_currInputRow; _currInputRowStart = _inputPtr; return; case INT_CR: _skipCR(); 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(i); break; default: // e.g. -1 if (code < 0) { // Is this good enough error message? _reportInvalidChar(i); } } } } } @Override protected char _decodeEscaped() throws IOException { if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOF(" in character escape sequence", JsonToken.VALUE_STRING); } } int c = (int) _inputBuffer[_inputPtr++]; 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) { if (_inputPtr >= _inputEnd) { if (!_loadMore()) { _reportInvalidEOF(" in character escape sequence", JsonToken.VALUE_STRING); } } int ch = _inputBuffer[_inputPtr++]; int digit = CharTypes.charToHex(ch); if (digit < 0) { _reportUnexpectedChar(ch & 0xFF, "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 = nextByte(); if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF); } c = (c << 6) | (d & 0x3F); if (needed > 1) { // needed == 1 means 2 bytes total d = nextByte(); // 3rd byte if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF); } c = (c << 6) | (d & 0x3F); if (needed > 2) { // 4 bytes? (need surrogates) d = nextByte(); 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 { if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } int d = (int) _inputBuffer[_inputPtr++]; if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF, _inputPtr); } return ((c & 0x1F) << 6) | (d & 0x3F); } private final int _decodeUtf8_3(int c1) throws IOException { if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } c1 &= 0x0F; int d = (int) _inputBuffer[_inputPtr++]; if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF, _inputPtr); } int c = (c1 << 6) | (d & 0x3F); if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } d = (int) _inputBuffer[_inputPtr++]; if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF, _inputPtr); } c = (c << 6) | (d & 0x3F); return c; } private final int _decodeUtf8_3fast(int c1) throws IOException { c1 &= 0x0F; int d = (int) _inputBuffer[_inputPtr++]; if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF, _inputPtr); } int c = (c1 << 6) | (d & 0x3F); d = (int) _inputBuffer[_inputPtr++]; if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF, _inputPtr); } c = (c << 6) | (d & 0x3F); return c; } /** * @return Character value minus 0x10000; this so that caller * can readily expand it to actual surrogates */ private final int _decodeUtf8_4(int c) throws IOException { if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } int d = (int) _inputBuffer[_inputPtr++]; if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF, _inputPtr); } c = ((c & 0x07) << 6) | (d & 0x3F); if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } d = (int) _inputBuffer[_inputPtr++]; if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF, _inputPtr); } c = (c << 6) | (d & 0x3F); if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } d = (int) _inputBuffer[_inputPtr++]; if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF, _inputPtr); } /* 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 { if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } int c = (int) _inputBuffer[_inputPtr++]; if ((c & 0xC0) != 0x080) { _reportInvalidOther(c & 0xFF, _inputPtr); } } /* Alas, can't heavily optimize skipping, since we still have to * do validity checks... */ private final void _skipUtf8_3() throws IOException { if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } //c &= 0x0F; int c = (int) _inputBuffer[_inputPtr++]; if ((c & 0xC0) != 0x080) { _reportInvalidOther(c & 0xFF, _inputPtr); } if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } c = (int) _inputBuffer[_inputPtr++]; if ((c & 0xC0) != 0x080) { _reportInvalidOther(c & 0xFF, _inputPtr); } } private final void _skipUtf8_4(int c) throws IOException { if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } int d = (int) _inputBuffer[_inputPtr++]; if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF, _inputPtr); } if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } d = (int) _inputBuffer[_inputPtr++]; if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF, _inputPtr); } if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } d = (int) _inputBuffer[_inputPtr++]; if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF, _inputPtr); } } /* /********************************************************** /* Internal methods, input loading /********************************************************** */ /** * We actually need to check the character value here * (to see if we have \n following \r). */ protected final void _skipCR() throws IOException { if (_inputPtr < _inputEnd || _loadMore()) { if (_inputBuffer[_inputPtr] == BYTE_LF) { ++_inputPtr; } } ++_currInputRow; _currInputRowStart = _inputPtr; } private int nextByte() throws IOException { if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } return _inputBuffer[_inputPtr++] & 0xFF; } /* /********************************************************** /* Internal methods, error reporting /********************************************************** */ protected void _reportInvalidToken(String matchedPart, int ptr) throws IOException { _inputPtr = ptr; _reportInvalidToken(matchedPart, _validJsonTokenList()); } protected void _reportInvalidToken(String matchedPart) throws IOException { _reportInvalidToken(matchedPart, _validJsonTokenList()); } protected void _reportInvalidToken(String matchedPart, String msg) throws IOException { /* 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). */ StringBuilder sb = new StringBuilder(matchedPart); while ((_inputPtr < _inputEnd) || _loadMore()) { int i = (int) _inputBuffer[_inputPtr++]; char c = (char) _decodeCharForError(i); if (!Character.isJavaIdentifierPart(c)) { // 11-Jan-2016, tatu: note: we will fully consume the character, // included or not, so if recovery was possible, it'd be off-by-one... break; } sb.append(c); if (sb.length() >= MAX_ERROR_TOKEN_LENGTH) { sb.append("..."); break; } } _reportError("Unrecognized token '%s': was expecting %s", sb, 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)); } protected void _reportInvalidOther(int mask) throws JsonParseException { _reportError("Invalid UTF-8 middle byte 0x"+Integer.toHexString(mask)); } protected void _reportInvalidOther(int mask, int ptr) throws JsonParseException { _inputPtr = ptr; _reportInvalidOther(mask); } /* /********************************************************** /* 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(); while (true) { // first, we'll skip preceding white space, if any int ch; do { if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = (int) _inputBuffer[_inputPtr++] & 0xFF; } 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 if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++] & 0xFF; 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 if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++] & 0xFF; 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 == INT_QUOTE) { decodedData >>= 4; builder.append(decodedData); if (b64variant.usesPadding()) { --_inputPtr; // to keep parser state bit more consistent _handleBase64MissingPadding(b64variant); } return builder.toByteArray(); } bits = _decodeBase64Escape(b64variant, ch, 2); } if (bits == Base64Variant.BASE64_VALUE_PADDING) { // Ok, must get padding if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++] & 0xFF; if (!b64variant.usesPaddingChar(ch)) { if (_decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) { 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 if (_inputPtr >= _inputEnd) { _loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++] & 0xFF; bits = b64variant.decodeBase64Char(ch); if (bits < 0) { if (bits != Base64Variant.BASE64_VALUE_PADDING) { // could also just be 'missing' padding if (ch == INT_QUOTE) { decodedData >>= 2; builder.appendTwoBytes(decodedData); if (b64variant.usesPadding()) { --_inputPtr; // to keep parser state bit more consistent _handleBase64MissingPadding(b64variant); } 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) /********************************************************** */ // As per [core#108], must ensure we call the right method @Override public JsonLocation getTokenLocation() { if (_currToken == JsonToken.FIELD_NAME) { long total = _currInputProcessed + (_nameStartOffset-1); return new JsonLocation(_getSourceReference(), total, -1L, _nameStartRow, _nameStartCol); } return new JsonLocation(_getSourceReference(), _tokenInputTotal-1, -1L, _tokenInputRow, _tokenInputCol); } // As per [core#108], must ensure we call the right method @Override public JsonLocation getCurrentLocation() { int col = _inputPtr - _currInputRowStart + 1; // 1-based return new JsonLocation(_getSourceReference(), _currInputProcessed + _inputPtr, -1L, // bytes, chars _currInputRow, col); } // @since 2.7 private final void _updateLocation() { _tokenInputRow = _currInputRow; final int ptr = _inputPtr; _tokenInputTotal = _currInputProcessed + ptr; _tokenInputCol = ptr - _currInputRowStart; } // @since 2.7 private final void _updateNameLocation() { _nameStartRow = _currInputRow; final int ptr = _inputPtr; _nameStartOffset = ptr; _nameStartCol = ptr - _currInputRowStart; } /* /********************************************************** /* Internal methods, other /********************************************************** */ private final JsonToken _closeScope(int i) throws JsonParseException { if (i == INT_RCURLY) { _closeObjectScope(); return (_currToken = JsonToken.END_OBJECT); } _closeArrayScope(); return (_currToken = JsonToken.END_ARRAY); } private final void _closeArrayScope() throws JsonParseException { _updateLocation(); if (!_parsingContext.inArray()) { _reportMismatchedEndMarker(']', '}'); } _parsingContext = _parsingContext.clearAndGetParent(); } private final void _closeObjectScope() throws JsonParseException { _updateLocation(); if (!_parsingContext.inObject()) { _reportMismatchedEndMarker('}', ']'); } _parsingContext = _parsingContext.clearAndGetParent(); } } WriterBasedJsonGenerator.java000066400000000000000000002034051356164247300350140ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.*; import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.CharTypes; import com.fasterxml.jackson.core.io.CharacterEscapes; import com.fasterxml.jackson.core.io.IOContext; import com.fasterxml.jackson.core.io.NumberOutput; /** * {@link JsonGenerator} that outputs JSON content using a {@link java.io.Writer} * which handles character encoding. */ public class WriterBasedJsonGenerator extends JsonGeneratorImpl { final protected static int SHORT_WRITE = 32; final protected static char[] HEX_CHARS = CharTypes.copyHexChars(); /* /********************************************************** /* Configuration /********************************************************** */ final protected Writer _writer; /** * Character used for quoting JSON Object property names * and String values. */ protected char _quoteChar; /* /********************************************************** /* Output buffering /********************************************************** */ /** * Intermediate buffer in which contents are buffered before * being written using {@link #_writer}. */ protected char[] _outputBuffer; /** * Pointer to the first buffered character to output */ protected int _outputHead; /** * Pointer to the position right beyond the last character to output * (end marker; may point to position right beyond the end of the buffer) */ protected int _outputTail; /** * End marker of the output buffer; one past the last valid position * within the buffer. */ protected int _outputEnd; /** * Short (14 char) temporary buffer allocated if needed, for constructing * escape sequences */ protected char[] _entityBuffer; /** * When custom escapes are used, this member variable is used * internally to hold a reference to currently used escape */ protected SerializableString _currentEscape; /** * Intermediate buffer in which characters of a String are copied * before being encoded. * * @since 2.10 */ protected char[] _copyBuffer; /* /********************************************************** /* Life-cycle /********************************************************** */ @Deprecated // since 2.10 public WriterBasedJsonGenerator(IOContext ctxt, int features, ObjectCodec codec, Writer w) { this(ctxt, features, codec, w, JsonFactory.DEFAULT_QUOTE_CHAR); } /** * @since 2.10 */ public WriterBasedJsonGenerator(IOContext ctxt, int features, ObjectCodec codec, Writer w, char quoteChar) { super(ctxt, features, codec); _writer = w; _outputBuffer = ctxt.allocConcatBuffer(); _outputEnd = _outputBuffer.length; _quoteChar = quoteChar; if (quoteChar != '"') { // since 2.10 _outputEscapes = CharTypes.get7BitOutputEscapes(quoteChar); } } /* /********************************************************** /* Overridden configuration, introspection methods /********************************************************** */ @Override public Object getOutputTarget() { return _writer; } @Override public int getOutputBuffered() { // Assuming tail and head are kept but... trust and verify: int len = _outputTail - _outputHead; return Math.max(0, len); } // json does allow this so @Override public boolean canWriteFormattedNumbers() { return true; } /* /********************************************************** /* Overridden methods /********************************************************** */ @Override public void writeFieldName(String name) throws IOException { int status = _writeContext.writeFieldName(name); if (status == JsonWriteContext.STATUS_EXPECT_VALUE) { _reportError("Can not write a field name, expecting a value"); } _writeFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA)); } @Override public void writeFieldName(SerializableString name) throws IOException { // Object is a value, need to verify it's allowed int status = _writeContext.writeFieldName(name.getValue()); if (status == JsonWriteContext.STATUS_EXPECT_VALUE) { _reportError("Can not write a field name, expecting a value"); } _writeFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA)); } protected final void _writeFieldName(String name, boolean commaBefore) throws IOException { if (_cfgPrettyPrinter != null) { _writePPFieldName(name, commaBefore); return; } // for fast+std case, need to output up to 2 chars, comma, dquote if ((_outputTail + 1) >= _outputEnd) { _flushBuffer(); } if (commaBefore) { _outputBuffer[_outputTail++] = ','; } // Alternate mode, in which quoting of field names disabled? if (_cfgUnqNames) { _writeString(name); return; } // we know there's room for at least one more char _outputBuffer[_outputTail++] = _quoteChar; // The beef: _writeString(name); // and closing quotes; need room for one more char: if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } protected final void _writeFieldName(SerializableString name, boolean commaBefore) throws IOException { if (_cfgPrettyPrinter != null) { _writePPFieldName(name, commaBefore); return; } // for fast+std case, need to output up to 2 chars, comma, dquote if ((_outputTail + 1) >= _outputEnd) { _flushBuffer(); } if (commaBefore) { _outputBuffer[_outputTail++] = ','; } // Alternate mode, in which quoting of field names disabled? if (_cfgUnqNames) { final char[] ch = name.asQuotedChars(); writeRaw(ch, 0, ch.length); return; } // we know there's room for at least one more char _outputBuffer[_outputTail++] = _quoteChar; // The beef: int len = name.appendQuoted(_outputBuffer, _outputTail); if (len < 0) { _writeFieldNameTail(name); return; } _outputTail += len; if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } private final void _writeFieldNameTail(SerializableString name) throws IOException { final char[] quoted = name.asQuotedChars(); writeRaw(quoted, 0, quoted.length); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } /* /********************************************************** /* Output method implementations, structural /********************************************************** */ @Override public void writeStartArray() throws IOException { _verifyValueWrite("start an array"); _writeContext = _writeContext.createChildArrayContext(); if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeStartArray(this); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '['; } } @Override // since 2.10 public void writeStartArray(int size) throws IOException { _verifyValueWrite("start an array"); _writeContext = _writeContext.createChildArrayContext(); if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeStartArray(this); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '['; } } @Override public void writeEndArray() throws IOException { if (!_writeContext.inArray()) { _reportError("Current context not Array but "+_writeContext.typeDesc()); } if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount()); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = ']'; } _writeContext = _writeContext.clearAndGetParent(); } @Override public void writeStartObject() throws IOException { _verifyValueWrite("start an object"); _writeContext = _writeContext.createChildObjectContext(); if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeStartObject(this); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '{'; } } @Override // since 2.8 public void writeStartObject(Object forValue) throws IOException { _verifyValueWrite("start an object"); JsonWriteContext ctxt = _writeContext.createChildObjectContext(forValue); _writeContext = ctxt; if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeStartObject(this); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '{'; } } @Override public void writeEndObject() throws IOException { if (!_writeContext.inObject()) { _reportError("Current context not Object but "+_writeContext.typeDesc()); } if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount()); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '}'; } _writeContext = _writeContext.clearAndGetParent(); } /** * Specialized version of _writeFieldName, off-lined * to keep the "fast path" as simple (and hopefully fast) as possible. */ protected final void _writePPFieldName(String name, boolean commaBefore) throws IOException { if (commaBefore) { _cfgPrettyPrinter.writeObjectEntrySeparator(this); } else { _cfgPrettyPrinter.beforeObjectEntries(this); } if (_cfgUnqNames) {// non-standard, omit quotes _writeString(name); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; _writeString(name); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } } protected final void _writePPFieldName(SerializableString name, boolean commaBefore) throws IOException { if (commaBefore) { _cfgPrettyPrinter.writeObjectEntrySeparator(this); } else { _cfgPrettyPrinter.beforeObjectEntries(this); } final char[] quoted = name.asQuotedChars(); if (_cfgUnqNames) {// non-standard, omit quotes writeRaw(quoted, 0, quoted.length); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; writeRaw(quoted, 0, quoted.length); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } } /* /********************************************************** /* Output method implementations, textual /********************************************************** */ @Override public void writeString(String text) throws IOException { _verifyValueWrite(WRITE_STRING); if (text == null) { _writeNull(); return; } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; _writeString(text); // And finally, closing quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } @Override public void writeString(Reader reader, int len) throws IOException { _verifyValueWrite(WRITE_STRING); if (reader == null) { _reportError("null reader"); } int toRead = (len >= 0) ? len : Integer.MAX_VALUE; //Add leading quote if ((_outputTail + len) >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; final char[] buf = _allocateCopyBuffer(); //read while (toRead > 0) { int toReadNow = Math.min(toRead, buf.length); int numRead = reader.read(buf, 0, toReadNow); if (numRead <= 0) { break; } if ((_outputTail + len) >= _outputEnd) { _flushBuffer(); } _writeString(buf, 0, numRead); //decrease tracker toRead -= numRead; } //Add trailing quote if ((_outputTail + len) >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; if (toRead > 0 && len >= 0) { _reportError("Didn't read enough from reader"); } } @Override public void writeString(char[] text, int offset, int len) throws IOException { _verifyValueWrite(WRITE_STRING); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; _writeString(text, offset, len); // And finally, closing quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } @Override public void writeString(SerializableString sstr) throws IOException { _verifyValueWrite(WRITE_STRING); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; int len = sstr.appendQuoted(_outputBuffer, _outputTail); if (len < 0) { _writeString2(sstr); return; } _outputTail += len; if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } private void _writeString2(SerializableString sstr) throws IOException { // Note: copied from writeRaw: char[] text = sstr.asQuotedChars(); final int len = text.length; if (len < SHORT_WRITE) { int room = _outputEnd - _outputTail; if (len > room) { _flushBuffer(); } System.arraycopy(text, 0, _outputBuffer, _outputTail, len); _outputTail += len; } else { _flushBuffer(); _writer.write(text, 0, len); } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } @Override public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException { // could add support for buffering if we really want it... _reportUnsupportedOperation(); } @Override public void writeUTF8String(byte[] text, int offset, int length) throws IOException { // could add support for buffering if we really want it... _reportUnsupportedOperation(); } /* /********************************************************** /* Output method implementations, unprocessed ("raw") /********************************************************** */ @Override public void writeRaw(String text) throws IOException { // Nothing to check, can just output as is int len = text.length(); int room = _outputEnd - _outputTail; if (room == 0) { _flushBuffer(); room = _outputEnd - _outputTail; } // But would it nicely fit in? If yes, it's easy if (room >= len) { text.getChars(0, len, _outputBuffer, _outputTail); _outputTail += len; } else { writeRawLong(text); } } @Override public void writeRaw(String text, int start, int len) throws IOException { // Nothing to check, can just output as is int room = _outputEnd - _outputTail; if (room < len) { _flushBuffer(); room = _outputEnd - _outputTail; } // But would it nicely fit in? If yes, it's easy if (room >= len) { text.getChars(start, start+len, _outputBuffer, _outputTail); _outputTail += len; } else { writeRawLong(text.substring(start, start+len)); } } // @since 2.1 @Override public void writeRaw(SerializableString text) throws IOException { int len = text.appendUnquoted(_outputBuffer, _outputTail); if (len < 0) { writeRaw(text.getValue()); return; } _outputTail += len; } @Override public void writeRaw(char[] text, int offset, int len) throws IOException { // Only worth buffering if it's a short write? if (len < SHORT_WRITE) { int room = _outputEnd - _outputTail; if (len > room) { _flushBuffer(); } System.arraycopy(text, offset, _outputBuffer, _outputTail, len); _outputTail += len; return; } // Otherwise, better just pass through: _flushBuffer(); _writer.write(text, offset, len); } @Override public void writeRaw(char c) throws IOException { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = c; } private void writeRawLong(String text) throws IOException { int room = _outputEnd - _outputTail; // If not, need to do it by looping text.getChars(0, room, _outputBuffer, _outputTail); _outputTail += room; _flushBuffer(); int offset = room; int len = text.length() - room; while (len > _outputEnd) { int amount = _outputEnd; text.getChars(offset, offset+amount, _outputBuffer, 0); _outputHead = 0; _outputTail = amount; _flushBuffer(); offset += amount; len -= amount; } // And last piece (at most length of buffer) text.getChars(offset, offset+len, _outputBuffer, 0); _outputHead = 0; _outputTail = len; } /* /********************************************************** /* Output method implementations, base64-encoded binary /********************************************************** */ @Override public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException, JsonGenerationException { _verifyValueWrite(WRITE_BINARY); // Starting quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; _writeBinary(b64variant, data, offset, offset+len); // and closing quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } @Override public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) throws IOException, JsonGenerationException { _verifyValueWrite(WRITE_BINARY); // Starting quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; byte[] encodingBuffer = _ioContext.allocBase64Buffer(); int bytes; try { if (dataLength < 0) { // length unknown bytes = _writeBinary(b64variant, data, encodingBuffer); } else { int missing = _writeBinary(b64variant, data, encodingBuffer, dataLength); if (missing > 0) { _reportError("Too few bytes available: missing "+missing+" bytes (out of "+dataLength+")"); } bytes = dataLength; } } finally { _ioContext.releaseBase64Buffer(encodingBuffer); } // and closing quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; return bytes; } /* /********************************************************** /* Output method implementations, primitive /********************************************************** */ @Override public void writeNumber(short s) throws IOException { _verifyValueWrite(WRITE_NUMBER); if (_cfgNumbersAsStrings) { _writeQuotedShort(s); return; } // up to 5 digits and possible minus sign if ((_outputTail + 6) >= _outputEnd) { _flushBuffer(); } _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail); } private void _writeQuotedShort(short s) throws IOException { if ((_outputTail + 8) >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail); _outputBuffer[_outputTail++] = _quoteChar; } @Override public void writeNumber(int i) throws IOException { _verifyValueWrite(WRITE_NUMBER); if (_cfgNumbersAsStrings) { _writeQuotedInt(i); return; } // up to 10 digits and possible minus sign if ((_outputTail + 11) >= _outputEnd) { _flushBuffer(); } _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail); } private void _writeQuotedInt(int i) throws IOException { if ((_outputTail + 13) >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail); _outputBuffer[_outputTail++] = _quoteChar; } @Override public void writeNumber(long l) throws IOException { _verifyValueWrite(WRITE_NUMBER); if (_cfgNumbersAsStrings) { _writeQuotedLong(l); return; } if ((_outputTail + 21) >= _outputEnd) { // up to 20 digits, minus sign _flushBuffer(); } _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail); } private void _writeQuotedLong(long l) throws IOException { if ((_outputTail + 23) >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail); _outputBuffer[_outputTail++] = _quoteChar; } // !!! 05-Aug-2008, tatus: Any ways to optimize these? @Override public void writeNumber(BigInteger value) throws IOException { _verifyValueWrite(WRITE_NUMBER); if (value == null) { _writeNull(); } else if (_cfgNumbersAsStrings) { _writeQuotedRaw(value.toString()); } else { writeRaw(value.toString()); } } @SuppressWarnings("deprecation") @Override public void writeNumber(double d) throws IOException { if (_cfgNumbersAsStrings || (NumberOutput.notFinite(d) && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS))) { writeString(String.valueOf(d)); return; } // What is the max length for doubles? 40 chars? _verifyValueWrite(WRITE_NUMBER); writeRaw(String.valueOf(d)); } @SuppressWarnings("deprecation") @Override public void writeNumber(float f) throws IOException { if (_cfgNumbersAsStrings || (NumberOutput.notFinite(f) && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS))) { writeString(String.valueOf(f)); return; } // What is the max length for floats? _verifyValueWrite(WRITE_NUMBER); writeRaw(String.valueOf(f)); } @Override public void writeNumber(BigDecimal value) throws IOException { // Don't really know max length for big decimal, no point checking _verifyValueWrite(WRITE_NUMBER); if (value == null) { _writeNull(); } else if (_cfgNumbersAsStrings) { _writeQuotedRaw(_asString(value)); } else { writeRaw(_asString(value)); } } @Override public void writeNumber(String encodedValue) throws IOException { _verifyValueWrite(WRITE_NUMBER); if (_cfgNumbersAsStrings) { _writeQuotedRaw(encodedValue); } else { writeRaw(encodedValue); } } private void _writeQuotedRaw(String value) throws IOException { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; writeRaw(value); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = _quoteChar; } @Override public void writeBoolean(boolean state) throws IOException { _verifyValueWrite(WRITE_BOOLEAN); if ((_outputTail + 5) >= _outputEnd) { _flushBuffer(); } int ptr = _outputTail; char[] buf = _outputBuffer; if (state) { buf[ptr] = 't'; buf[++ptr] = 'r'; buf[++ptr] = 'u'; buf[++ptr] = 'e'; } else { buf[ptr] = 'f'; buf[++ptr] = 'a'; buf[++ptr] = 'l'; buf[++ptr] = 's'; buf[++ptr] = 'e'; } _outputTail = ptr+1; } @Override public void writeNull() throws IOException { _verifyValueWrite(WRITE_NULL); _writeNull(); } /* /********************************************************** /* Implementations for other methods /********************************************************** */ @Override protected final void _verifyValueWrite(String typeMsg) throws IOException { final int status = _writeContext.writeValue(); if (_cfgPrettyPrinter != null) { // Otherwise, pretty printer knows what to do... _verifyPrettyValueWrite(typeMsg, status); return; } char c; switch (status) { case JsonWriteContext.STATUS_OK_AS_IS: default: return; case JsonWriteContext.STATUS_OK_AFTER_COMMA: c = ','; break; case JsonWriteContext.STATUS_OK_AFTER_COLON: c = ':'; break; case JsonWriteContext.STATUS_OK_AFTER_SPACE: // root-value separator if (_rootValueSeparator != null) { writeRaw(_rootValueSeparator.getValue()); } return; case JsonWriteContext.STATUS_EXPECT_NAME: _reportCantWriteValueExpectName(typeMsg); return; } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = c; } /* /********************************************************** /* Low-level output handling /********************************************************** */ @Override public void flush() throws IOException { _flushBuffer(); if (_writer != null) { if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) { _writer.flush(); } } } @Override public void close() throws IOException { super.close(); /* 05-Dec-2008, tatu: To add [JACKSON-27], need to close open * scopes. */ // First: let's see that we still have buffers... if (_outputBuffer != null && isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) { while (true) { JsonStreamContext ctxt = getOutputContext(); if (ctxt.inArray()) { writeEndArray(); } else if (ctxt.inObject()) { writeEndObject(); } else { break; } } } _flushBuffer(); _outputHead = 0; _outputTail = 0; /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close() * on the underlying Reader, unless we "own" it, or auto-closing * feature is enabled. * One downside: when using UTF8Writer, underlying buffer(s) * may not be properly recycled if we don't close the writer. */ if (_writer != null) { if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) { _writer.close(); } else if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) { // If we can't close it, we should at least flush _writer.flush(); } } // Internal buffer(s) generator has can now be released as well _releaseBuffers(); } @Override protected void _releaseBuffers() { char[] buf = _outputBuffer; if (buf != null) { _outputBuffer = null; _ioContext.releaseConcatBuffer(buf); } buf = _copyBuffer; if (buf != null) { _copyBuffer = null; _ioContext.releaseNameCopyBuffer(buf); } } /* /********************************************************** /* Internal methods, low-level writing; text, default /********************************************************** */ private void _writeString(String text) throws IOException { /* One check first: if String won't fit in the buffer, let's * segment writes. No point in extending buffer to huge sizes * (like if someone wants to include multi-megabyte base64 * encoded stuff or such) */ final int len = text.length(); if (len > _outputEnd) { // Let's reserve space for entity at begin/end _writeLongString(text); return; } // Ok: we know String will fit in buffer ok // But do we need to flush first? if ((_outputTail + len) > _outputEnd) { _flushBuffer(); } text.getChars(0, len, _outputBuffer, _outputTail); if (_characterEscapes != null) { _writeStringCustom(len); } else if (_maximumNonEscapedChar != 0) { _writeStringASCII(len, _maximumNonEscapedChar); } else { _writeString2(len); } } private void _writeString2(final int len) throws IOException { // And then we'll need to verify need for escaping etc: final int end = _outputTail + len; final int[] escCodes = _outputEscapes; final int escLen = escCodes.length; output_loop: while (_outputTail < end) { // Fast loop for chars not needing escaping escape_loop: while (true) { char c = _outputBuffer[_outputTail]; if (c < escLen && escCodes[c] != 0) { break escape_loop; } if (++_outputTail >= end) { break output_loop; } } // Ok, bumped into something that needs escaping. /* First things first: need to flush the buffer. * Inlined, as we don't want to lose tail pointer */ int flushLen = (_outputTail - _outputHead); if (flushLen > 0) { _writer.write(_outputBuffer, _outputHead, flushLen); } /* In any case, tail will be the new start, so hopefully * we have room now. */ char c = _outputBuffer[_outputTail++]; _prependOrWriteCharacterEscape(c, escCodes[c]); } } /** * Method called to write "long strings", strings whose length exceeds * output buffer length. */ private void _writeLongString(String text) throws IOException { // First things first: let's flush the buffer to get some more room _flushBuffer(); // Then we can write final int textLen = text.length(); int offset = 0; do { int max = _outputEnd; int segmentLen = ((offset + max) > textLen) ? (textLen - offset) : max; text.getChars(offset, offset+segmentLen, _outputBuffer, 0); if (_characterEscapes != null) { _writeSegmentCustom(segmentLen); } else if (_maximumNonEscapedChar != 0) { _writeSegmentASCII(segmentLen, _maximumNonEscapedChar); } else { _writeSegment(segmentLen); } offset += segmentLen; } while (offset < textLen); } /** * Method called to output textual context which has been copied * to the output buffer prior to call. If any escaping is needed, * it will also be handled by the method. *

* Note: when called, textual content to write is within output * buffer, right after buffered content (if any). That's why only * length of that text is passed, as buffer and offset are implied. */ private void _writeSegment(int end) throws IOException { final int[] escCodes = _outputEscapes; final int escLen = escCodes.length; int ptr = 0; int start = ptr; output_loop: while (ptr < end) { // Fast loop for chars not needing escaping char c; while (true) { c = _outputBuffer[ptr]; if (c < escLen && escCodes[c] != 0) { break; } if (++ptr >= end) { break; } } // Ok, bumped into something that needs escaping. /* First things first: need to flush the buffer. * Inlined, as we don't want to lose tail pointer */ int flushLen = (ptr - start); if (flushLen > 0) { _writer.write(_outputBuffer, start, flushLen); if (ptr >= end) { break output_loop; } } ++ptr; // So; either try to prepend (most likely), or write directly: start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCodes[c]); } } /** * This method called when the string content is already in * a char buffer, and need not be copied for processing. */ private void _writeString(char[] text, int offset, int len) throws IOException { if (_characterEscapes != null) { _writeStringCustom(text, offset, len); return; } if (_maximumNonEscapedChar != 0) { _writeStringASCII(text, offset, len, _maximumNonEscapedChar); return; } /* Let's just find longest spans of non-escapable * content, and for each see if it makes sense * to copy them, or write through */ len += offset; // -> len marks the end from now on final int[] escCodes = _outputEscapes; final int escLen = escCodes.length; while (offset < len) { int start = offset; while (true) { char c = text[offset]; if (c < escLen && escCodes[c] != 0) { break; } if (++offset >= len) { break; } } // Short span? Better just copy it to buffer first: int newAmount = offset - start; if (newAmount < SHORT_WRITE) { // Note: let's reserve room for escaped char (up to 6 chars) if ((_outputTail + newAmount) > _outputEnd) { _flushBuffer(); } if (newAmount > 0) { System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount); _outputTail += newAmount; } } else { // Nope: better just write through _flushBuffer(); _writer.write(text, start, newAmount); } // Was this the end? if (offset >= len) { // yup break; } // Nope, need to escape the char. char c = text[offset++]; _appendCharacterEscape(c, escCodes[c]); } } /* /********************************************************** /* Internal methods, low-level writing, text segment /* with additional escaping (ASCII or such) /********************************************************** */ /* Same as "_writeString2()", except needs additional escaping * for subset of characters */ private void _writeStringASCII(final int len, final int maxNonEscaped) throws IOException, JsonGenerationException { // And then we'll need to verify need for escaping etc: int end = _outputTail + len; final int[] escCodes = _outputEscapes; final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); int escCode = 0; output_loop: while (_outputTail < end) { char c; // Fast loop for chars not needing escaping escape_loop: while (true) { c = _outputBuffer[_outputTail]; if (c < escLimit) { escCode = escCodes[c]; if (escCode != 0) { break escape_loop; } } else if (c > maxNonEscaped) { escCode = CharacterEscapes.ESCAPE_STANDARD; break escape_loop; } if (++_outputTail >= end) { break output_loop; } } int flushLen = (_outputTail - _outputHead); if (flushLen > 0) { _writer.write(_outputBuffer, _outputHead, flushLen); } ++_outputTail; _prependOrWriteCharacterEscape(c, escCode); } } private void _writeSegmentASCII(int end, final int maxNonEscaped) throws IOException, JsonGenerationException { final int[] escCodes = _outputEscapes; final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); int ptr = 0; int escCode = 0; int start = ptr; output_loop: while (ptr < end) { // Fast loop for chars not needing escaping char c; while (true) { c = _outputBuffer[ptr]; if (c < escLimit) { escCode = escCodes[c]; if (escCode != 0) { break; } } else if (c > maxNonEscaped) { escCode = CharacterEscapes.ESCAPE_STANDARD; break; } if (++ptr >= end) { break; } } int flushLen = (ptr - start); if (flushLen > 0) { _writer.write(_outputBuffer, start, flushLen); if (ptr >= end) { break output_loop; } } ++ptr; start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCode); } } private void _writeStringASCII(char[] text, int offset, int len, final int maxNonEscaped) throws IOException, JsonGenerationException { len += offset; // -> len marks the end from now on final int[] escCodes = _outputEscapes; final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); int escCode = 0; while (offset < len) { int start = offset; char c; while (true) { c = text[offset]; if (c < escLimit) { escCode = escCodes[c]; if (escCode != 0) { break; } } else if (c > maxNonEscaped) { escCode = CharacterEscapes.ESCAPE_STANDARD; break; } if (++offset >= len) { break; } } // Short span? Better just copy it to buffer first: int newAmount = offset - start; if (newAmount < SHORT_WRITE) { // Note: let's reserve room for escaped char (up to 6 chars) if ((_outputTail + newAmount) > _outputEnd) { _flushBuffer(); } if (newAmount > 0) { System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount); _outputTail += newAmount; } } else { // Nope: better just write through _flushBuffer(); _writer.write(text, start, newAmount); } // Was this the end? if (offset >= len) { // yup break; } // Nope, need to escape the char. ++offset; _appendCharacterEscape(c, escCode); } } /* /********************************************************** /* Internal methods, low-level writing, text segment /* with custom escaping (possibly coupling with ASCII limits) /********************************************************** */ /* Same as "_writeString2()", except needs additional escaping * for subset of characters */ private void _writeStringCustom(final int len) throws IOException, JsonGenerationException { // And then we'll need to verify need for escaping etc: int end = _outputTail + len; final int[] escCodes = _outputEscapes; final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar; final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); int escCode = 0; final CharacterEscapes customEscapes = _characterEscapes; output_loop: while (_outputTail < end) { char c; // Fast loop for chars not needing escaping escape_loop: while (true) { c = _outputBuffer[_outputTail]; if (c < escLimit) { escCode = escCodes[c]; if (escCode != 0) { break escape_loop; } } else if (c > maxNonEscaped) { escCode = CharacterEscapes.ESCAPE_STANDARD; break escape_loop; } else { if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) { escCode = CharacterEscapes.ESCAPE_CUSTOM; break escape_loop; } } if (++_outputTail >= end) { break output_loop; } } int flushLen = (_outputTail - _outputHead); if (flushLen > 0) { _writer.write(_outputBuffer, _outputHead, flushLen); } ++_outputTail; _prependOrWriteCharacterEscape(c, escCode); } } private void _writeSegmentCustom(int end) throws IOException, JsonGenerationException { final int[] escCodes = _outputEscapes; final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar; final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); final CharacterEscapes customEscapes = _characterEscapes; int ptr = 0; int escCode = 0; int start = ptr; output_loop: while (ptr < end) { // Fast loop for chars not needing escaping char c; while (true) { c = _outputBuffer[ptr]; if (c < escLimit) { escCode = escCodes[c]; if (escCode != 0) { break; } } else if (c > maxNonEscaped) { escCode = CharacterEscapes.ESCAPE_STANDARD; break; } else { if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) { escCode = CharacterEscapes.ESCAPE_CUSTOM; break; } } if (++ptr >= end) { break; } } int flushLen = (ptr - start); if (flushLen > 0) { _writer.write(_outputBuffer, start, flushLen); if (ptr >= end) { break output_loop; } } ++ptr; start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCode); } } private void _writeStringCustom(char[] text, int offset, int len) throws IOException, JsonGenerationException { len += offset; // -> len marks the end from now on final int[] escCodes = _outputEscapes; final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar; final int escLimit = Math.min(escCodes.length, maxNonEscaped+1); final CharacterEscapes customEscapes = _characterEscapes; int escCode = 0; while (offset < len) { int start = offset; char c; while (true) { c = text[offset]; if (c < escLimit) { escCode = escCodes[c]; if (escCode != 0) { break; } } else if (c > maxNonEscaped) { escCode = CharacterEscapes.ESCAPE_STANDARD; break; } else { if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) { escCode = CharacterEscapes.ESCAPE_CUSTOM; break; } } if (++offset >= len) { break; } } // Short span? Better just copy it to buffer first: int newAmount = offset - start; if (newAmount < SHORT_WRITE) { // Note: let's reserve room for escaped char (up to 6 chars) if ((_outputTail + newAmount) > _outputEnd) { _flushBuffer(); } if (newAmount > 0) { System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount); _outputTail += newAmount; } } else { // Nope: better just write through _flushBuffer(); _writer.write(text, start, newAmount); } // Was this the end? if (offset >= len) { // yup break; } // Nope, need to escape the char. ++offset; _appendCharacterEscape(c, escCode); } } /* /********************************************************** /* Internal methods, low-level writing; binary /********************************************************** */ protected final void _writeBinary(Base64Variant b64variant, byte[] input, int inputPtr, final int inputEnd) throws IOException, JsonGenerationException { // Encoding is by chunks of 3 input, 4 output chars, so: int safeInputEnd = inputEnd - 3; // Let's also reserve room for possible (and quoted) lf char each round int safeOutputEnd = _outputEnd - 6; int chunksBeforeLF = b64variant.getMaxLineLength() >> 2; // Ok, first we loop through all full triplets of data: while (inputPtr <= safeInputEnd) { if (_outputTail > safeOutputEnd) { // need to flush _flushBuffer(); } // First, mash 3 bytes into lsb of 32-bit int int b24 = ((int) input[inputPtr++]) << 8; b24 |= ((int) input[inputPtr++]) & 0xFF; b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF); _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail); if (--chunksBeforeLF <= 0) { // note: must quote in JSON value _outputBuffer[_outputTail++] = '\\'; _outputBuffer[_outputTail++] = 'n'; chunksBeforeLF = b64variant.getMaxLineLength() >> 2; } } // And then we may have 1 or 2 leftover bytes to encode int inputLeft = inputEnd - inputPtr; // 0, 1 or 2 if (inputLeft > 0) { // yes, but do we have room for output? if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but... _flushBuffer(); } int b24 = ((int) input[inputPtr++]) << 16; if (inputLeft == 2) { b24 |= (((int) input[inputPtr++]) & 0xFF) << 8; } _outputTail = b64variant.encodeBase64Partial(b24, inputLeft, _outputBuffer, _outputTail); } } // write-method called when length is definitely known protected final int _writeBinary(Base64Variant b64variant, InputStream data, byte[] readBuffer, int bytesLeft) throws IOException, JsonGenerationException { int inputPtr = 0; int inputEnd = 0; int lastFullOffset = -3; // Let's also reserve room for possible (and quoted) lf char each round int safeOutputEnd = _outputEnd - 6; int chunksBeforeLF = b64variant.getMaxLineLength() >> 2; while (bytesLeft > 2) { // main loop for full triplets if (inputPtr > lastFullOffset) { inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft); inputPtr = 0; if (inputEnd < 3) { // required to try to read to have at least 3 bytes break; } lastFullOffset = inputEnd-3; } if (_outputTail > safeOutputEnd) { // need to flush _flushBuffer(); } int b24 = ((int) readBuffer[inputPtr++]) << 8; b24 |= ((int) readBuffer[inputPtr++]) & 0xFF; b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF); bytesLeft -= 3; _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail); if (--chunksBeforeLF <= 0) { _outputBuffer[_outputTail++] = '\\'; _outputBuffer[_outputTail++] = 'n'; chunksBeforeLF = b64variant.getMaxLineLength() >> 2; } } // And then we may have 1 or 2 leftover bytes to encode if (bytesLeft > 0) { inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft); inputPtr = 0; if (inputEnd > 0) { // yes, but do we have room for output? if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but... _flushBuffer(); } int b24 = ((int) readBuffer[inputPtr++]) << 16; int amount; if (inputPtr < inputEnd) { b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8; amount = 2; } else { amount = 1; } _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail); bytesLeft -= amount; } } return bytesLeft; } // write method when length is unknown protected final int _writeBinary(Base64Variant b64variant, InputStream data, byte[] readBuffer) throws IOException, JsonGenerationException { int inputPtr = 0; int inputEnd = 0; int lastFullOffset = -3; int bytesDone = 0; // Let's also reserve room for possible (and quoted) LF char each round int safeOutputEnd = _outputEnd - 6; int chunksBeforeLF = b64variant.getMaxLineLength() >> 2; // Ok, first we loop through all full triplets of data: while (true) { if (inputPtr > lastFullOffset) { // need to load more inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, readBuffer.length); inputPtr = 0; if (inputEnd < 3) { // required to try to read to have at least 3 bytes break; } lastFullOffset = inputEnd-3; } if (_outputTail > safeOutputEnd) { // need to flush _flushBuffer(); } // First, mash 3 bytes into lsb of 32-bit int int b24 = ((int) readBuffer[inputPtr++]) << 8; b24 |= ((int) readBuffer[inputPtr++]) & 0xFF; b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF); bytesDone += 3; _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail); if (--chunksBeforeLF <= 0) { _outputBuffer[_outputTail++] = '\\'; _outputBuffer[_outputTail++] = 'n'; chunksBeforeLF = b64variant.getMaxLineLength() >> 2; } } // And then we may have 1 or 2 leftover bytes to encode if (inputPtr < inputEnd) { // yes, but do we have room for output? if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but... _flushBuffer(); } int b24 = ((int) readBuffer[inputPtr++]) << 16; int amount = 1; if (inputPtr < inputEnd) { b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8; amount = 2; } bytesDone += amount; _outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail); } return bytesDone; } private int _readMore(InputStream in, byte[] readBuffer, int inputPtr, int inputEnd, int maxRead) throws IOException { // anything to shift to front? int i = 0; while (inputPtr < inputEnd) { readBuffer[i++] = readBuffer[inputPtr++]; } inputPtr = 0; inputEnd = i; maxRead = Math.min(maxRead, readBuffer.length); do { int length = maxRead - inputEnd; if (length == 0) { break; } int count = in.read(readBuffer, inputEnd, length); if (count < 0) { return inputEnd; } inputEnd += count; } while (inputEnd < 3); return inputEnd; } /* /********************************************************** /* Internal methods, low-level writing, other /********************************************************** */ private final void _writeNull() throws IOException { if ((_outputTail + 4) >= _outputEnd) { _flushBuffer(); } int ptr = _outputTail; char[] buf = _outputBuffer; buf[ptr] = 'n'; buf[++ptr] = 'u'; buf[++ptr] = 'l'; buf[++ptr] = 'l'; _outputTail = ptr+1; } /* /********************************************************** /* Internal methods, low-level writing, escapes /********************************************************** */ /** * Method called to try to either prepend character escape at front of * given buffer; or if not possible, to write it out directly. * Uses head and tail pointers (and updates as necessary) */ private void _prependOrWriteCharacterEscape(char ch, int escCode) throws IOException, JsonGenerationException { if (escCode >= 0) { // \\N (2 char) if (_outputTail >= 2) { // fits, just prepend int ptr = _outputTail - 2; _outputHead = ptr; _outputBuffer[ptr++] = '\\'; _outputBuffer[ptr] = (char) escCode; return; } // won't fit, write char[] buf = _entityBuffer; if (buf == null) { buf = _allocateEntityBuffer(); } _outputHead = _outputTail; buf[1] = (char) escCode; _writer.write(buf, 0, 2); return; } if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX if (_outputTail >= 6) { // fits, prepend to buffer char[] buf = _outputBuffer; int ptr = _outputTail - 6; _outputHead = ptr; buf[ptr] = '\\'; buf[++ptr] = 'u'; // We know it's a control char, so only the last 2 chars are non-0 if (ch > 0xFF) { // beyond 8 bytes int hi = (ch >> 8) & 0xFF; buf[++ptr] = HEX_CHARS[hi >> 4]; buf[++ptr] = HEX_CHARS[hi & 0xF]; ch &= 0xFF; } else { buf[++ptr] = '0'; buf[++ptr] = '0'; } buf[++ptr] = HEX_CHARS[ch >> 4]; buf[++ptr] = HEX_CHARS[ch & 0xF]; return; } // won't fit, flush and write char[] buf = _entityBuffer; if (buf == null) { buf = _allocateEntityBuffer(); } _outputHead = _outputTail; if (ch > 0xFF) { // beyond 8 bytes int hi = (ch >> 8) & 0xFF; int lo = ch & 0xFF; buf[10] = HEX_CHARS[hi >> 4]; buf[11] = HEX_CHARS[hi & 0xF]; buf[12] = HEX_CHARS[lo >> 4]; buf[13] = HEX_CHARS[lo & 0xF]; _writer.write(buf, 8, 6); } else { // We know it's a control char, so only the last 2 chars are non-0 buf[6] = HEX_CHARS[ch >> 4]; buf[7] = HEX_CHARS[ch & 0xF]; _writer.write(buf, 2, 6); } return; } String escape; if (_currentEscape == null) { escape = _characterEscapes.getEscapeSequence(ch).getValue(); } else { escape = _currentEscape.getValue(); _currentEscape = null; } int len = escape.length(); if (_outputTail >= len) { // fits in, prepend int ptr = _outputTail - len; _outputHead = ptr; escape.getChars(0, len, _outputBuffer, ptr); return; } // won't fit, write separately _outputHead = _outputTail; _writer.write(escape); } /** * Method called to try to either prepend character escape at front of * given buffer; or if not possible, to write it out directly. * * @return Pointer to start of prepended entity (if prepended); or 'ptr' * if not. */ private int _prependOrWriteCharacterEscape(char[] buffer, int ptr, int end, char ch, int escCode) throws IOException, JsonGenerationException { if (escCode >= 0) { // \\N (2 char) if (ptr > 1 && ptr < end) { // fits, just prepend ptr -= 2; buffer[ptr] = '\\'; buffer[ptr+1] = (char) escCode; } else { // won't fit, write char[] ent = _entityBuffer; if (ent == null) { ent = _allocateEntityBuffer(); } ent[1] = (char) escCode; _writer.write(ent, 0, 2); } return ptr; } if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX if (ptr > 5 && ptr < end) { // fits, prepend to buffer ptr -= 6; buffer[ptr++] = '\\'; buffer[ptr++] = 'u'; // We know it's a control char, so only the last 2 chars are non-0 if (ch > 0xFF) { // beyond 8 bytes int hi = (ch >> 8) & 0xFF; buffer[ptr++] = HEX_CHARS[hi >> 4]; buffer[ptr++] = HEX_CHARS[hi & 0xF]; ch &= 0xFF; } else { buffer[ptr++] = '0'; buffer[ptr++] = '0'; } buffer[ptr++] = HEX_CHARS[ch >> 4]; buffer[ptr] = HEX_CHARS[ch & 0xF]; ptr -= 5; } else { // won't fit, flush and write char[] ent = _entityBuffer; if (ent == null) { ent = _allocateEntityBuffer(); } _outputHead = _outputTail; if (ch > 0xFF) { // beyond 8 bytes int hi = (ch >> 8) & 0xFF; int lo = ch & 0xFF; ent[10] = HEX_CHARS[hi >> 4]; ent[11] = HEX_CHARS[hi & 0xF]; ent[12] = HEX_CHARS[lo >> 4]; ent[13] = HEX_CHARS[lo & 0xF]; _writer.write(ent, 8, 6); } else { // We know it's a control char, so only the last 2 chars are non-0 ent[6] = HEX_CHARS[ch >> 4]; ent[7] = HEX_CHARS[ch & 0xF]; _writer.write(ent, 2, 6); } } return ptr; } String escape; if (_currentEscape == null) { escape = _characterEscapes.getEscapeSequence(ch).getValue(); } else { escape = _currentEscape.getValue(); _currentEscape = null; } int len = escape.length(); if (ptr >= len && ptr < end) { // fits in, prepend ptr -= len; escape.getChars(0, len, buffer, ptr); } else { // won't fit, write separately _writer.write(escape); } return ptr; } /** * Method called to append escape sequence for given character, at the * end of standard output buffer; or if not possible, write out directly. */ private void _appendCharacterEscape(char ch, int escCode) throws IOException, JsonGenerationException { if (escCode >= 0) { // \\N (2 char) if ((_outputTail + 2) > _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '\\'; _outputBuffer[_outputTail++] = (char) escCode; return; } if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX if ((_outputTail + 5) >= _outputEnd) { _flushBuffer(); } int ptr = _outputTail; char[] buf = _outputBuffer; buf[ptr++] = '\\'; buf[ptr++] = 'u'; // We know it's a control char, so only the last 2 chars are non-0 if (ch > 0xFF) { // beyond 8 bytes int hi = (ch >> 8) & 0xFF; buf[ptr++] = HEX_CHARS[hi >> 4]; buf[ptr++] = HEX_CHARS[hi & 0xF]; ch &= 0xFF; } else { buf[ptr++] = '0'; buf[ptr++] = '0'; } buf[ptr++] = HEX_CHARS[ch >> 4]; buf[ptr++] = HEX_CHARS[ch & 0xF]; _outputTail = ptr; return; } String escape; if (_currentEscape == null) { escape = _characterEscapes.getEscapeSequence(ch).getValue(); } else { escape = _currentEscape.getValue(); _currentEscape = null; } int len = escape.length(); if ((_outputTail + len) > _outputEnd) { _flushBuffer(); if (len > _outputEnd) { // very very long escape; unlikely but theoretically possible _writer.write(escape); return; } } escape.getChars(0, len, _outputBuffer, _outputTail); _outputTail += len; } private char[] _allocateEntityBuffer() { char[] buf = new char[14]; // first 2 chars, non-numeric escapes (like \n) buf[0] = '\\'; // next 6; 8-bit escapes (control chars mostly) buf[2] = '\\'; buf[3] = 'u'; buf[4] = '0'; buf[5] = '0'; // last 6, beyond 8 bits buf[8] = '\\'; buf[9] = 'u'; _entityBuffer = buf; return buf; } /** * @since 2.9 */ private char[] _allocateCopyBuffer() { if (_copyBuffer == null) { _copyBuffer = _ioContext.allocNameCopyBuffer(2000); } return _copyBuffer; } protected void _flushBuffer() throws IOException { int len = _outputTail - _outputHead; if (len > 0) { int offset = _outputHead; _outputTail = _outputHead = 0; _writer.write(_outputBuffer, offset, len); } } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/async/000077500000000000000000000000001356164247300304255ustar00rootroot00000000000000NonBlockingJsonParser.java000066400000000000000000003222711356164247300354320ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.IOException; import java.io.OutputStream; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.ByteArrayFeeder; import com.fasterxml.jackson.core.async.NonBlockingInputFeeder; 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.VersionUtil; public class NonBlockingJsonParser extends NonBlockingJsonParserBase implements ByteArrayFeeder { @SuppressWarnings("deprecation") private final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask(); @SuppressWarnings("deprecation") private final static int FEAT_MASK_LEADING_ZEROS = Feature.ALLOW_NUMERIC_LEADING_ZEROS.getMask(); @SuppressWarnings("deprecation") private final static int FEAT_MASK_ALLOW_MISSING = Feature.ALLOW_MISSING_VALUES.getMask(); private final static int FEAT_MASK_ALLOW_SINGLE_QUOTES = Feature.ALLOW_SINGLE_QUOTES.getMask(); private final static int FEAT_MASK_ALLOW_UNQUOTED_NAMES = Feature.ALLOW_UNQUOTED_FIELD_NAMES.getMask(); private final static int FEAT_MASK_ALLOW_JAVA_COMMENTS = Feature.ALLOW_COMMENTS.getMask(); private final static int FEAT_MASK_ALLOW_YAML_COMMENTS = Feature.ALLOW_YAML_COMMENTS.getMask(); // 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(); /* /********************************************************************** /* Input source config /********************************************************************** */ /** * This buffer is actually provided via {@link NonBlockingInputFeeder} */ protected byte[] _inputBuffer = NO_BYTES; /** * In addition to current buffer pointer, and end pointer, * we will also need to know number of bytes originally * contained. This is needed to correctly update location * information when the block has been completed. */ protected int _origBufferLen; // And from ParserBase: // protected int _inputPtr; // protected int _inputEnd; /* /********************************************************************** /* Life-cycle /********************************************************************** */ public NonBlockingJsonParser(IOContext ctxt, int parserFeatures, ByteQuadsCanonicalizer sym) { super(ctxt, parserFeatures, sym); } /* /********************************************************************** /* AsyncInputFeeder impl /********************************************************************** */ @Override public ByteArrayFeeder getNonBlockingInputFeeder() { return this; } @Override public final boolean needMoreInput() { return (_inputPtr >=_inputEnd) && !_endOfInput; } @Override public void feedInput(byte[] buf, int start, int end) throws IOException { // Must not have remaining input if (_inputPtr < _inputEnd) { _reportError("Still have %d undecoded bytes, should not call 'feedInput'", _inputEnd - _inputPtr); } if (end < start) { _reportError("Input end (%d) may not be before start (%d)", end, start); } // and shouldn't have been marked as end-of-input if (_endOfInput) { _reportError("Already closed, can not feed more input"); } // Time to update pointers first _currInputProcessed += _origBufferLen; // Also need to adjust row start, to work as if it extended into the past wrt new buffer _currInputRowStart = start - (_inputEnd - _currInputRowStart); // And then update buffer settings _currBufferStart = start; _inputBuffer = buf; _inputPtr = start; _inputEnd = end; _origBufferLen = end - start; } @Override public void endOfInput() { _endOfInput = true; } /* /********************************************************************** /* Abstract methods/overrides from JsonParser /********************************************************************** */ /* Implementing these methods efficiently for non-blocking cases would * be complicated; so for now let's just use the default non-optimized * implementation */ // public boolean nextFieldName(SerializableString str) throws IOException // public String nextTextValue() throws IOException // public int nextIntValue(int defaultValue) throws IOException // public long nextLongValue(long defaultValue) throws IOException // public Boolean nextBooleanValue() throws IOException @Override public int releaseBuffered(OutputStream out) throws IOException { int avail = _inputEnd - _inputPtr; if (avail > 0) { out.write(_inputBuffer, _inputPtr, avail); } return avail; } // Should never be called: can not be implemented quite as expected // due to non-blocking behavior @Override protected char _decodeEscaped() throws IOException { VersionUtil.throwInternal(); return ' '; } /* /********************************************************************** /* Main-level decoding /********************************************************************** */ @Override public JsonToken nextToken() throws IOException { // First: regardless of where we really are, need at least one more byte; // can simplify some of the checks by short-circuiting right away if (_inputPtr >= _inputEnd) { if (_closed) { return null; } // note: if so, do not even bother changing state if (_endOfInput) { // except for this special case // End-of-input within (possibly...) started token is bit complicated, // so offline if (_currToken == JsonToken.NOT_AVAILABLE) { return _finishTokenWithEOF(); } return _eofAsNextToken(); } return JsonToken.NOT_AVAILABLE; } // in the middle of tokenization? if (_currToken == JsonToken.NOT_AVAILABLE) { return _finishToken(); } // No: fresh new token; may or may not have existing one _numTypesValid = NR_UNKNOWN; _tokenInputTotal = _currInputProcessed + _inputPtr; // also: clear any data retained so far _binaryValue = null; int ch = _inputBuffer[_inputPtr++] & 0xFF; switch (_majorState) { case MAJOR_INITIAL: return _startDocument(ch); case MAJOR_ROOT: return _startValue(ch); case MAJOR_OBJECT_FIELD_FIRST: // expect field-name or end-object return _startFieldName(ch); case MAJOR_OBJECT_FIELD_NEXT: // expect comma + field-name or end-object return _startFieldNameAfterComma(ch); case MAJOR_OBJECT_VALUE: // expect colon, followed by value return _startValueExpectColon(ch); case MAJOR_ARRAY_ELEMENT_FIRST: // expect value or end-array return _startValue(ch); case MAJOR_ARRAY_ELEMENT_NEXT: // expect leading comma + value or end-array return _startValueExpectComma(ch); default: } VersionUtil.throwInternal(); return null; } /** * Method called when decoding of a token has been started, but not yet completed due * to missing input; method is to continue decoding due to at least one more byte * being made available to decode. */ protected final JsonToken _finishToken() throws IOException { // NOTE: caller ensures there's input available... switch (_minorState) { case MINOR_ROOT_BOM: return _finishBOM(_pending32); case MINOR_FIELD_LEADING_WS: return _startFieldName(_inputBuffer[_inputPtr++] & 0xFF); case MINOR_FIELD_LEADING_COMMA: return _startFieldNameAfterComma(_inputBuffer[_inputPtr++] & 0xFF); // Field name states case MINOR_FIELD_NAME: return _parseEscapedName(_quadLength, _pending32, _pendingBytes); case MINOR_FIELD_NAME_ESCAPE: return _finishFieldWithEscape(); case MINOR_FIELD_APOS_NAME: return _finishAposName(_quadLength, _pending32, _pendingBytes); case MINOR_FIELD_UNQUOTED_NAME: return _finishUnquotedName(_quadLength, _pending32, _pendingBytes); // Value states case MINOR_VALUE_LEADING_WS: return _startValue(_inputBuffer[_inputPtr++] & 0xFF); case MINOR_VALUE_WS_AFTER_COMMA: return _startValueAfterComma(_inputBuffer[_inputPtr++] & 0xFF); case MINOR_VALUE_EXPECTING_COMMA: return _startValueExpectComma(_inputBuffer[_inputPtr++] & 0xFF); case MINOR_VALUE_EXPECTING_COLON: return _startValueExpectColon(_inputBuffer[_inputPtr++] & 0xFF); case MINOR_VALUE_TOKEN_NULL: return _finishKeywordToken("null", _pending32, JsonToken.VALUE_NULL); case MINOR_VALUE_TOKEN_TRUE: return _finishKeywordToken("true", _pending32, JsonToken.VALUE_TRUE); case MINOR_VALUE_TOKEN_FALSE: return _finishKeywordToken("false", _pending32, JsonToken.VALUE_FALSE); case MINOR_VALUE_TOKEN_NON_STD: return _finishNonStdToken(_nonStdTokenType, _pending32); case MINOR_NUMBER_MINUS: return _finishNumberMinus(_inputBuffer[_inputPtr++] & 0xFF); case MINOR_NUMBER_ZERO: return _finishNumberLeadingZeroes(); case MINOR_NUMBER_MINUSZERO: return _finishNumberLeadingNegZeroes(); case MINOR_NUMBER_INTEGER_DIGITS: return _finishNumberIntegralPart(_textBuffer.getBufferWithoutReset(), _textBuffer.getCurrentSegmentSize()); case MINOR_NUMBER_FRACTION_DIGITS: return _finishFloatFraction(); case MINOR_NUMBER_EXPONENT_MARKER: return _finishFloatExponent(true, _inputBuffer[_inputPtr++] & 0xFF); case MINOR_NUMBER_EXPONENT_DIGITS: return _finishFloatExponent(false, _inputBuffer[_inputPtr++] & 0xFF); case MINOR_VALUE_STRING: return _finishRegularString(); case MINOR_VALUE_STRING_UTF8_2: _textBuffer.append((char) _decodeUTF8_2(_pending32, _inputBuffer[_inputPtr++])); if (_minorStateAfterSplit == MINOR_VALUE_APOS_STRING) { return _finishAposString(); } return _finishRegularString(); case MINOR_VALUE_STRING_UTF8_3: if (!_decodeSplitUTF8_3(_pending32, _pendingBytes, _inputBuffer[_inputPtr++])) { return JsonToken.NOT_AVAILABLE; } if (_minorStateAfterSplit == MINOR_VALUE_APOS_STRING) { return _finishAposString(); } return _finishRegularString(); case MINOR_VALUE_STRING_UTF8_4: if (!_decodeSplitUTF8_4(_pending32, _pendingBytes, _inputBuffer[_inputPtr++])) { return JsonToken.NOT_AVAILABLE; } if (_minorStateAfterSplit == MINOR_VALUE_APOS_STRING) { return _finishAposString(); } return _finishRegularString(); case MINOR_VALUE_STRING_ESCAPE: { int c = _decodeSplitEscaped(_quoted32, _quotedDigits); if (c < 0) { return JsonToken.NOT_AVAILABLE; } _textBuffer.append((char) c); } if (_minorStateAfterSplit == MINOR_VALUE_APOS_STRING) { return _finishAposString(); } return _finishRegularString(); case MINOR_VALUE_APOS_STRING: return _finishAposString(); case MINOR_VALUE_TOKEN_ERROR: // case of "almost token", just need tokenize for error return _finishErrorToken(); // Comments case MINOR_COMMENT_LEADING_SLASH: return _startSlashComment(_pending32); case MINOR_COMMENT_CLOSING_ASTERISK: return _finishCComment(_pending32, true); case MINOR_COMMENT_C: return _finishCComment(_pending32, false); case MINOR_COMMENT_CPP: return _finishCppComment(_pending32); case MINOR_COMMENT_YAML: return _finishHashComment(_pending32); } VersionUtil.throwInternal(); return null; } /** * Method similar to {@link #_finishToken}, but called when no more input is * available, and end-of-input has been detected. This is usually problem * case, but not always: root-level values may be properly terminated by * this, and similarly trailing white-space may have been skipped. */ protected final JsonToken _finishTokenWithEOF() throws IOException { // NOTE: caller ensures there's input available... JsonToken t = _currToken; switch (_minorState) { case MINOR_ROOT_GOT_SEPARATOR: // fine, just skip some trailing space return _eofAsNextToken(); case MINOR_VALUE_LEADING_WS: // finished at token boundary; probably fine return _eofAsNextToken(); // case MINOR_VALUE_EXPECTING_COMMA: // not fine // case MINOR_VALUE_EXPECTING_COLON: // not fine case MINOR_VALUE_TOKEN_NULL: return _finishKeywordTokenWithEOF("null", _pending32, JsonToken.VALUE_NULL); case MINOR_VALUE_TOKEN_TRUE: return _finishKeywordTokenWithEOF("true", _pending32, JsonToken.VALUE_TRUE); case MINOR_VALUE_TOKEN_FALSE: return _finishKeywordTokenWithEOF("false", _pending32, JsonToken.VALUE_FALSE); case MINOR_VALUE_TOKEN_NON_STD: return _finishNonStdTokenWithEOF(_nonStdTokenType, _pending32); case MINOR_VALUE_TOKEN_ERROR: // case of "almost token", just need tokenize for error return _finishErrorTokenWithEOF(); // Number-parsing states; valid stopping points, more explicit errors case MINOR_NUMBER_ZERO: case MINOR_NUMBER_MINUSZERO: // NOTE: does NOT retain possible leading minus-sign (can change if // absolutely needs be) return _valueCompleteInt(0, "0"); case MINOR_NUMBER_INTEGER_DIGITS: // Fine: just need to ensure we have value fully defined { int len = _textBuffer.getCurrentSegmentSize(); if (_numberNegative) { --len; } _intLength = len; } return _valueComplete(JsonToken.VALUE_NUMBER_INT); case MINOR_NUMBER_FRACTION_DIGITS: _expLength = 0; // fall through case MINOR_NUMBER_EXPONENT_DIGITS: return _valueComplete(JsonToken.VALUE_NUMBER_FLOAT); case MINOR_NUMBER_EXPONENT_MARKER: _reportInvalidEOF(": was expecting fraction after exponent marker", JsonToken.VALUE_NUMBER_FLOAT); // How about comments? // Inside C-comments; not legal // case MINOR_COMMENT_LEADING_SLASH: // not legal, but use default error case MINOR_COMMENT_CLOSING_ASTERISK: case MINOR_COMMENT_C: _reportInvalidEOF(": was expecting closing '*/' for comment", JsonToken.NOT_AVAILABLE); case MINOR_COMMENT_CPP: case MINOR_COMMENT_YAML: // within C++/YAML comments, ok, as long as major state agrees... return _eofAsNextToken(); default: } _reportInvalidEOF(": was expecting rest of token (internal state: "+_minorState+")", _currToken); return t; // never gets here } /* /********************************************************************** /* Second-level decoding, root level /********************************************************************** */ private final JsonToken _startDocument(int ch) throws IOException { ch &= 0xFF; // Very first byte: could be BOM if ((ch == 0xEF) && (_minorState != MINOR_ROOT_BOM)) { return _finishBOM(1); } // If not BOM (or we got past it), could be whitespace or comment to skip while (ch <= 0x020) { if (ch != INT_SPACE) { if (ch == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (ch == INT_CR) { ++_currInputRowAlt; _currInputRowStart = _inputPtr; } else if (ch != INT_TAB) { _throwInvalidSpace(ch); } } if (_inputPtr >= _inputEnd) { _minorState = MINOR_ROOT_GOT_SEPARATOR; if (_closed) { return null; } // note: if so, do not even bother changing state if (_endOfInput) { // except for this special case return _eofAsNextToken(); } return JsonToken.NOT_AVAILABLE; } ch = _inputBuffer[_inputPtr++] & 0xFF; } return _startValue(ch); } private final JsonToken _finishBOM(int bytesHandled) throws IOException { // public final static byte UTF8_BOM_1 = (byte) 0xEF; // public final static byte UTF8_BOM_2 = (byte) 0xBB; // public final static byte UTF8_BOM_3 = (byte) 0xBF; while (_inputPtr < _inputEnd) { int ch = _inputBuffer[_inputPtr++] & 0xFF; switch (bytesHandled) { case 3: // got it all; go back to "start document" handling, without changing // minor state (to let it know we've done BOM) _currInputProcessed -= 3; return _startDocument(ch); case 2: if (ch != 0xBF) { _reportError("Unexpected byte 0x%02x following 0xEF 0xBB; should get 0xBF as third byte of UTF-8 BOM", ch); } break; case 1: if (ch != 0xBB) { _reportError("Unexpected byte 0x%02x following 0xEF; should get 0xBB as second byte UTF-8 BOM", ch); } break; } ++bytesHandled; } _pending32 = bytesHandled; _minorState = MINOR_ROOT_BOM; return (_currToken = JsonToken.NOT_AVAILABLE); } /* /********************************************************************** /* Second-level decoding, primary field name decoding /********************************************************************** */ /** * Method that handles initial token type recognition for token * that has to be either FIELD_NAME or END_OBJECT. */ private final JsonToken _startFieldName(int ch) throws IOException { // First: any leading white space? if (ch <= 0x0020) { ch = _skipWS(ch); if (ch <= 0) { _minorState = MINOR_FIELD_LEADING_WS; return _currToken; } } _updateTokenLocation(); if (ch != INT_QUOTE) { if (ch == INT_RCURLY) { return _closeObjectScope(); } return _handleOddName(ch); } // First: can we optimize out bounds checks? if ((_inputPtr + 13) <= _inputEnd) { // Need up to 12 chars, plus one trailing (quote) String n = _fastParseName(); if (n != null) { return _fieldComplete(n); } } return _parseEscapedName(0, 0, 0); } private final JsonToken _startFieldNameAfterComma(int ch) throws IOException { // First: any leading white space? if (ch <= 0x0020) { ch = _skipWS(ch); // will skip through all available ws (and comments) if (ch <= 0) { _minorState = MINOR_FIELD_LEADING_COMMA; return _currToken; } } if (ch != INT_COMMA) { // either comma, separating entries, or closing right curly if (ch == INT_RCURLY) { return _closeObjectScope(); } if (ch == INT_HASH) { return _finishHashComment(MINOR_FIELD_LEADING_COMMA); } if (ch == INT_SLASH) { return _startSlashComment(MINOR_FIELD_LEADING_COMMA); } _reportUnexpectedChar(ch, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries"); } int ptr = _inputPtr; if (ptr >= _inputEnd) { _minorState = MINOR_FIELD_LEADING_WS; return (_currToken = JsonToken.NOT_AVAILABLE); } ch = _inputBuffer[ptr]; _inputPtr = ptr+1; if (ch <= 0x0020) { ch = _skipWS(ch); if (ch <= 0) { _minorState = MINOR_FIELD_LEADING_WS; return _currToken; } } _updateTokenLocation(); if (ch != INT_QUOTE) { if (ch == INT_RCURLY) { if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { return _closeObjectScope(); } } return _handleOddName(ch); } // First: can we optimize out bounds checks? if ((_inputPtr + 13) <= _inputEnd) { // Need up to 12 chars, plus one trailing (quote) String n = _fastParseName(); if (n != null) { return _fieldComplete(n); } } return _parseEscapedName(0, 0, 0); } /* /********************************************************************** /* Second-level decoding, value decoding /********************************************************************** */ /** * Helper method called to detect type of a value token (at any level), and possibly * decode it if contained in input buffer. * Value may be preceded by leading white-space, but no separator (comma). */ private final JsonToken _startValue(int ch) throws IOException { // First: any leading white space? if (ch <= 0x0020) { ch = _skipWS(ch); if (ch <= 0) { _minorState = MINOR_VALUE_LEADING_WS; return _currToken; } } _updateTokenLocation(); // 17-Sep-2019, tatu: [core#563] Need to call this to update index within array _parsingContext.expectComma(); if (ch == INT_QUOTE) { return _startString(); } switch (ch) { case '#': return _finishHashComment(MINOR_VALUE_LEADING_WS); case '-': return _startNegativeNumber(); case '/': // c/c++ comments return _startSlashComment(MINOR_VALUE_LEADING_WS); // 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': return _startNumberLeadingZero(); case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return _startPositiveNumber(ch); case 'f': return _startFalseToken(); case 'n': return _startNullToken(); case 't': return _startTrueToken(); case '[': return _startArrayScope(); case INT_RBRACKET: return _closeArrayScope(); case '{': return _startObjectScope(); case INT_RCURLY: return _closeObjectScope(); default: } return _startUnexpectedValue(false, ch); } /** * Helper method called to parse token that is either a value token in array * or end-array marker */ private final JsonToken _startValueExpectComma(int ch) throws IOException { // First: any leading white space? if (ch <= 0x0020) { ch = _skipWS(ch); // will skip through all available ws (and comments) if (ch <= 0) { _minorState = MINOR_VALUE_EXPECTING_COMMA; return _currToken; } } if (ch != INT_COMMA) { if (ch == INT_RBRACKET) { return _closeArrayScope(); } if (ch == INT_RCURLY){ return _closeObjectScope(); } if (ch == INT_SLASH) { return _startSlashComment(MINOR_VALUE_EXPECTING_COMMA); } if (ch == INT_HASH) { return _finishHashComment(MINOR_VALUE_EXPECTING_COMMA); } _reportUnexpectedChar(ch, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries"); } // 17-Sep-2019, tatu: [core#563] Need to call this to update index within array _parsingContext.expectComma(); int ptr = _inputPtr; if (ptr >= _inputEnd) { _minorState = MINOR_VALUE_WS_AFTER_COMMA; return (_currToken = JsonToken.NOT_AVAILABLE); } ch = _inputBuffer[ptr]; _inputPtr = ptr+1; if (ch <= 0x0020) { ch = _skipWS(ch); if (ch <= 0) { _minorState = MINOR_VALUE_WS_AFTER_COMMA; return _currToken; } } _updateTokenLocation(); if (ch == INT_QUOTE) { return _startString(); } switch (ch) { case '#': return _finishHashComment(MINOR_VALUE_WS_AFTER_COMMA); case '-': return _startNegativeNumber(); case '/': return _startSlashComment(MINOR_VALUE_WS_AFTER_COMMA); // 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': return _startNumberLeadingZero(); case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return _startPositiveNumber(ch); case 'f': return _startFalseToken(); case 'n': return _startNullToken(); case 't': return _startTrueToken(); case '[': return _startArrayScope(); case INT_RBRACKET: // Was that a trailing comma? if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { return _closeArrayScope(); } break; case '{': return _startObjectScope(); case INT_RCURLY: // Was that a trailing comma? if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { return _closeObjectScope(); } break; default: } return _startUnexpectedValue(true, ch); } /** * Helper method called to detect type of a value token (at any level), and possibly * decode it if contained in input buffer. * Value MUST be preceded by a semi-colon (which may be surrounded by white-space) */ private final JsonToken _startValueExpectColon(int ch) throws IOException { // First: any leading white space? if (ch <= 0x0020) { ch = _skipWS(ch); // will skip through all available ws (and comments) if (ch <= 0) { _minorState = MINOR_VALUE_EXPECTING_COLON; return _currToken; } } if (ch != INT_COLON) { if (ch == INT_SLASH) { return _startSlashComment(MINOR_VALUE_EXPECTING_COLON); } if (ch == INT_HASH) { return _finishHashComment(MINOR_VALUE_EXPECTING_COLON); } // can not omit colon here _reportUnexpectedChar(ch, "was expecting a colon to separate field name and value"); } int ptr = _inputPtr; if (ptr >= _inputEnd) { _minorState = MINOR_VALUE_LEADING_WS; return (_currToken = JsonToken.NOT_AVAILABLE); } ch = _inputBuffer[ptr]; _inputPtr = ptr+1; if (ch <= 0x0020) { ch = _skipWS(ch); // will skip through all available ws (and comments) if (ch <= 0) { _minorState = MINOR_VALUE_LEADING_WS; return _currToken; } } _updateTokenLocation(); if (ch == INT_QUOTE) { return _startString(); } switch (ch) { case '#': return _finishHashComment(MINOR_VALUE_LEADING_WS); case '-': return _startNegativeNumber(); case '/': return _startSlashComment(MINOR_VALUE_LEADING_WS); // 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': return _startNumberLeadingZero(); case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return _startPositiveNumber(ch); case 'f': return _startFalseToken(); case 'n': return _startNullToken(); case 't': return _startTrueToken(); case '[': return _startArrayScope(); case '{': return _startObjectScope(); default: } return _startUnexpectedValue(false, ch); } /* Method called when we have already gotten a comma (i.e. not the first value) */ private final JsonToken _startValueAfterComma(int ch) throws IOException { // First: any leading white space? if (ch <= 0x0020) { ch = _skipWS(ch); if (ch <= 0) { _minorState = MINOR_VALUE_WS_AFTER_COMMA; return _currToken; } } _updateTokenLocation(); if (ch == INT_QUOTE) { return _startString(); } switch (ch) { case '#': return _finishHashComment(MINOR_VALUE_WS_AFTER_COMMA); case '-': return _startNegativeNumber(); case '/': return _startSlashComment(MINOR_VALUE_WS_AFTER_COMMA); // 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': return _startNumberLeadingZero(); case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return _startPositiveNumber(ch); case 'f': return _startFalseToken(); case 'n': return _startNullToken(); case 't': return _startTrueToken(); case '[': return _startArrayScope(); case INT_RBRACKET: // Was that a trailing comma? if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { return _closeArrayScope(); } break; case '{': return _startObjectScope(); case INT_RCURLY: // Was that a trailing comma? if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) { return _closeObjectScope(); } break; default: } return _startUnexpectedValue(true, ch); } protected JsonToken _startUnexpectedValue(boolean leadingComma, int ch) throws IOException { switch (ch) { case INT_RBRACKET: 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 ((_features & FEAT_MASK_ALLOW_MISSING) != 0) { --_inputPtr; return _valueComplete(JsonToken.VALUE_NULL); } // fall through case INT_RCURLY: // Error: neither is valid at this point; valid closers have // been handled earlier break; case '\'': if ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { return _startAposString(); } break; case '+': return _finishNonStdToken(NON_STD_TOKEN_PLUS_INFINITY, 1); case 'N': return _finishNonStdToken(NON_STD_TOKEN_NAN, 1); case 'I': return _finishNonStdToken(NON_STD_TOKEN_INFINITY, 1); } // !!! TODO: maybe try to collect more information for better diagnostics _reportUnexpectedChar(ch, "expected a valid value "+_validJsonValueList()); return null; } /* /********************************************************************** /* Second-level decoding, skipping white-space, comments /********************************************************************** */ private final int _skipWS(int ch) throws IOException { do { if (ch != INT_SPACE) { if (ch == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (ch == INT_CR) { ++_currInputRowAlt; _currInputRowStart = _inputPtr; } else if (ch != INT_TAB) { _throwInvalidSpace(ch); } } if (_inputPtr >= _inputEnd) { _currToken = JsonToken.NOT_AVAILABLE; return 0; } ch = _inputBuffer[_inputPtr++] & 0xFF; } while (ch <= 0x0020); return ch; } private final JsonToken _startSlashComment(int fromMinorState) throws IOException { if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) { _reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)"); } // After that, need to verify if we have c/c++ comment if (_inputPtr >= _inputEnd) { _pending32 = fromMinorState; _minorState = MINOR_COMMENT_LEADING_SLASH; return (_currToken = JsonToken.NOT_AVAILABLE); } int ch = _inputBuffer[_inputPtr++]; if (ch == INT_ASTERISK) { // c-style return _finishCComment(fromMinorState, false); } if (ch == INT_SLASH) { // c++-style return _finishCppComment(fromMinorState); } _reportUnexpectedChar(ch & 0xFF, "was expecting either '*' or '/' for a comment"); return null; } private final JsonToken _finishHashComment(int fromMinorState) throws IOException { // Could by-pass this check by refactoring, but for now simplest way... if ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) == 0) { _reportUnexpectedChar('#', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_YAML_COMMENTS' not enabled for parser)"); } while (true) { if (_inputPtr >= _inputEnd) { _minorState = MINOR_COMMENT_YAML; _pending32 = fromMinorState; return (_currToken = JsonToken.NOT_AVAILABLE); } int ch = _inputBuffer[_inputPtr++] & 0xFF; if (ch < 0x020) { if (ch == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; break; } else if (ch == INT_CR) { ++_currInputRowAlt; _currInputRowStart = _inputPtr; break; } else if (ch != INT_TAB) { _throwInvalidSpace(ch); } } } return _startAfterComment(fromMinorState); } private final JsonToken _finishCppComment(int fromMinorState) throws IOException { while (true) { if (_inputPtr >= _inputEnd) { _minorState = MINOR_COMMENT_CPP; _pending32 = fromMinorState; return (_currToken = JsonToken.NOT_AVAILABLE); } int ch = _inputBuffer[_inputPtr++] & 0xFF; if (ch < 0x020) { if (ch == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; break; } else if (ch == INT_CR) { ++_currInputRowAlt; _currInputRowStart = _inputPtr; break; } else if (ch != INT_TAB) { _throwInvalidSpace(ch); } } } return _startAfterComment(fromMinorState); } private final JsonToken _finishCComment(int fromMinorState, boolean gotStar) throws IOException { while (true) { if (_inputPtr >= _inputEnd) { _minorState = gotStar ? MINOR_COMMENT_CLOSING_ASTERISK : MINOR_COMMENT_C; _pending32 = fromMinorState; return (_currToken = JsonToken.NOT_AVAILABLE); } int ch = _inputBuffer[_inputPtr++] & 0xFF; if (ch < 0x020) { if (ch == INT_LF) { ++_currInputRow; _currInputRowStart = _inputPtr; } else if (ch == INT_CR) { ++_currInputRowAlt; _currInputRowStart = _inputPtr; } else if (ch != INT_TAB) { _throwInvalidSpace(ch); } } else if (ch == INT_ASTERISK) { gotStar = true; continue; } else if (ch == INT_SLASH) { if (gotStar) { break; } } gotStar = false; } return _startAfterComment(fromMinorState); } private final JsonToken _startAfterComment(int fromMinorState) throws IOException { // Ok, then, need one more character... if (_inputPtr >= _inputEnd) { _minorState = fromMinorState; return (_currToken = JsonToken.NOT_AVAILABLE); } int ch = _inputBuffer[_inputPtr++] & 0xFF; switch (fromMinorState) { case MINOR_FIELD_LEADING_WS: return _startFieldName(ch); case MINOR_FIELD_LEADING_COMMA: return _startFieldNameAfterComma(ch); case MINOR_VALUE_LEADING_WS: return _startValue(ch); case MINOR_VALUE_EXPECTING_COMMA: return _startValueExpectComma(ch); case MINOR_VALUE_EXPECTING_COLON: return _startValueExpectColon(ch); case MINOR_VALUE_WS_AFTER_COMMA: return _startValueAfterComma(ch); default: } VersionUtil.throwInternal(); return null; } /* /********************************************************************** /* Tertiary decoding, simple tokens /********************************************************************** */ protected JsonToken _startFalseToken() throws IOException { int ptr = _inputPtr; if ((ptr + 4) < _inputEnd) { // yes, can determine efficiently byte[] buf = _inputBuffer; if ((buf[ptr++] == 'a') && (buf[ptr++] == 'l') && (buf[ptr++] == 's') && (buf[ptr++] == 'e')) { int ch = buf[ptr] & 0xFF; if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars _inputPtr = ptr; return _valueComplete(JsonToken.VALUE_FALSE); } } } _minorState = MINOR_VALUE_TOKEN_FALSE; return _finishKeywordToken("false", 1, JsonToken.VALUE_FALSE); } protected JsonToken _startTrueToken() throws IOException { int ptr = _inputPtr; if ((ptr + 3) < _inputEnd) { // yes, can determine efficiently byte[] buf = _inputBuffer; if ((buf[ptr++] == 'r') && (buf[ptr++] == 'u') && (buf[ptr++] == 'e')) { int ch = buf[ptr] & 0xFF; if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars _inputPtr = ptr; return _valueComplete(JsonToken.VALUE_TRUE); } } } _minorState = MINOR_VALUE_TOKEN_TRUE; return _finishKeywordToken("true", 1, JsonToken.VALUE_TRUE); } protected JsonToken _startNullToken() throws IOException { int ptr = _inputPtr; if ((ptr + 3) < _inputEnd) { // yes, can determine efficiently byte[] buf = _inputBuffer; if ((buf[ptr++] == 'u') && (buf[ptr++] == 'l') && (buf[ptr++] == 'l')) { int ch = buf[ptr] & 0xFF; if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars _inputPtr = ptr; return _valueComplete(JsonToken.VALUE_NULL); } } } _minorState = MINOR_VALUE_TOKEN_NULL; return _finishKeywordToken("null", 1, JsonToken.VALUE_NULL); } protected JsonToken _finishKeywordToken(String expToken, int matched, JsonToken result) throws IOException { final int end = expToken.length(); while (true) { if (_inputPtr >= _inputEnd) { _pending32 = matched; return (_currToken = JsonToken.NOT_AVAILABLE); } int ch = _inputBuffer[_inputPtr]; if (matched == end) { // need to verify trailing separator if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars return _valueComplete(result); } break; } if (ch != expToken.charAt(matched)) { break; } ++matched; ++_inputPtr; } _minorState = MINOR_VALUE_TOKEN_ERROR; _textBuffer.resetWithCopy(expToken, 0, matched); return _finishErrorToken(); } protected JsonToken _finishKeywordTokenWithEOF(String expToken, int matched, JsonToken result) throws IOException { if (matched == expToken.length()) { return (_currToken = result); } _textBuffer.resetWithCopy(expToken, 0, matched); return _finishErrorTokenWithEOF(); } protected JsonToken _finishNonStdToken(int type, int matched) throws IOException { final String expToken = _nonStdToken(type); final int end = expToken.length(); while (true) { if (_inputPtr >= _inputEnd) { _nonStdTokenType = type; _pending32 = matched; _minorState = MINOR_VALUE_TOKEN_NON_STD; return (_currToken = JsonToken.NOT_AVAILABLE); } int ch = _inputBuffer[_inputPtr]; if (matched == end) { // need to verify trailing separator if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars return _valueNonStdNumberComplete(type); } break; } if (ch != expToken.charAt(matched)) { break; } ++matched; ++_inputPtr; } _minorState = MINOR_VALUE_TOKEN_ERROR; _textBuffer.resetWithCopy(expToken, 0, matched); return _finishErrorToken(); } protected JsonToken _finishNonStdTokenWithEOF(int type, int matched) throws IOException { final String expToken = _nonStdToken(type); if (matched == expToken.length()) { return _valueNonStdNumberComplete(type); } _textBuffer.resetWithCopy(expToken, 0, matched); return _finishErrorTokenWithEOF(); } protected JsonToken _finishErrorToken() throws IOException { while (_inputPtr < _inputEnd) { int i = (int) _inputBuffer[_inputPtr++]; // !!! TODO: Decode UTF-8 characters properly... // char c = (char) _decodeCharForError(i); char ch = (char) i; if (Character.isJavaIdentifierPart(ch)) { // 11-Jan-2016, tatu: note: we will fully consume the character, // included or not, so if recovery was possible, it'd be off-by-one... _textBuffer.append(ch); if (_textBuffer.size() < MAX_ERROR_TOKEN_LENGTH) { continue; } } return _reportErrorToken(_textBuffer.contentsAsString()); } return (_currToken = JsonToken.NOT_AVAILABLE); } protected JsonToken _finishErrorTokenWithEOF() throws IOException { return _reportErrorToken(_textBuffer.contentsAsString()); } protected JsonToken _reportErrorToken(String actualToken) throws IOException { // !!! TODO: Include non-standard ones if enabled _reportError("Unrecognized token '%s': was expecting %s", _textBuffer.contentsAsString(), _validJsonTokenList()); return JsonToken.NOT_AVAILABLE; // never gets here } /* /********************************************************************** /* Second-level decoding, Number decoding /********************************************************************** */ protected JsonToken _startPositiveNumber(int ch) throws IOException { _numberNegative = false; char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); outBuf[0] = (char) ch; // in unlikely event of not having more input, denote location if (_inputPtr >= _inputEnd) { _minorState = MINOR_NUMBER_INTEGER_DIGITS; _textBuffer.setCurrentLength(1); return (_currToken = JsonToken.NOT_AVAILABLE); } int outPtr = 1; ch = _inputBuffer[_inputPtr] & 0xFF; while (true) { if (ch < INT_0) { if (ch == INT_PERIOD) { _intLength = outPtr; ++_inputPtr; return _startFloat(outBuf, outPtr, ch); } break; } if (ch > INT_9) { if (ch == INT_e || ch == INT_E) { _intLength = outPtr; ++_inputPtr; return _startFloat(outBuf, outPtr, ch); } break; } if (outPtr >= outBuf.length) { // NOTE: must expand to ensure contents all in a single buffer (to keep // other parts of parsing simpler) outBuf = _textBuffer.expandCurrentSegment(); } outBuf[outPtr++] = (char) ch; if (++_inputPtr >= _inputEnd) { _minorState = MINOR_NUMBER_INTEGER_DIGITS; _textBuffer.setCurrentLength(outPtr); return (_currToken = JsonToken.NOT_AVAILABLE); } ch = _inputBuffer[_inputPtr] & 0xFF; } _intLength = outPtr; _textBuffer.setCurrentLength(outPtr); return _valueComplete(JsonToken.VALUE_NUMBER_INT); } protected JsonToken _startNegativeNumber() throws IOException { _numberNegative = true; if (_inputPtr >= _inputEnd) { _minorState = MINOR_NUMBER_MINUS; return (_currToken = JsonToken.NOT_AVAILABLE); } int ch = _inputBuffer[_inputPtr++] & 0xFF; if (ch <= INT_0) { if (ch == INT_0) { return _finishNumberLeadingNegZeroes(); } // One special case: if first char is 0, must not be followed by a digit reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value"); } else if (ch > INT_9) { if (ch == 'I') { return _finishNonStdToken(NON_STD_TOKEN_MINUS_INFINITY, 2); } reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value"); } char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); outBuf[0] = '-'; outBuf[1] = (char) ch; if (_inputPtr >= _inputEnd) { _minorState = MINOR_NUMBER_INTEGER_DIGITS; _textBuffer.setCurrentLength(2); _intLength = 1; return (_currToken = JsonToken.NOT_AVAILABLE); } ch = _inputBuffer[_inputPtr]; int outPtr = 2; while (true) { if (ch < INT_0) { if (ch == INT_PERIOD) { _intLength = outPtr-1; ++_inputPtr; return _startFloat(outBuf, outPtr, ch); } break; } if (ch > INT_9) { if (ch == INT_e || ch == INT_E) { _intLength = outPtr-1; ++_inputPtr; return _startFloat(outBuf, outPtr, ch); } break; } if (outPtr >= outBuf.length) { // NOTE: must expand, to ensure contiguous buffer, outPtr is the length outBuf = _textBuffer.expandCurrentSegment(); } outBuf[outPtr++] = (char) ch; if (++_inputPtr >= _inputEnd) { _minorState = MINOR_NUMBER_INTEGER_DIGITS; _textBuffer.setCurrentLength(outPtr); return (_currToken = JsonToken.NOT_AVAILABLE); } ch = _inputBuffer[_inputPtr] & 0xFF; } _intLength = outPtr-1; _textBuffer.setCurrentLength(outPtr); return _valueComplete(JsonToken.VALUE_NUMBER_INT); } protected JsonToken _startNumberLeadingZero() throws IOException { int ptr = _inputPtr; if (ptr >= _inputEnd) { _minorState = MINOR_NUMBER_ZERO; return (_currToken = JsonToken.NOT_AVAILABLE); } // While we could call `_finishNumberLeadingZeroes()`, let's try checking // the very first char after first zero since the most common case is that // there is a separator int ch = _inputBuffer[ptr++] & 0xFF; // one early check: leading zeroes may or may not be allowed if (ch < INT_0) { if (ch == INT_PERIOD) { _inputPtr = ptr; _intLength = 1; char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); outBuf[0] = '0'; return _startFloat(outBuf, 1, ch); } } else if (ch > INT_9) { if (ch == INT_e || ch == INT_E) { _inputPtr = ptr; _intLength = 1; char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); outBuf[0] = '0'; return _startFloat(outBuf, 1, ch); } // Ok; unfortunately we have closing bracket/curly that are valid so need // (colon not possible since this is within value, not after key) // if ((ch != INT_RBRACKET) && (ch != INT_RCURLY)) { reportUnexpectedNumberChar(ch, "expected digit (0-9), decimal point (.) or exponent indicator (e/E) to follow '0'"); } } else { // leading zero case (zero followed by a digit) // leave inputPtr as is (i.e. "push back" digit) return _finishNumberLeadingZeroes(); } // leave _inputPtr as-is, to push back byte we checked return _valueCompleteInt(0, "0"); } protected JsonToken _finishNumberMinus(int ch) throws IOException { if (ch <= INT_0) { if (ch == INT_0) { return _finishNumberLeadingNegZeroes(); } reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value"); } else if (ch > INT_9) { if (ch == 'I') { return _finishNonStdToken(NON_STD_TOKEN_MINUS_INFINITY, 2); } reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value"); } char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); outBuf[0] = '-'; outBuf[1] = (char) ch; _intLength = 1; return _finishNumberIntegralPart(outBuf, 2); } protected JsonToken _finishNumberLeadingZeroes() throws IOException { // In general, skip further zeroes (if allowed), look for legal follow-up // numeric characters; likely legal separators, or, known illegal (letters). while (true) { if (_inputPtr >= _inputEnd) { _minorState = MINOR_NUMBER_ZERO; return (_currToken = JsonToken.NOT_AVAILABLE); } int ch = _inputBuffer[_inputPtr++] & 0xFF; if (ch < INT_0) { if (ch == INT_PERIOD) { char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); outBuf[0] = '0'; _intLength = 1; return _startFloat(outBuf, 1, ch); } } else if (ch > INT_9) { if (ch == INT_e || ch == INT_E) { char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); outBuf[0] = '0'; _intLength = 1; return _startFloat(outBuf, 1, ch); } // Ok; unfortunately we have closing bracket/curly that are valid so need // (colon not possible since this is within value, not after key) // if ((ch != INT_RBRACKET) && (ch != INT_RCURLY)) { reportUnexpectedNumberChar(ch, "expected digit (0-9), decimal point (.) or exponent indicator (e/E) to follow '0'"); } } else { // Number between 0 and 9 // although not guaranteed, seems likely valid separator (white space, // comma, end bracket/curly); next time token needed will verify if ((_features & FEAT_MASK_LEADING_ZEROS) == 0) { reportInvalidNumber("Leading zeroes not allowed"); } if (ch == INT_0) { // coalesce multiple leading zeroes into just one continue; } char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); // trim out leading zero outBuf[0] = (char) ch; _intLength = 1; return _finishNumberIntegralPart(outBuf, 1); } --_inputPtr; return _valueCompleteInt(0, "0"); } } protected JsonToken _finishNumberLeadingNegZeroes() throws IOException { // In general, skip further zeroes (if allowed), look for legal follow-up // numeric characters; likely legal separators, or, known illegal (letters). while (true) { if (_inputPtr >= _inputEnd) { _minorState = MINOR_NUMBER_MINUSZERO; return (_currToken = JsonToken.NOT_AVAILABLE); } int ch = _inputBuffer[_inputPtr++] & 0xFF; if (ch < INT_0) { if (ch == INT_PERIOD) { char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); outBuf[0] = '-'; outBuf[1] = '0'; _intLength = 1; return _startFloat(outBuf, 2, ch); } } else if (ch > INT_9) { if (ch == INT_e || ch == INT_E) { char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); outBuf[0] = '-'; outBuf[1] = '0'; _intLength = 1; return _startFloat(outBuf, 2, ch); } // Ok; unfortunately we have closing bracket/curly that are valid so need // (colon not possible since this is within value, not after key) // if ((ch != INT_RBRACKET) && (ch != INT_RCURLY)) { reportUnexpectedNumberChar(ch, "expected digit (0-9), decimal point (.) or exponent indicator (e/E) to follow '0'"); } } else { // Number between 1 and 9; go integral // although not guaranteed, seems likely valid separator (white space, // comma, end bracket/curly); next time token needed will verify if ((_features & FEAT_MASK_LEADING_ZEROS) == 0) { reportInvalidNumber("Leading zeroes not allowed"); } if (ch == INT_0) { // coalesce multiple leading zeroes into just one continue; } char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); // trim out leading zero outBuf[0] = '-'; outBuf[1] = (char) ch; _intLength = 1; return _finishNumberIntegralPart(outBuf, 2); } --_inputPtr; return _valueCompleteInt(0, "0"); } } protected JsonToken _finishNumberIntegralPart(char[] outBuf, int outPtr) throws IOException { int negMod = _numberNegative ? -1 : 0; while (true) { if (_inputPtr >= _inputEnd) { _minorState = MINOR_NUMBER_INTEGER_DIGITS; _textBuffer.setCurrentLength(outPtr); return (_currToken = JsonToken.NOT_AVAILABLE); } int ch = _inputBuffer[_inputPtr] & 0xFF; if (ch < INT_0) { if (ch == INT_PERIOD) { _intLength = outPtr+negMod; ++_inputPtr; return _startFloat(outBuf, outPtr, ch); } break; } if (ch > INT_9) { if (ch == INT_e || ch == INT_E) { _intLength = outPtr+negMod; ++_inputPtr; return _startFloat(outBuf, outPtr, ch); } break; } ++_inputPtr; if (outPtr >= outBuf.length) { // NOTE: must expand to ensure contents all in a single buffer (to keep // other parts of parsing simpler) outBuf = _textBuffer.expandCurrentSegment(); } outBuf[outPtr++] = (char) ch; } _intLength = outPtr+negMod; _textBuffer.setCurrentLength(outPtr); return _valueComplete(JsonToken.VALUE_NUMBER_INT); } protected JsonToken _startFloat(char[] outBuf, int outPtr, int ch) throws IOException { int fractLen = 0; if (ch == INT_PERIOD) { if (outPtr >= outBuf.length) { outBuf = _textBuffer.expandCurrentSegment(); } outBuf[outPtr++] = '.'; while (true) { if (_inputPtr >= _inputEnd) { _textBuffer.setCurrentLength(outPtr); _minorState = MINOR_NUMBER_FRACTION_DIGITS; _fractLength = fractLen; return (_currToken = JsonToken.NOT_AVAILABLE); } ch = _inputBuffer[_inputPtr++]; // ok to have sign extension for now if (ch < INT_0 || ch > INT_9) { ch &= 0xFF; // but here we'll want to mask it to unsigned 8-bit // must be followed by sequence of ints, one minimum if (fractLen == 0) { reportUnexpectedNumberChar(ch, "Decimal point not followed by a digit"); } break; } if (outPtr >= outBuf.length) { outBuf = _textBuffer.expandCurrentSegment(); } outBuf[outPtr++] = (char) ch; ++fractLen; } } _fractLength = fractLen; int expLen = 0; if (ch == INT_e || ch == INT_E) { // exponent? if (outPtr >= outBuf.length) { outBuf = _textBuffer.expandCurrentSegment(); } outBuf[outPtr++] = (char) ch; if (_inputPtr >= _inputEnd) { _textBuffer.setCurrentLength(outPtr); _minorState = MINOR_NUMBER_EXPONENT_MARKER; _expLength = 0; return (_currToken = JsonToken.NOT_AVAILABLE); } ch = _inputBuffer[_inputPtr++]; // ok to have sign extension for now if (ch == INT_MINUS || ch == INT_PLUS) { if (outPtr >= outBuf.length) { outBuf = _textBuffer.expandCurrentSegment(); } outBuf[outPtr++] = (char) ch; if (_inputPtr >= _inputEnd) { _textBuffer.setCurrentLength(outPtr); _minorState = MINOR_NUMBER_EXPONENT_DIGITS; _expLength = 0; return (_currToken = JsonToken.NOT_AVAILABLE); } ch = _inputBuffer[_inputPtr++]; } while (ch >= INT_0 && ch <= INT_9) { ++expLen; if (outPtr >= outBuf.length) { outBuf = _textBuffer.expandCurrentSegment(); } outBuf[outPtr++] = (char) ch; if (_inputPtr >= _inputEnd) { _textBuffer.setCurrentLength(outPtr); _minorState = MINOR_NUMBER_EXPONENT_DIGITS; _expLength = expLen; return (_currToken = JsonToken.NOT_AVAILABLE); } ch = _inputBuffer[_inputPtr++]; } // must be followed by sequence of ints, one minimum ch &= 0xFF; if (expLen == 0) { reportUnexpectedNumberChar(ch, "Exponent indicator not followed by a digit"); } } // push back the last char --_inputPtr; _textBuffer.setCurrentLength(outPtr); // negative, int-length, fract-length already set, so... _expLength = expLen; return _valueComplete(JsonToken.VALUE_NUMBER_FLOAT); } protected JsonToken _finishFloatFraction() throws IOException { int fractLen = _fractLength; char[] outBuf = _textBuffer.getBufferWithoutReset(); int outPtr = _textBuffer.getCurrentSegmentSize(); // caller guarantees at least one char; also, sign-extension not needed here int ch; while (((ch = _inputBuffer[_inputPtr++]) >= INT_0) && (ch <= INT_9)) { ++fractLen; if (outPtr >= outBuf.length) { outBuf = _textBuffer.expandCurrentSegment(); } outBuf[outPtr++] = (char) ch; if (_inputPtr >= _inputEnd) { _textBuffer.setCurrentLength(outPtr); _fractLength = fractLen; return JsonToken.NOT_AVAILABLE; } } // Ok, fraction done; what have we got next? // must be followed by sequence of ints, one minimum if (fractLen == 0) { reportUnexpectedNumberChar(ch, "Decimal point not followed by a digit"); } _fractLength = fractLen; _textBuffer.setCurrentLength(outPtr); // Ok: end of floating point number or exponent? if (ch == INT_e || ch == INT_E) { // exponent? _textBuffer.append((char) ch); _expLength = 0; if (_inputPtr >= _inputEnd) { _minorState = MINOR_NUMBER_EXPONENT_MARKER; return JsonToken.NOT_AVAILABLE; } _minorState = MINOR_NUMBER_EXPONENT_DIGITS; return _finishFloatExponent(true, _inputBuffer[_inputPtr++] & 0xFF); } // push back the last char --_inputPtr; _textBuffer.setCurrentLength(outPtr); // negative, int-length, fract-length already set, so... _expLength = 0; return _valueComplete(JsonToken.VALUE_NUMBER_FLOAT); } protected JsonToken _finishFloatExponent(boolean checkSign, int ch) throws IOException { if (checkSign) { _minorState = MINOR_NUMBER_EXPONENT_DIGITS; if (ch == INT_MINUS || ch == INT_PLUS) { _textBuffer.append((char) ch); if (_inputPtr >= _inputEnd) { _minorState = MINOR_NUMBER_EXPONENT_DIGITS; _expLength = 0; return JsonToken.NOT_AVAILABLE; } ch = _inputBuffer[_inputPtr++]; } } char[] outBuf = _textBuffer.getBufferWithoutReset(); int outPtr = _textBuffer.getCurrentSegmentSize(); int expLen = _expLength; while (ch >= INT_0 && ch <= INT_9) { ++expLen; if (outPtr >= outBuf.length) { outBuf = _textBuffer.expandCurrentSegment(); } outBuf[outPtr++] = (char) ch; if (_inputPtr >= _inputEnd) { _textBuffer.setCurrentLength(outPtr); _expLength = expLen; return JsonToken.NOT_AVAILABLE; } ch = _inputBuffer[_inputPtr++]; } // must be followed by sequence of ints, one minimum ch &= 0xFF; if (expLen == 0) { reportUnexpectedNumberChar(ch, "Exponent indicator not followed by a digit"); } // push back the last char --_inputPtr; _textBuffer.setCurrentLength(outPtr); // negative, int-length, fract-length already set, so... _expLength = expLen; return _valueComplete(JsonToken.VALUE_NUMBER_FLOAT); } /* /********************************************************************** /* Name-decoding, tertiary decoding /********************************************************************** */ private final String _fastParseName() throws IOException { // If so, can also unroll loops nicely // 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 be // caught later on), and just handle quotes and backslashes here. final byte[] input = _inputBuffer; final int[] codes = _icLatin1; int ptr = _inputPtr; int q0 = input[ptr++] & 0xFF; if (codes[q0] == 0) { int i = input[ptr++] & 0xFF; if (codes[i] == 0) { int q = (q0 << 8) | i; i = input[ptr++] & 0xFF; if (codes[i] == 0) { q = (q << 8) | i; i = input[ptr++] & 0xFF; if (codes[i] == 0) { q = (q << 8) | i; i = input[ptr++] & 0xFF; if (codes[i] == 0) { _quad1 = q; return _parseMediumName(ptr, i); } if (i == INT_QUOTE) { // 4 byte/char case or broken _inputPtr = ptr; return _findName(q, 4); } return null; } if (i == INT_QUOTE) { // 3 byte/char case or broken _inputPtr = ptr; return _findName(q, 3); } return null; } if (i == INT_QUOTE) { // 2 byte/char case or broken _inputPtr = ptr; return _findName(q, 2); } return null; } if (i == INT_QUOTE) { // one byte/char case or broken _inputPtr = ptr; return _findName(q0, 1); } return null; } if (q0 == INT_QUOTE) { _inputPtr = ptr; return ""; } return null; } private final String _parseMediumName(int ptr, int q2) throws IOException { final byte[] input = _inputBuffer; final int[] codes = _icLatin1; // Ok, got 5 name bytes so far int i = input[ptr++] & 0xFF; if (codes[i] == 0) { q2 = (q2 << 8) | i; i = input[ptr++] & 0xFF; if (codes[i] == 0) { q2 = (q2 << 8) | i; i = input[ptr++] & 0xFF; if (codes[i] == 0) { q2 = (q2 << 8) | i; i = input[ptr++] & 0xFF; if (codes[i] == 0) { return _parseMediumName2(ptr, i, q2); } if (i == INT_QUOTE) { // 8 bytes _inputPtr = ptr; return _findName(_quad1, q2, 4); } return null; } if (i == INT_QUOTE) { // 7 bytes _inputPtr = ptr; return _findName(_quad1, q2, 3); } return null; } if (i == INT_QUOTE) { // 6 bytes _inputPtr = ptr; return _findName(_quad1, q2, 2); } return null; } if (i == INT_QUOTE) { // 5 bytes _inputPtr = ptr; return _findName(_quad1, q2, 1); } return null; } private final String _parseMediumName2(int ptr, int q3, final int q2) throws IOException { final byte[] input = _inputBuffer; final int[] codes = _icLatin1; // Got 9 name bytes so far int i = input[ptr++] & 0xFF; if (codes[i] != 0) { if (i == INT_QUOTE) { // 9 bytes _inputPtr = ptr; return _findName(_quad1, q2, q3, 1); } return null; } q3 = (q3 << 8) | i; i = input[ptr++] & 0xFF; if (codes[i] != 0) { if (i == INT_QUOTE) { // 10 bytes _inputPtr = ptr; return _findName(_quad1, q2, q3, 2); } return null; } q3 = (q3 << 8) | i; i = input[ptr++] & 0xFF; if (codes[i] != 0) { if (i == INT_QUOTE) { // 11 bytes _inputPtr = ptr; return _findName(_quad1, q2, q3, 3); } return null; } q3 = (q3 << 8) | i; i = input[ptr++] & 0xFF; if (i == INT_QUOTE) { // 12 bytes _inputPtr = ptr; return _findName(_quad1, q2, q3, 4); } // Could continue return null; } /** * 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 hence is offlined to a separate method. */ private final JsonToken _parseEscapedName(int qlen, int currQuad, int currQuadBytes) throws IOException { // 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. int[] quads = _quadBuffer; final int[] codes = _icLatin1; while (true) { if (_inputPtr >= _inputEnd) { _quadLength = qlen; _pending32 = currQuad; _pendingBytes = currQuadBytes; _minorState = MINOR_FIELD_NAME; return (_currToken = JsonToken.NOT_AVAILABLE); } int ch = _inputBuffer[_inputPtr++] & 0xFF; if (codes[ch] == 0) { if (currQuadBytes < 4) { ++currQuadBytes; currQuad = (currQuad << 8) | ch; continue; } if (qlen >= quads.length) { _quadBuffer = quads = growArrayBy(quads, quads.length); } quads[qlen++] = currQuad; currQuad = ch; currQuadBytes = 1; continue; } // Otherwise bit longer handling if (ch == INT_QUOTE) { // we are done break; } // Unquoted white space? if (ch != INT_BACKSLASH) { // Call can actually now return (if unquoted linefeeds allowed) _throwUnquotedSpace(ch, "name"); } else { // Nope, escape sequence ch = _decodeCharEscape(); if (ch < 0) { // method has set up state about escape sequence _minorState = MINOR_FIELD_NAME_ESCAPE; _minorStateAfterSplit = MINOR_FIELD_NAME; _quadLength = qlen; _pending32 = currQuad; _pendingBytes = currQuadBytes; return (_currToken = JsonToken.NOT_AVAILABLE); } } // 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 (qlen >= quads.length) { _quadBuffer = quads = growArrayBy(quads, quads.length); } if (ch > 127) { // Ok, we'll need room for first byte right away if (currQuadBytes >= 4) { 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) { 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); } if (currQuadBytes < 4) { ++currQuadBytes; currQuad = (currQuad << 8) | ch; continue; } quads[qlen++] = currQuad; currQuad = ch; currQuadBytes = 1; } if (currQuadBytes > 0) { if (qlen >= quads.length) { _quadBuffer = quads = growArrayBy(quads, quads.length); } quads[qlen++] = _padLastQuad(currQuad, currQuadBytes); } else if (qlen == 0) { // rare, but may happen return _fieldComplete(""); } String name = _symbols.findName(quads, qlen); if (name == null) { name = _addName(quads, qlen, currQuadBytes); } return _fieldComplete(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. */ private JsonToken _handleOddName(int ch) throws IOException { // First: may allow single quotes switch (ch) { case '#': // Careful, since this may alternatively be leading char of // unquoted name... if ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) != 0) { return _finishHashComment(MINOR_FIELD_LEADING_WS); } break; case '/': return _startSlashComment(MINOR_FIELD_LEADING_WS); case '\'': if ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) { return _finishAposName(0, 0, 0); } break; case INT_RBRACKET: // for better error reporting... return _closeArrayScope(); } // allow unquoted names if feature enabled: if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) { // !!! TODO: Decode UTF-8 characters properly... // char c = (char) _decodeCharForError(ch); char c = (char) 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"); } return _finishUnquotedName(0, ch, 1); } /** * Parsing of optionally supported non-standard "unquoted" names: names without * either double-quotes or apostrophes surrounding them. * Unlike other */ private JsonToken _finishUnquotedName(int qlen, int currQuad, int currQuadBytes) throws IOException { int[] quads = _quadBuffer; final int[] codes = CharTypes.getInputCodeUtf8JsNames(); // 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. while (true) { if (_inputPtr >= _inputEnd) { _quadLength = qlen; _pending32 = currQuad; _pendingBytes = currQuadBytes; _minorState = MINOR_FIELD_UNQUOTED_NAME; return (_currToken = JsonToken.NOT_AVAILABLE); } int ch = _inputBuffer[_inputPtr] & 0xFF; if (codes[ch] != 0) { break; } ++_inputPtr; // 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; } } 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 _fieldComplete(name); } private JsonToken _finishAposName(int qlen, int currQuad, int currQuadBytes) throws IOException { int[] quads = _quadBuffer; final int[] codes = _icLatin1; while (true) { if (_inputPtr >= _inputEnd) { _quadLength = qlen; _pending32 = currQuad; _pendingBytes = currQuadBytes; _minorState = MINOR_FIELD_APOS_NAME; return (_currToken = JsonToken.NOT_AVAILABLE); } int ch = _inputBuffer[_inputPtr++] & 0xFF; if (ch == INT_APOS) { break; } // additional check to skip handling of double-quotes if (ch != '"' && codes[ch] != 0) { if (ch != '\\') { // Unquoted white space? _throwUnquotedSpace(ch, "name"); } else { // Nope, escape sequence ch = _decodeCharEscape(); if (ch < 0) { // method has set up state about escape sequence _minorState = MINOR_FIELD_NAME_ESCAPE; _minorStateAfterSplit = MINOR_FIELD_APOS_NAME; _quadLength = qlen; _pending32 = currQuad; _pendingBytes = currQuadBytes; return (_currToken = JsonToken.NOT_AVAILABLE); } } 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; } } if (currQuadBytes > 0) { if (qlen >= quads.length) { _quadBuffer = quads = growArrayBy(quads, quads.length); } quads[qlen++] = _padLastQuad(currQuad, currQuadBytes); } else if (qlen == 0) { // rare case but possible return _fieldComplete(""); } String name = _symbols.findName(quads, qlen); if (name == null) { name = _addName(quads, qlen, currQuadBytes); } return _fieldComplete(name); } protected final JsonToken _finishFieldWithEscape() throws IOException { // First: try finishing what wasn't yet: int ch = _decodeSplitEscaped(_quoted32, _quotedDigits); if (ch < 0) { // ... if possible _minorState = MINOR_FIELD_NAME_ESCAPE; return JsonToken.NOT_AVAILABLE; } if (_quadLength >= _quadBuffer.length) { _quadBuffer = growArrayBy(_quadBuffer, 32); } int currQuad = _pending32; int currQuadBytes = _pendingBytes; if (ch > 127) { // Ok, we'll need room for first byte right away if (currQuadBytes >= 4) { _quadBuffer[_quadLength++] = 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)); // need room for middle byte? if (++currQuadBytes >= 4) { _quadBuffer[_quadLength++] = 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); } if (currQuadBytes < 4) { ++currQuadBytes; currQuad = (currQuad << 8) | ch; } else { _quadBuffer[_quadLength++] = currQuad; currQuad = ch; currQuadBytes = 1; } if (_minorStateAfterSplit == MINOR_FIELD_APOS_NAME) { return _finishAposName(_quadLength, currQuad, currQuadBytes); } return _parseEscapedName(_quadLength, currQuad, currQuadBytes); } private int _decodeSplitEscaped(int value, int bytesRead) throws IOException { if (_inputPtr >= _inputEnd) { _quoted32 = value; _quotedDigits = bytesRead; return -1; } int c = _inputBuffer[_inputPtr++]; if (bytesRead == -1) { // expecting first char after backslash 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 c; case 'u': // and finally hex-escaped break; default: { // !!! TODO: Decode UTF-8 characters properly... // char ch = (char) _decodeCharForError(c); char ch = (char) c; return _handleUnrecognizedCharacterEscape(ch); } } if (_inputPtr >= _inputEnd) { _quotedDigits = 0; _quoted32 = 0; return -1; } c = _inputBuffer[_inputPtr++]; bytesRead = 0; } c &= 0xFF; while (true) { int digit = CharTypes.charToHex(c); if (digit < 0) { _reportUnexpectedChar(c & 0xFF, "expected a hex-digit for character escape sequence"); } value = (value << 4) | digit; if (++bytesRead == 4) { return value; } if (_inputPtr >= _inputEnd) { _quotedDigits = bytesRead; _quoted32 = value; return -1; } c = _inputBuffer[_inputPtr++] & 0xFF; } } /* /********************************************************************** /* Second-level decoding, String decoding /********************************************************************** */ protected JsonToken _startString() throws IOException { int ptr = _inputPtr; int outPtr = 0; char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); final int[] codes = _icUTF8; final int max = Math.min(_inputEnd, (ptr + outBuf.length)); final byte[] inputBuffer = _inputBuffer; while (ptr < max) { int c = (int) inputBuffer[ptr] & 0xFF; if (codes[c] != 0) { if (c == INT_QUOTE) { _inputPtr = ptr+1; _textBuffer.setCurrentLength(outPtr); return _valueComplete(JsonToken.VALUE_STRING); } break; } ++ptr; outBuf[outPtr++] = (char) c; } _textBuffer.setCurrentLength(outPtr); _inputPtr = ptr; return _finishRegularString(); } private final JsonToken _finishRegularString() throws IOException { int c; // Here we do want to do full decoding, hence: final int[] codes = _icUTF8; final byte[] inputBuffer = _inputBuffer; char[] outBuf = _textBuffer.getBufferWithoutReset(); int outPtr = _textBuffer.getCurrentSegmentSize(); int ptr = _inputPtr; final int safeEnd = _inputEnd - 5; // longest escape is 6 chars main_loop: while (true) { // Then the tight ASCII non-funny-char loop: ascii_loop: while (true) { if (ptr >= _inputEnd) { _inputPtr = ptr; _minorState = MINOR_VALUE_STRING; _textBuffer.setCurrentLength(outPtr); return (_currToken = JsonToken.NOT_AVAILABLE); } if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } final int max = Math.min(_inputEnd, (ptr + (outBuf.length - outPtr))); while (ptr < max) { c = inputBuffer[ptr++] & 0xFF; if (codes[c] != 0) { break ascii_loop; } outBuf[outPtr++] = (char) c; } } // Ok: end marker, escape or multi-byte? if (c == INT_QUOTE) { _inputPtr = ptr; _textBuffer.setCurrentLength(outPtr); return _valueComplete(JsonToken.VALUE_STRING); } // If possibly split, use off-lined longer version if (ptr >= safeEnd) { _inputPtr = ptr; _textBuffer.setCurrentLength(outPtr); if (!_decodeSplitMultiByte(c, codes[c], ptr < _inputEnd)) { _minorStateAfterSplit = MINOR_VALUE_STRING; return (_currToken = JsonToken.NOT_AVAILABLE); } outBuf = _textBuffer.getBufferWithoutReset(); outPtr = _textBuffer.getCurrentSegmentSize(); ptr = _inputPtr; continue main_loop; } // otherwise use inlined switch (codes[c]) { case 1: // backslash _inputPtr = ptr; c = _decodeFastCharEscape(); // since we know it's not split ptr = _inputPtr; break; case 2: // 2-byte UTF c = _decodeUTF8_2(c, _inputBuffer[ptr++]); break; case 3: // 3-byte UTF c = _decodeUTF8_3(c, _inputBuffer[ptr++], _inputBuffer[ptr++]); break; case 4: // 4-byte UTF c = _decodeUTF8_4(c, _inputBuffer[ptr++], _inputBuffer[ptr++], _inputBuffer[ptr++]); // 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) { // Note: call can now actually return (to allow unquoted linefeeds) _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; } // Ok, let's add char to output: outBuf[outPtr++] = (char) c; } } protected JsonToken _startAposString() throws IOException { int ptr = _inputPtr; int outPtr = 0; char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); final int[] codes = _icUTF8; final int max = Math.min(_inputEnd, (ptr + outBuf.length)); final byte[] inputBuffer = _inputBuffer; while (ptr < max) { int c = (int) inputBuffer[ptr] & 0xFF; if (c == INT_APOS) { _inputPtr = ptr+1; _textBuffer.setCurrentLength(outPtr); return _valueComplete(JsonToken.VALUE_STRING); } if (codes[c] != 0) { break; } ++ptr; outBuf[outPtr++] = (char) c; } _textBuffer.setCurrentLength(outPtr); _inputPtr = ptr; return _finishAposString(); } private final JsonToken _finishAposString() throws IOException { int c; final int[] codes = _icUTF8; final byte[] inputBuffer = _inputBuffer; char[] outBuf = _textBuffer.getBufferWithoutReset(); int outPtr = _textBuffer.getCurrentSegmentSize(); int ptr = _inputPtr; final int safeEnd = _inputEnd - 5; // longest escape is 6 chars main_loop: while (true) { ascii_loop: while (true) { if (ptr >= _inputEnd) { _inputPtr = ptr; _minorState = MINOR_VALUE_APOS_STRING; _textBuffer.setCurrentLength(outPtr); return (_currToken = JsonToken.NOT_AVAILABLE); } if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; } final int max = Math.min(_inputEnd, (ptr + (outBuf.length - outPtr))); while (ptr < max) { c = inputBuffer[ptr++] & 0xFF; if ((codes[c] != 0) && (c != INT_QUOTE)) { break ascii_loop; } if (c == INT_APOS) { _inputPtr = ptr; _textBuffer.setCurrentLength(outPtr); return _valueComplete(JsonToken.VALUE_STRING); } outBuf[outPtr++] = (char) c; } } // Escape or multi-byte? // If possibly split, use off-lined longer version if (ptr >= safeEnd) { _inputPtr = ptr; _textBuffer.setCurrentLength(outPtr); if (!_decodeSplitMultiByte(c, codes[c], ptr < _inputEnd)) { _minorStateAfterSplit = MINOR_VALUE_APOS_STRING; return (_currToken = JsonToken.NOT_AVAILABLE); } outBuf = _textBuffer.getBufferWithoutReset(); outPtr = _textBuffer.getCurrentSegmentSize(); ptr = _inputPtr; continue main_loop; } // otherwise use inlined switch (codes[c]) { case 1: // backslash _inputPtr = ptr; c = _decodeFastCharEscape(); // since we know it's not split ptr = _inputPtr; break; case 2: // 2-byte UTF c = _decodeUTF8_2(c, _inputBuffer[ptr++]); break; case 3: // 3-byte UTF c = _decodeUTF8_3(c, _inputBuffer[ptr++], _inputBuffer[ptr++]); break; case 4: // 4-byte UTF c = _decodeUTF8_4(c, _inputBuffer[ptr++], _inputBuffer[ptr++], _inputBuffer[ptr++]); // 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) { // Note: call can now actually return (to allow unquoted linefeeds) _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; } // Ok, let's add char to output: outBuf[outPtr++] = (char) c; } } private final boolean _decodeSplitMultiByte(int c, int type, boolean gotNext) throws IOException { switch (type) { case 1: c = _decodeSplitEscaped(0, -1); if (c < 0) { _minorState = MINOR_VALUE_STRING_ESCAPE; return false; } _textBuffer.append((char) c); return true; case 2: // 2-byte UTF; easy, either got both, or just miss one if (gotNext) { // NOTE: always succeeds, no need to check c = _decodeUTF8_2(c, _inputBuffer[_inputPtr++]); _textBuffer.append((char) c); return true; } _minorState = MINOR_VALUE_STRING_UTF8_2; _pending32 = c; return false; case 3: // 3-byte UTF c &= 0x0F; if (gotNext) { return _decodeSplitUTF8_3(c, 1, _inputBuffer[_inputPtr++]); } _minorState = MINOR_VALUE_STRING_UTF8_3; _pending32 = c; _pendingBytes = 1; return false; case 4: // 4-byte UTF c &= 0x07; if (gotNext) { return _decodeSplitUTF8_4(c, 1, _inputBuffer[_inputPtr++]); } _pending32 = c; _pendingBytes = 1; _minorState = MINOR_VALUE_STRING_UTF8_4; return false; default: if (c < INT_SPACE) { // Note: call can now actually return (to allow unquoted linefeeds) _throwUnquotedSpace(c, "string value"); } else { // Is this good enough error message? _reportInvalidChar(c); } _textBuffer.append((char) c); return true; } } private final boolean _decodeSplitUTF8_3(int prev, int prevCount, int next) throws IOException { if (prevCount == 1) { if ((next & 0xC0) != 0x080) { _reportInvalidOther(next & 0xFF, _inputPtr); } prev = (prev << 6) | (next & 0x3F); if (_inputPtr >= _inputEnd) { _minorState = MINOR_VALUE_STRING_UTF8_3; _pending32 = prev; _pendingBytes = 2; return false; } next = _inputBuffer[_inputPtr++]; } if ((next & 0xC0) != 0x080) { _reportInvalidOther(next & 0xFF, _inputPtr); } _textBuffer.append((char) ((prev << 6) | (next & 0x3F))); return true; } // @return Character value minus 0x10000; this so that caller // can readily expand it to actual surrogates private final boolean _decodeSplitUTF8_4(int prev, int prevCount, int next) throws IOException { if (prevCount == 1) { if ((next & 0xC0) != 0x080) { _reportInvalidOther(next & 0xFF, _inputPtr); } prev = (prev << 6) | (next & 0x3F); if (_inputPtr >= _inputEnd) { _minorState = MINOR_VALUE_STRING_UTF8_4; _pending32 = prev; _pendingBytes = 2; return false; } prevCount = 2; next = _inputBuffer[_inputPtr++]; } if (prevCount == 2) { if ((next & 0xC0) != 0x080) { _reportInvalidOther(next & 0xFF, _inputPtr); } prev = (prev << 6) | (next & 0x3F); if (_inputPtr >= _inputEnd) { _minorState = MINOR_VALUE_STRING_UTF8_4; _pending32 = prev; _pendingBytes = 3; return false; } next = _inputBuffer[_inputPtr++]; } if ((next & 0xC0) != 0x080) { _reportInvalidOther(next & 0xFF, _inputPtr); } int c = ((prev << 6) | (next & 0x3F)) - 0x10000; // Let's add first part right away: _textBuffer.append((char) (0xD800 | (c >> 10))); c = 0xDC00 | (c & 0x3FF); // And let the other char output down below _textBuffer.append((char) c); return true; } /* /********************************************************************** /* Internal methods, UTF8 decoding /********************************************************************** */ private final int _decodeCharEscape() throws IOException { int left = _inputEnd - _inputPtr; if (left < 5) { // offline boundary-checking case: return _decodeSplitEscaped(0, -1); } return _decodeFastCharEscape(); } private final int _decodeFastCharEscape() throws IOException { int c = (int) _inputBuffer[_inputPtr++]; 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: { // !!! TODO: Decode UTF-8 characters properly... // char ch = (char) _decodeCharForError(c); char ch = (char) c; return _handleUnrecognizedCharacterEscape(ch); } } int ch = (int) _inputBuffer[_inputPtr++]; int digit = CharTypes.charToHex(ch); int result = digit; if (digit >= 0) { ch = (int) _inputBuffer[_inputPtr++]; digit = CharTypes.charToHex(ch); if (digit >= 0) { result = (result << 4) | digit; ch = (int) _inputBuffer[_inputPtr++]; digit = CharTypes.charToHex(ch); if (digit >= 0) { result = (result << 4) | digit; ch = (int) _inputBuffer[_inputPtr++]; digit = CharTypes.charToHex(ch); if (digit >= 0) { return (result << 4) | digit; } } } } _reportUnexpectedChar(ch & 0xFF, "expected a hex-digit for character escape sequence"); return -1; } /* /********************************************************************** /* Internal methods, UTF8 decoding /********************************************************************** */ private final int _decodeUTF8_2(int c, int d) throws IOException { if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF, _inputPtr); } return ((c & 0x1F) << 6) | (d & 0x3F); } private final int _decodeUTF8_3(int c, int d, int e) throws IOException { c &= 0x0F; if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF, _inputPtr); } c = (c << 6) | (d & 0x3F); if ((e & 0xC0) != 0x080) { _reportInvalidOther(e & 0xFF, _inputPtr); } return (c << 6) | (e & 0x3F); } // @return Character value minus 0x10000; this so that caller // can readily expand it to actual surrogates private final int _decodeUTF8_4(int c, int d, int e, int f) throws IOException { if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF, _inputPtr); } c = ((c & 0x07) << 6) | (d & 0x3F); if ((e & 0xC0) != 0x080) { _reportInvalidOther(e & 0xFF, _inputPtr); } c = (c << 6) | (e & 0x3F); if ((f & 0xC0) != 0x080) { _reportInvalidOther(f & 0xFF, _inputPtr); } return ((c << 6) | (f & 0x3F)) - 0x10000; } /* /********************************************************************** /* Internal methods, other /********************************************************************** */ } NonBlockingJsonParserBase.java000066400000000000000000000735421356164247300362310ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.base.ParserBase; import com.fasterxml.jackson.core.io.IOContext; import com.fasterxml.jackson.core.json.JsonReadContext; import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer; import com.fasterxml.jackson.core.util.ByteArrayBuilder; import static com.fasterxml.jackson.core.JsonTokenId.*; /** * Intermediate base class for non-blocking JSON parsers. */ public abstract class NonBlockingJsonParserBase extends ParserBase { /* /********************************************************************** /* Major state constants /********************************************************************** */ /** * State right after parser has been constructed, before seeing the first byte * to handle possible (but optional) BOM. */ protected final static int MAJOR_INITIAL = 0; /** * State right after parser a root value has been * finished, but next token has not yet been recognized. */ protected final static int MAJOR_ROOT = 1; protected final static int MAJOR_OBJECT_FIELD_FIRST = 2; protected final static int MAJOR_OBJECT_FIELD_NEXT = 3; protected final static int MAJOR_OBJECT_VALUE = 4; protected final static int MAJOR_ARRAY_ELEMENT_FIRST = 5; protected final static int MAJOR_ARRAY_ELEMENT_NEXT = 6; /** * State after non-blocking input source has indicated that no more input * is forthcoming AND we have exhausted all the input */ protected final static int MAJOR_CLOSED = 7; /* /********************************************************************** /* Minor state constants /********************************************************************** */ /** * State in which part of (UTF-8) BOM has been detected, but not yet completely. */ protected final static int MINOR_ROOT_BOM = 1; /** * State between root-level value, waiting for at least one white-space * character as separator */ protected final static int MINOR_ROOT_NEED_SEPARATOR = 2; /** * State between root-level value, having processed at least one white-space * character, and expecting either more, start of a value, or end of input * stream. */ protected final static int MINOR_ROOT_GOT_SEPARATOR = 3; // state before field name itself, waiting for quote (or unquoted name) protected final static int MINOR_FIELD_LEADING_WS = 4; // state before field name, expecting comma (or closing curly), then field name protected final static int MINOR_FIELD_LEADING_COMMA = 5; // State within regular (double-quoted) field name protected final static int MINOR_FIELD_NAME = 7; // State within regular (double-quoted) field name, within escape (having // encountered either just backslash, or backslash and 'u' and 0 - 3 hex digits, protected final static int MINOR_FIELD_NAME_ESCAPE = 8; protected final static int MINOR_FIELD_APOS_NAME = 9; protected final static int MINOR_FIELD_UNQUOTED_NAME = 10; protected final static int MINOR_VALUE_LEADING_WS = 12; protected final static int MINOR_VALUE_EXPECTING_COMMA = 13; protected final static int MINOR_VALUE_EXPECTING_COLON = 14; protected final static int MINOR_VALUE_WS_AFTER_COMMA = 15; protected final static int MINOR_VALUE_TOKEN_NULL = 16; protected final static int MINOR_VALUE_TOKEN_TRUE = 17; protected final static int MINOR_VALUE_TOKEN_FALSE = 18; protected final static int MINOR_VALUE_TOKEN_NON_STD = 19; protected final static int MINOR_NUMBER_MINUS = 23; protected final static int MINOR_NUMBER_ZERO = 24; // zero as first, possibly trimming multiple protected final static int MINOR_NUMBER_MINUSZERO = 25; // "-0" (and possibly more zeroes) receive protected final static int MINOR_NUMBER_INTEGER_DIGITS = 26; protected final static int MINOR_NUMBER_FRACTION_DIGITS = 30; protected final static int MINOR_NUMBER_EXPONENT_MARKER = 31; protected final static int MINOR_NUMBER_EXPONENT_DIGITS = 32; protected final static int MINOR_VALUE_STRING = 40; protected final static int MINOR_VALUE_STRING_ESCAPE = 41; protected final static int MINOR_VALUE_STRING_UTF8_2 = 42; protected final static int MINOR_VALUE_STRING_UTF8_3 = 43; protected final static int MINOR_VALUE_STRING_UTF8_4 = 44; protected final static int MINOR_VALUE_APOS_STRING = 45; /** * Special state at which point decoding of a non-quoted token has encountered * a problem; that is, either not matching fully (like "truf" instead of "true", * at "tru"), or not having trailing separator (or end of input), like "trueful". * Attempt is made, then, to decode likely full input token to report suitable * error. */ protected final static int MINOR_VALUE_TOKEN_ERROR = 50; protected final static int MINOR_COMMENT_LEADING_SLASH = 51; protected final static int MINOR_COMMENT_CLOSING_ASTERISK = 52; protected final static int MINOR_COMMENT_C = 53; protected final static int MINOR_COMMENT_CPP = 54; protected final static int MINOR_COMMENT_YAML = 55; /* /********************************************************************** /* Helper objects, symbols (field names) /********************************************************************** */ /** * Symbol table that contains field names encountered so far */ final protected ByteQuadsCanonicalizer _symbols; /** * Temporary buffer used for name parsing. */ protected int[] _quadBuffer = new int[8]; protected int _quadLength; protected int _quad1; protected int _pending32; protected int _pendingBytes; protected int _quoted32; protected int _quotedDigits; /* /********************************************************************** /* Additional parsing state /********************************************************************** */ /** * Current main decoding state within logical tree */ protected int _majorState; /** * Value of {@link #_majorState} after completing a scalar value */ protected int _majorStateAfterValue; /** * Additional indicator within state; contextually relevant for just that state */ protected int _minorState; /** * Secondary minor state indicator used during decoding of escapes and/or * multi-byte Unicode characters */ protected int _minorStateAfterSplit; /** * Flag that is sent when calling application indicates that there will * be no more input to parse. */ protected boolean _endOfInput = false; /* /********************************************************************** /* Additional parsing state: non-standard tokens /********************************************************************** */ protected final static int NON_STD_TOKEN_NAN = 0; protected final static int NON_STD_TOKEN_INFINITY = 1; protected final static int NON_STD_TOKEN_PLUS_INFINITY = 2; protected final static int NON_STD_TOKEN_MINUS_INFINITY = 3; protected final static String[] NON_STD_TOKENS = new String[] { "NaN", "Infinity", "+Infinity", "-Infinity", }; protected final static double[] NON_STD_TOKEN_VALUES = new double[] { Double.NaN, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, }; /** * When tokenizing non-standard ("odd") tokens, this is the type to consider; * also works as index to actual textual representation. */ protected int _nonStdTokenType; /* /********************************************************************** /* Location tracking, additional /********************************************************************** */ /** * Since we are fed content that may or may not start at zero offset, we need * to keep track of the first byte within that buffer, to be able to calculate * logical offset within input "stream" */ protected int _currBufferStart = 0; /** * Alternate row tracker, used to keep track of position by `\r` marker * (whereas _currInputRow tracks `\n`). Used to simplify * tracking of linefeeds, assuming that input typically uses various * linefeed combinations (`\r`, `\n` or `\r\n`) consistently, in which * case we can simply choose max of two row candidates. */ protected int _currInputRowAlt = 1; /* /********************************************************************** /* Life-cycle /********************************************************************** */ public NonBlockingJsonParserBase(IOContext ctxt, int parserFeatures, ByteQuadsCanonicalizer sym) { super(ctxt, parserFeatures); _symbols = sym; _currToken = null; _majorState = MAJOR_INITIAL; _majorStateAfterValue = MAJOR_ROOT; } @Override public ObjectCodec getCodec() { return null; } @Override public void setCodec(ObjectCodec c) { throw new UnsupportedOperationException("Can not use ObjectMapper with non-blocking parser"); } /** * @since 2.9 */ @Override public boolean canParseAsync() { return true; } /* /********************************************************** /* Test support /********************************************************** */ protected ByteQuadsCanonicalizer symbolTableForTests() { return _symbols; } /* /********************************************************** /* Abstract methods from JsonParser /********************************************************** */ @Override public abstract int releaseBuffered(OutputStream out) throws IOException; @Override protected void _releaseBuffers() throws IOException { super._releaseBuffers(); // Merge found symbols, if any: _symbols.release(); // any other temp buffers? } @Override public Object getInputSource() { // since input is "pushed", to traditional source... return null; } @Override protected void _closeInput() throws IOException { // 30-May-2017, tatu: Seems like this is the most certain way to prevent // further decoding... not the optimal place, but due to inheritance // hierarchy most convenient. _currBufferStart = 0; _inputEnd = 0; } /* /********************************************************************** /* Overridden methods /********************************************************************** */ @Override public boolean hasTextCharacters() { if (_currToken == JsonToken.VALUE_STRING) { // yes; is or can be made available efficiently as char[] return _textBuffer.hasTextAsCharacters(); } if (_currToken == JsonToken.FIELD_NAME) { // not necessarily; possible but: return _nameCopied; } // other types, no benefit from accessing as char[] return false; } @Override public JsonLocation getCurrentLocation() { int col = _inputPtr - _currInputRowStart + 1; // 1-based // Since we track CR and LF separately, max should gives us right answer int row = Math.max(_currInputRow, _currInputRowAlt); return new JsonLocation(_getSourceReference(), _currInputProcessed + (_inputPtr - _currBufferStart), -1L, // bytes, chars row, col); } @Override public JsonLocation getTokenLocation() { return new JsonLocation(_getSourceReference(), _tokenInputTotal, -1L, _tokenInputRow, _tokenInputCol); } /* /********************************************************************** /* Public API, access to token information, text /********************************************************************** */ /** * Method for accessing textual representation of the current event; * if no current event (before first call to {@link #nextToken}, or * after encountering end-of-input), returns null. * Method can be called for any event. */ @Override public String getText() throws IOException { if (_currToken == JsonToken.VALUE_STRING) { return _textBuffer.contentsAsString(); } return _getText2(_currToken); } protected final String _getText2(JsonToken t) { if (t == null) { return null; } switch (t.id()) { case ID_NOT_AVAILABLE: return null; 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 // since 2.8 public int getText(Writer writer) throws IOException { JsonToken t = _currToken; if (t == JsonToken.VALUE_STRING) { 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); } if (t == JsonToken.NOT_AVAILABLE) { _reportError("Current token not available: can not call this method"); } char[] ch = t.asCharArray(); writer.write(ch); return ch.length; } return 0; } // // // Let's override default impls for improved performance // @since 2.1 @Override public String getValueAsString() throws IOException { if (_currToken == JsonToken.VALUE_STRING) { return _textBuffer.contentsAsString(); } if (_currToken == JsonToken.FIELD_NAME) { return getCurrentName(); } return super.getValueAsString(null); } // @since 2.1 @Override public String getValueAsString(String defValue) throws IOException { if (_currToken == JsonToken.VALUE_STRING) { return _textBuffer.contentsAsString(); } if (_currToken == JsonToken.FIELD_NAME) { return getCurrentName(); } return super.getValueAsString(defValue); } @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: // 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 != null) { // null only before/after document switch (_currToken.id()) { case ID_FIELD_NAME: return _parsingContext.getCurrentName().length(); case ID_STRING: // fall through case ID_NUMBER_INT: case ID_NUMBER_FLOAT: return _textBuffer.size(); default: 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: // fall through case ID_NUMBER_INT: case ID_NUMBER_FLOAT: return _textBuffer.getTextOffset(); default: } } return 0; } /* /********************************************************************** /* Public API, access to token information, binary /********************************************************************** */ @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { if (_currToken != JsonToken.VALUE_STRING) { _reportError("Current token (%s) not VALUE_STRING or VALUE_EMBEDDED_OBJECT, can not access as binary", _currToken); } 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 { byte[] b = getBinaryValue(b64variant); out.write(b); return b.length; } @Override public Object getEmbeddedObject() throws IOException { if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT ) { return _binaryValue; } return null; } /* /********************************************************************** /* Handling of nested scope, state /********************************************************************** */ protected final JsonToken _startArrayScope() throws IOException { _parsingContext = _parsingContext.createChildArrayContext(-1, -1); _majorState = MAJOR_ARRAY_ELEMENT_FIRST; _majorStateAfterValue = MAJOR_ARRAY_ELEMENT_NEXT; return (_currToken = JsonToken.START_ARRAY); } protected final JsonToken _startObjectScope() throws IOException { _parsingContext = _parsingContext.createChildObjectContext(-1, -1); _majorState = MAJOR_OBJECT_FIELD_FIRST; _majorStateAfterValue = MAJOR_OBJECT_FIELD_NEXT; return (_currToken = JsonToken.START_OBJECT); } protected final JsonToken _closeArrayScope() throws IOException { if (!_parsingContext.inArray()) { _reportMismatchedEndMarker(']', '}'); } JsonReadContext ctxt = _parsingContext.getParent(); _parsingContext = ctxt; int st; if (ctxt.inObject()) { st = MAJOR_OBJECT_FIELD_NEXT; } else if (ctxt.inArray()) { st = MAJOR_ARRAY_ELEMENT_NEXT; } else { st = MAJOR_ROOT; } _majorState = st; _majorStateAfterValue = st; return (_currToken = JsonToken.END_ARRAY); } protected final JsonToken _closeObjectScope() throws IOException { if (!_parsingContext.inObject()) { _reportMismatchedEndMarker('}', ']'); } JsonReadContext ctxt = _parsingContext.getParent(); _parsingContext = ctxt; int st; if (ctxt.inObject()) { st = MAJOR_OBJECT_FIELD_NEXT; } else if (ctxt.inArray()) { st = MAJOR_ARRAY_ELEMENT_NEXT; } else { st = MAJOR_ROOT; } _majorState = st; _majorStateAfterValue = st; return (_currToken = JsonToken.END_OBJECT); } /* /********************************************************** /* Internal methods, symbol (name) handling /********************************************************** */ protected final String _findName(int q1, int lastQuadBytes) throws JsonParseException { q1 = _padLastQuad(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); } protected final String _findName(int q1, int q2, int lastQuadBytes) throws JsonParseException { q2 = _padLastQuad(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); } protected final String _findName(int q1, int q2, int q3, int lastQuadBytes) throws JsonParseException { q3 = _padLastQuad(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] = _padLastQuad(q3, lastQuadBytes); return _addName(quads, 3, lastQuadBytes); } /** * 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. */ protected 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); } /** * Helper method needed to fix [jackson-core#148], masking of 0x00 character */ protected final static int _padLastQuad(int q, int bytes) { return (bytes == 4) ? q : (q | (-1 << (bytes << 3))); } /* /********************************************************************** /* Internal methods, state changes /********************************************************************** */ /** * Helper method called at point when all input has been exhausted and * input feeder has indicated no more input will be forthcoming. */ protected final JsonToken _eofAsNextToken() throws IOException { _majorState = MAJOR_CLOSED; if (!_parsingContext.inRoot()) { _handleEOF(); } close(); return (_currToken = null); } protected final JsonToken _fieldComplete(String name) throws IOException { _majorState = MAJOR_OBJECT_VALUE; _parsingContext.setCurrentName(name); return (_currToken = JsonToken.FIELD_NAME); } protected final JsonToken _valueComplete(JsonToken t) throws IOException { _majorState = _majorStateAfterValue; _currToken = t; return t; } protected final JsonToken _valueCompleteInt(int value, String asText) throws IOException { _textBuffer.resetWithString(asText); _intLength = asText.length(); _numTypesValid = NR_INT; // to force parsing _numberInt = value; _majorState = _majorStateAfterValue; JsonToken t = JsonToken.VALUE_NUMBER_INT; _currToken = t; return t; } @SuppressWarnings("deprecation") protected final JsonToken _valueNonStdNumberComplete(int type) throws IOException { String tokenStr = NON_STD_TOKENS[type]; _textBuffer.resetWithString(tokenStr); if (!isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { _reportError("Non-standard token '%s': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow", tokenStr); } _intLength = 0; _numTypesValid = NR_DOUBLE; _numberDouble = NON_STD_TOKEN_VALUES[type]; _majorState = _majorStateAfterValue; return (_currToken = JsonToken.VALUE_NUMBER_FLOAT); } protected final String _nonStdToken(int type) { return NON_STD_TOKENS[type]; } /* /********************************************************************** /* Internal methods, error reporting, related /********************************************************************** */ protected final void _updateTokenLocation() { _tokenInputRow = Math.max(_currInputRow, _currInputRowAlt); final int ptr = _inputPtr; _tokenInputCol = ptr - _currInputRowStart; _tokenInputTotal = _currInputProcessed + (ptr - _currBufferStart); } 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)); } protected void _reportInvalidOther(int mask, int ptr) throws JsonParseException { _inputPtr = ptr; _reportInvalidOther(mask); } protected void _reportInvalidOther(int mask) throws JsonParseException { _reportError("Invalid UTF-8 middle byte 0x"+Integer.toHexString(mask)); } } package-info.java000066400000000000000000000001761356164247300335410ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/async/** * Non-blocking ("async") JSON parser implementation. * * @since 2.9 */ package com.fasterxml.jackson.core.json.async; jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/package-info.java000066400000000000000000000004361356164247300325020ustar00rootroot00000000000000/** * JSON-specific parser and generator implementation classes that * Jackson defines and uses. * Application code should not (need to) use contents of this package; * nor are these implementations likely to be of use for sub-classing. */ package com.fasterxml.jackson.core.json; jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/package-info.java000066400000000000000000000022171356164247300315300ustar00rootroot00000000000000/** * Main public API classes of the core streaming JSON * processor: most importantly {@link com.fasterxml.jackson.core.JsonFactory} * used for constructing * JSON parser ({@link com.fasterxml.jackson.core.JsonParser}) * and generator * ({@link com.fasterxml.jackson.core.JsonGenerator}) * instances. *

* Public API of the higher-level mapping interfaces ("Mapping API") * is found from the "jackson-databind" bundle, except for following * base interfaces that are defined here: *

*/ package com.fasterxml.jackson.core; jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sym/000077500000000000000000000000001356164247300271475ustar00rootroot00000000000000ByteQuadsCanonicalizer.java000066400000000000000000001307601356164247300343450ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sympackage com.fasterxml.jackson.core.sym; import java.util.Arrays; import java.util.concurrent.atomic.AtomicReference; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.util.InternCache; /** * Replacement for BytesToNameCanonicalizer which aims at more localized * memory access due to flattening of name quad data. * Performance improvement modest for simple JSON document data binding (maybe 3%), * but should help more for larger symbol tables, or for binary formats like Smile. *

* Hash area is divided into 4 sections: *

    *
  1. Primary area (1/2 of total size), direct match from hash (LSB)
  2. *
  3. Secondary area (1/4 of total size), match from {@code hash (LSB) >> 1}
  4. *
  5. Tertiary area (1/8 of total size), match from {@code hash (LSB) >> 2}
  6. *
  7. Spill-over area (remaining 1/8) with linear scan, insertion order
  8. *
  9. *
* and within every area, entries are 4 {@code int}s, where 1 - 3 ints contain 1 - 12 * UTF-8 encoded bytes of name (null-padded), and last int is offset in * {@code _names} that contains actual name Strings. * * @since 2.6 */ public final class ByteQuadsCanonicalizer { /** * Initial size of the primary hash area. Each entry consumes 4 ints (16 bytes), * and secondary area is same as primary; so default size will use 2kB of memory * (plus 64x4 or 64x8 (256/512 bytes) for references to Strings, and Strings * themselves). */ private static final int DEFAULT_T_SIZE = 64; // private static final int DEFAULT_T_SIZE = 256; /** * Let's not expand symbol tables past some maximum size; * with unique (~= random) names. * Size is in */ private static final int MAX_T_SIZE = 0x10000; // 64k entries == 2M mem hash area /** * No point in trying to construct tiny tables, just need to resize soon. */ private final static int MIN_HASH_SIZE = 16; /** * Let's only share reasonably sized symbol tables. Max size set to 3/4 of 8k; * this corresponds to 256k main hash index. This should allow for enough distinct * names for almost any case, while preventing ballooning for cases where names * are unique (or close thereof). */ final static int MAX_ENTRIES_FOR_REUSE = 6000; /* /********************************************************** /* Linkage, needed for merging symbol tables /********************************************************** */ /** * Reference to the root symbol table, for child tables, so * that they can merge table information back as necessary. */ final private ByteQuadsCanonicalizer _parent; /** * Member that is only used by the root table instance: root * passes immutable state info child instances, and children * may return new state if they add entries to the table. * Child tables do NOT use the reference. */ final private AtomicReference _tableInfo; /** * Seed value we use as the base to make hash codes non-static between * different runs, but still stable for lifetime of a single symbol table * instance. * This is done for security reasons, to avoid potential DoS attack via * hash collisions. */ final private int _seed; /* /********************************************************** /* Configuration /********************************************************** */ /** * Whether canonical symbol Strings are to be intern()ed before added * to the table or not. *

* NOTE: non-final to allow disabling intern()ing in case of excessive * collisions. */ private boolean _intern; /** * Flag that indicates whether we should throw an exception if enough * hash collisions are detected (true); or just worked around (false). * * @since 2.4 */ private final boolean _failOnDoS; /* /********************************************************** /* First, main hash area info /********************************************************** */ /** * Primary hash information area: consists of 2 * _hashSize * entries of 16 bytes (4 ints), arranged in a cascading lookup * structure (details of which may be tweaked depending on expected rates * of collisions). */ private int[] _hashArea; /** * Number of slots for primary entries within {@link #_hashArea}; which is * at most 1/8 of actual size of the underlying array (4-int slots, * primary covers only half of the area; plus, additional area for longer * symbols after hash area). */ private int _hashSize; /** * Offset within {@link #_hashArea} where secondary entries start */ private int _secondaryStart; /** * Offset within {@link #_hashArea} where tertiary entries start */ private int _tertiaryStart; /** * Constant that determines size of buckets for tertiary entries: * 1 << _tertiaryShift is the size, and shift value * is also used for translating from primary offset into * tertiary bucket (shift right by 4 + _tertiaryShift). *

* Default value is 2, for buckets of 4 slots; grows bigger with * bigger table sizes. */ private int _tertiaryShift; /** * Total number of Strings in the symbol table; only used for child tables. */ private int _count; /** * Array that contains String instances matching * entries in {@link #_hashArea}. * Contains nulls for unused entries. Note that this size is twice * that of {@link #_hashArea} */ private String[] _names; /* /********************************************************** /* Then information on collisions etc /********************************************************** */ /** * Pointer to the offset within spill-over area where there is room * for more spilled over entries (if any). * Spill over area is within fixed-size portion of {@link #_hashArea}. */ private int _spilloverEnd; /** * Offset within {@link #_hashArea} that follows main slots and contains * quads for longer names (13 bytes or longer), and points to the * first available int that may be used for appending quads of the next * long name. * Note that long name area follows immediately after the fixed-size * main hash area ({@link #_hashArea}). */ private int _longNameOffset; /* /********************************************************** /* Sharing, versioning /********************************************************** */ // // // Which of the buffers may be shared (and are copy-on-write)? /** * Flag that indicates whether underlying data structures for * the main hash area are shared or not. If they are, then they * need to be handled in copy-on-write way, i.e. if they need * to be modified, a copy needs to be made first; at this point * it will not be shared any more, and can be modified. *

* This flag needs to be checked both when adding new main entries, * and when adding new collision list queues (i.e. creating a new * collision list head entry) */ private boolean _hashShared; /* /********************************************************** /* Life-cycle: constructors /********************************************************** */ /** * Constructor used for creating per-JsonFactory "root" * symbol tables: ones used for merging and sharing common symbols * * @param sz Initial primary hash area size * @param intern Whether Strings contained should be {@link String#intern}ed * @param seed Random seed valued used to make it more difficult to cause * collisions (used for collision-based DoS attacks). */ private ByteQuadsCanonicalizer(int sz, boolean intern, int seed, boolean failOnDoS) { _parent = null; _seed = seed; _intern = intern; _failOnDoS = failOnDoS; // Sanity check: let's now allow hash sizes below certain minimum value if (sz < MIN_HASH_SIZE) { sz = MIN_HASH_SIZE; } else { // Also; size must be 2^N; otherwise hash algorithm won't // work... so let's just pad it up, if so if ((sz & (sz - 1)) != 0) { // only true if it's 2^N int curr = MIN_HASH_SIZE; while (curr < sz) { curr += curr; } sz = curr; } } _tableInfo = new AtomicReference(TableInfo.createInitial(sz)); } /** * Constructor used when creating a child instance */ private ByteQuadsCanonicalizer(ByteQuadsCanonicalizer parent, boolean intern, int seed, boolean failOnDoS, TableInfo state) { _parent = parent; _seed = seed; _intern = intern; _failOnDoS = failOnDoS; _tableInfo = null; // not used by child tables // Then copy shared state _count = state.count; _hashSize = state.size; _secondaryStart = _hashSize << 2; // right after primary area _tertiaryStart = _secondaryStart + (_secondaryStart >> 1); // right after secondary _tertiaryShift = state.tertiaryShift; _hashArea = state.mainHash; _names = state.names; _spilloverEnd = state.spilloverEnd; _longNameOffset = state.longNameOffset; // and then set other state to reflect sharing status _hashShared = true; } /* /********************************************************** /* Life-cycle: factory methods, merging /********************************************************** */ /** * Factory method to call to create a symbol table instance with a * randomized seed value. */ public static ByteQuadsCanonicalizer createRoot() { // Need to use a variable seed, to thwart hash-collision based attacks. // 14-Feb-2017, tatu: Does this actually help? long now = System.currentTimeMillis(); // ensure it's not 0; and might as well require to be odd so: int seed = (((int) now) + ((int) (now >>> 32))) | 1; return createRoot(seed); } // Factory method that should only be called from unit tests, where seed // value should remain the same. protected static ByteQuadsCanonicalizer createRoot(int seed) { return new ByteQuadsCanonicalizer(DEFAULT_T_SIZE, true, seed, true); } /** * Factory method used to create actual symbol table instance to * use for parsing. */ public ByteQuadsCanonicalizer makeChild(int flags) { return new ByteQuadsCanonicalizer(this, JsonFactory.Feature.INTERN_FIELD_NAMES.enabledIn(flags), _seed, JsonFactory.Feature.FAIL_ON_SYMBOL_HASH_OVERFLOW.enabledIn(flags), _tableInfo.get()); } /** * Method called by the using code to indicate it is done with this instance. * This lets instance merge accumulated changes into parent (if need be), * safely and efficiently, and without calling code having to know about parent * information. */ public void release() { // we will try to merge if child table has new entries // 28-Jul-2019, tatu: From [core#548]: do not share if immediate rehash needed if ((_parent != null) && maybeDirty()) { _parent.mergeChild(new TableInfo(this)); // Let's also mark this instance as dirty, so that just in // case release was too early, there's no corruption of possibly shared data. _hashShared = true; } } private void mergeChild(TableInfo childState) { final int childCount = childState.count; TableInfo currState = _tableInfo.get(); // Should usually grow; but occasionally could also shrink if (but only if) // collision list overflow ends up clearing some collision lists. if (childCount == currState.count) { return; } // One caveat: let's try to avoid problems with degenerate cases of documents with // generated "random" names: for these, symbol tables would bloat indefinitely. // One way to do this is to just purge tables if they grow // too large, and that's what we'll do here. if (childCount > MAX_ENTRIES_FOR_REUSE) { // At any rate, need to clean up the tables childState = TableInfo.createInitial(DEFAULT_T_SIZE); } _tableInfo.compareAndSet(currState, childState); } /* /********************************************************** /* API, accessors /********************************************************** */ public int size() { if (_tableInfo != null) { // root table return _tableInfo.get().count; } // nope, child table return _count; } /** * Returns number of primary slots table has currently */ public int bucketCount() { return _hashSize; } /** * Method called to check to quickly see if a child symbol table * may have gotten additional entries. Used for checking to see * if a child table should be merged into shared table. */ public boolean maybeDirty() { return !_hashShared; } public int hashSeed() { return _seed; } /** * Method mostly needed by unit tests; calculates number of * entries that are in the primary slot set. These are * "perfect" entries, accessible with a single lookup */ public int primaryCount() { int count = 0; for (int offset = 3, end = _secondaryStart; offset < end; offset += 4) { if (_hashArea[offset] != 0) { ++count; } } return count; } /** * Method mostly needed by unit tests; calculates number of entries * in secondary buckets */ public int secondaryCount() { int count = 0; int offset = _secondaryStart + 3; for (int end = _tertiaryStart; offset < end; offset += 4) { if (_hashArea[offset] != 0) { ++count; } } return count; } /** * Method mostly needed by unit tests; calculates number of entries * in tertiary buckets */ public int tertiaryCount() { int count = 0; int offset = _tertiaryStart + 3; // to 1.5x, starting point of tertiary for (int end = offset + _hashSize; offset < end; offset += 4) { if (_hashArea[offset] != 0) { ++count; } } return count; } /** * Method mostly needed by unit tests; calculates number of entries * in shared spillover area */ public int spilloverCount() { // difference between spillover end, start, divided by 4 (four ints per slot) return (_spilloverEnd - _spilloverStart()) >> 2; } public int totalCount() { int count = 0; for (int offset = 3, end = (_hashSize << 3); offset < end; offset += 4) { if (_hashArea[offset] != 0) { ++count; } } return count; } @Override public String toString() { int pri = primaryCount(); int sec = secondaryCount(); int tert = tertiaryCount(); int spill = spilloverCount(); int total = totalCount(); return String.format("[%s: size=%d, hashSize=%d, %d/%d/%d/%d pri/sec/ter/spill (=%s), total:%d]", getClass().getName(), _count, _hashSize, pri, sec, tert, spill, (pri+sec+tert+spill), total); } /* /********************************************************** /* Public API, accessing symbols /********************************************************** */ public String findName(int q1) { int offset = _calcOffset(calcHash(q1)); // first: primary match? final int[] hashArea = _hashArea; int len = hashArea[offset+3]; if (len == 1) { if (hashArea[offset] == q1) { return _names[offset >> 2]; } } else if (len == 0) { // empty slot; unlikely but avoid further lookups if so return null; } // secondary? single slot shared by N/2 primaries int offset2 = _secondaryStart + ((offset >> 3) << 2); len = hashArea[offset2+3]; if (len == 1) { if (hashArea[offset2] == q1) { return _names[offset2 >> 2]; } } else if (len == 0) { // empty slot; unlikely but avoid further lookups if so return null; } // tertiary lookup & spillovers best to offline return _findSecondary(offset, q1); } public String findName(int q1, int q2) { int offset = _calcOffset(calcHash(q1, q2)); final int[] hashArea = _hashArea; int len = hashArea[offset+3]; if (len == 2) { if ((q1 == hashArea[offset]) && (q2 == hashArea[offset+1])) { return _names[offset >> 2]; } } else if (len == 0) { // empty slot; unlikely but avoid further lookups if so return null; } // secondary? int offset2 = _secondaryStart + ((offset >> 3) << 2); len = hashArea[offset2+3]; if (len == 2) { if ((q1 == hashArea[offset2]) && (q2 == hashArea[offset2+1])) { return _names[offset2 >> 2]; } } else if (len == 0) { // empty slot? Short-circuit if no more spillovers return null; } return _findSecondary(offset, q1, q2); } public String findName(int q1, int q2, int q3) { int offset = _calcOffset(calcHash(q1, q2, q3)); final int[] hashArea = _hashArea; int len = hashArea[offset+3]; if (len == 3) { if ((q1 == hashArea[offset]) && (hashArea[offset+1] == q2) && (hashArea[offset+2] == q3)) { return _names[offset >> 2]; } } else if (len == 0) { // empty slot; unlikely but avoid further lookups if so return null; } // secondary? int offset2 = _secondaryStart + ((offset >> 3) << 2); len = hashArea[offset2+3]; if (len == 3) { if ((q1 == hashArea[offset2]) && (hashArea[offset2+1] == q2) && (hashArea[offset2+2] == q3)) { return _names[offset2 >> 2]; } } else if (len == 0) { // empty slot? Short-circuit if no more spillovers return null; } return _findSecondary(offset, q1, q2, q3); } public String findName(int[] q, int qlen) { /* This version differs significantly, because longer names do not fit within cell. * Rather, they contain hash in main slot, and offset+length to extension area * that contains actual quads. */ if (qlen < 4) { // another sanity check switch (qlen) { case 3: return findName(q[0], q[1], q[2]); case 2: return findName(q[0], q[1]); case 1: return findName(q[0]); default: // if 0 ever passed return ""; } } final int hash = calcHash(q, qlen); int offset = _calcOffset(hash); final int[] hashArea = _hashArea; final int len = hashArea[offset+3]; if ((hash == hashArea[offset]) && (len == qlen)) { // probable but not guaranteed: verify if (_verifyLongName(q, qlen, hashArea[offset+1])) { return _names[offset >> 2]; } } if (len == 0) { // empty slot; unlikely but avoid further lookups if so return null; } // secondary? int offset2 = _secondaryStart + ((offset >> 3) << 2); final int len2 = hashArea[offset2+3]; if ((hash == hashArea[offset2]) && (len2 == qlen)) { if (_verifyLongName(q, qlen, hashArea[offset2+1])) { return _names[offset2 >> 2]; } } return _findSecondary(offset, hash, q, qlen); } private final int _calcOffset(int hash) { // NOTE: simple for initial impl, but we may want to interleave it a bit // in near future // So: first, hash into primary hash index int ix = hash & (_hashSize-1); // keeping in mind we have 4 ints per entry return (ix << 2); } /* /********************************************************** /* Access from spill-over areas /********************************************************** */ private String _findSecondary(int origOffset, int q1) { // tertiary area division is dynamic. First; its size is N/4 compared to // primary hash size; and offsets are for 4 int slots. So to get to logical // index would shift by 4. But! Tertiary area is further split into buckets, // determined by shift value. And finally, from bucket back into physical offsets int offset = _tertiaryStart + ((origOffset >> (_tertiaryShift + 2)) << _tertiaryShift); final int[] hashArea = _hashArea; final int bucketSize = (1 << _tertiaryShift); for (int end = offset + bucketSize; offset < end; offset += 4) { int len = hashArea[offset+3]; if ((q1 == hashArea[offset]) && (1 == len)) { return _names[offset >> 2]; } if (len == 0) { return null; } } // but if tertiary full, check out spill-over area as last resort // shared spillover starts at 7/8 of the main hash area // (which is sized at 2 * _hashSize), so: for (offset = _spilloverStart(); offset < _spilloverEnd; offset += 4) { if ((q1 == hashArea[offset]) && (1 == hashArea[offset+3])) { return _names[offset >> 2]; } } return null; } private String _findSecondary(int origOffset, int q1, int q2) { int offset = _tertiaryStart + ((origOffset >> (_tertiaryShift + 2)) << _tertiaryShift); final int[] hashArea = _hashArea; final int bucketSize = (1 << _tertiaryShift); for (int end = offset + bucketSize; offset < end; offset += 4) { int len = hashArea[offset+3]; if ((q1 == hashArea[offset]) && (q2 == hashArea[offset+1]) && (2 == len)) { return _names[offset >> 2]; } if (len == 0) { return null; } } for (offset = _spilloverStart(); offset < _spilloverEnd; offset += 4) { if ((q1 == hashArea[offset]) && (q2 == hashArea[offset+1]) && (2 == hashArea[offset+3])) { return _names[offset >> 2]; } } return null; } private String _findSecondary(int origOffset, int q1, int q2, int q3) { int offset = _tertiaryStart + ((origOffset >> (_tertiaryShift + 2)) << _tertiaryShift); final int[] hashArea = _hashArea; final int bucketSize = (1 << _tertiaryShift); for (int end = offset + bucketSize; offset < end; offset += 4) { int len = hashArea[offset+3]; if ((q1 == hashArea[offset]) && (q2 == hashArea[offset+1]) && (q3 == hashArea[offset+2]) && (3 == len)) { return _names[offset >> 2]; } if (len == 0) { return null; } } for (offset = _spilloverStart(); offset < _spilloverEnd; offset += 4) { if ((q1 == hashArea[offset]) && (q2 == hashArea[offset+1]) && (q3 == hashArea[offset+2]) && (3 == hashArea[offset+3])) { return _names[offset >> 2]; } } return null; } private String _findSecondary(int origOffset, int hash, int[] q, int qlen) { int offset = _tertiaryStart + ((origOffset >> (_tertiaryShift + 2)) << _tertiaryShift); final int[] hashArea = _hashArea; final int bucketSize = (1 << _tertiaryShift); for (int end = offset + bucketSize; offset < end; offset += 4) { int len = hashArea[offset+3]; if ((hash == hashArea[offset]) && (qlen == len)) { if (_verifyLongName(q, qlen, hashArea[offset+1])) { return _names[offset >> 2]; } } if (len == 0) { return null; } } for (offset = _spilloverStart(); offset < _spilloverEnd; offset += 4) { if ((hash == hashArea[offset]) && (qlen == hashArea[offset+3])) { if (_verifyLongName(q, qlen, hashArea[offset+1])) { return _names[offset >> 2]; } } } return null; } private boolean _verifyLongName(int[] q, int qlen, int spillOffset) { final int[] hashArea = _hashArea; // spillOffset assumed to be physical index right into quad string int ix = 0; switch (qlen) { default: return _verifyLongName2(q, qlen, spillOffset); case 8: if (q[ix++] != hashArea[spillOffset++]) return false; case 7: if (q[ix++] != hashArea[spillOffset++]) return false; case 6: if (q[ix++] != hashArea[spillOffset++]) return false; case 5: if (q[ix++] != hashArea[spillOffset++]) return false; case 4: // always at least 4 if (q[ix++] != hashArea[spillOffset++]) return false; if (q[ix++] != hashArea[spillOffset++]) return false; if (q[ix++] != hashArea[spillOffset++]) return false; if (q[ix++] != hashArea[spillOffset++]) return false; } return true; } private boolean _verifyLongName2(int[] q, int qlen, int spillOffset) { int ix = 0; do { if (q[ix++] != _hashArea[spillOffset++]) { return false; } } while (ix < qlen); return true; } /* /********************************************************** /* API, mutators /********************************************************** */ public String addName(String name, int q1) { _verifySharing(); if (_intern) { name = InternCache.instance.intern(name); } int offset = _findOffsetForAdd(calcHash(q1)); _hashArea[offset] = q1; _hashArea[offset+3] = 1; _names[offset >> 2] = name; ++_count; return name; } public String addName(String name, int q1, int q2) { _verifySharing(); if (_intern) { name = InternCache.instance.intern(name); } int hash = (q2 == 0) ? calcHash(q1) : calcHash(q1, q2); int offset = _findOffsetForAdd(hash); _hashArea[offset] = q1; _hashArea[offset+1] = q2; _hashArea[offset+3] = 2; _names[offset >> 2] = name; ++_count; return name; } public String addName(String name, int q1, int q2, int q3) { _verifySharing(); if (_intern) { name = InternCache.instance.intern(name); } int offset = _findOffsetForAdd(calcHash(q1, q2, q3)); _hashArea[offset] = q1; _hashArea[offset+1] = q2; _hashArea[offset+2] = q3; _hashArea[offset+3] = 3; _names[offset >> 2] = name; ++_count; return name; } public String addName(String name, int[] q, int qlen) { _verifySharing(); if (_intern) { name = InternCache.instance.intern(name); } int offset; switch (qlen) { case 1: { offset = _findOffsetForAdd(calcHash(q[0])); _hashArea[offset] = q[0]; _hashArea[offset+3] = 1; } break; case 2: { offset = _findOffsetForAdd(calcHash(q[0], q[1])); _hashArea[offset] = q[0]; _hashArea[offset+1] = q[1]; _hashArea[offset+3] = 2; } break; case 3: { offset = _findOffsetForAdd(calcHash(q[0], q[1], q[2])); _hashArea[offset] = q[0]; _hashArea[offset+1] = q[1]; _hashArea[offset+2] = q[2]; _hashArea[offset+3] = 3; } break; default: final int hash = calcHash(q, qlen); offset = _findOffsetForAdd(hash); _hashArea[offset] = hash; int longStart = _appendLongName(q, qlen); _hashArea[offset+1] = longStart; _hashArea[offset+3] = qlen; } // plus add the actual String _names[offset >> 2] = name; // and finally; see if we really should rehash. ++_count; return name; } private void _verifySharing() { if (_hashShared) { _hashArea = Arrays.copyOf(_hashArea, _hashArea.length); _names = Arrays.copyOf(_names, _names.length); _hashShared = false; } } /** * Method called to find the location within hash table to add a new symbol in. */ private int _findOffsetForAdd(int hash) { // first, check the primary: if slot found, no need for resize int offset = _calcOffset(hash); final int[] hashArea = _hashArea; if (hashArea[offset+3] == 0) { //System.err.printf(" PRImary slot #%d, hash %X\n", (offset>>2), hash & 0x7F); return offset; } // Otherwise let's see if we are due resize(): if (_checkNeedForRehash()) { return _resizeAndFindOffsetForAdd(hash); } // If not, proceed with secondary slot int offset2 = _secondaryStart + ((offset >> 3) << 2); if (hashArea[offset2+3] == 0) { //System.err.printf(" SECondary slot #%d (start x%X), hash %X\n",(offset >> 3), _secondaryStart, (hash & 0x7F)); return offset2; } // if not, tertiary? offset2 = _tertiaryStart + ((offset >> (_tertiaryShift + 2)) << _tertiaryShift); final int bucketSize = (1 << _tertiaryShift); for (int end = offset2 + bucketSize; offset2 < end; offset2 += 4) { if (hashArea[offset2+3] == 0) { //System.err.printf(" TERtiary slot x%X (from x%X, start x%X), hash %X.\n", offset2, ((offset >> (_tertiaryShift + 2)) << _tertiaryShift), _tertiaryStart, (hash & 0x7F)); return offset2; } } // and if even tertiary full, append at the end of spill area offset = _spilloverEnd; _spilloverEnd += 4; //System.err.printf(" SPIll-over at x%X; start x%X; end x%X, hash %X\n", offset, _spilloverStart(), _hashArea.length, (hash & 0x7F)); // one caveat: in the unlikely event if spill-over filling up, // check if that could be considered a DoS attack; handle appropriately // (NOTE: approximate for now; we could verify details if that becomes necessary) /* 31-Jul-2015, tatu: Note that spillover area does NOT end at end of array, * since "long names" area follows. Instead, need to calculate from hash size. */ final int end = (_hashSize << 3); if (_spilloverEnd >= end) { if (_failOnDoS) { _reportTooManyCollisions(); } return _resizeAndFindOffsetForAdd(hash); } return offset; } // @since 2.10 private int _resizeAndFindOffsetForAdd(int hash) { // First things first: we need to resize+rehash (or, if too big, nuke contents) rehash(); // Copy of main _findOffsetForAdd except for checks to resize: can not be needed int offset = _calcOffset(hash); final int[] hashArea = _hashArea; if (hashArea[offset+3] == 0) { return offset; } int offset2 = _secondaryStart + ((offset >> 3) << 2); if (hashArea[offset2+3] == 0) { return offset2; } offset2 = _tertiaryStart + ((offset >> (_tertiaryShift + 2)) << _tertiaryShift); final int bucketSize = (1 << _tertiaryShift); for (int end = offset2 + bucketSize; offset2 < end; offset2 += 4) { if (hashArea[offset2+3] == 0) { return offset2; } } offset = _spilloverEnd; _spilloverEnd += 4; return offset; } // Helper method for checking if we should simply rehash() before add private boolean _checkNeedForRehash() { // Yes if above 80%, or above 50% AND have ~1% spill-overs if (_count > (_hashSize >> 1)) { // over 50% int spillCount = (_spilloverEnd - _spilloverStart()) >> 2; if ((spillCount > (1 + _count >> 7)) || (_count > (_hashSize * 0.80))) { return true; } } return false; } private int _appendLongName(int[] quads, int qlen) { int start = _longNameOffset; // note: at this point we must already be shared. But may not have enough space if ((start + qlen) > _hashArea.length) { // try to increment in reasonable chunks; at least space that we need int toAdd = (start + qlen) - _hashArea.length; // but at least 1/8 of regular hash area size or 16kB (whichever smaller) int minAdd = Math.min(4096, _hashSize); int newSize = _hashArea.length + Math.max(toAdd, minAdd); _hashArea = Arrays.copyOf(_hashArea, newSize); } System.arraycopy(quads, 0, _hashArea, start, qlen); _longNameOffset += qlen; return start; } /* /********************************************************** /* Hash calculation /********************************************************** */ /* Note on hash calculation: we try to make it more difficult to * generate collisions automatically; part of this is to avoid * simple "multiply-add" algorithm (like JDK String.hashCode()), * and add bit of shifting. And other part is to make this * non-linear, at least for shorter symbols. */ // JDK uses 31; other fine choices are 33 and 65599, let's use 33 // as it seems to give fewest collisions for us // (see [http://www.cse.yorku.ca/~oz/hash.html] for details) private final static int MULT = 33; private final static int MULT2 = 65599; private final static int MULT3 = 31; public int calcHash(int q1) { int hash = q1 ^ _seed; /* 29-Mar-2015, tatu: Earlier used 15 + 9 right shifts, which worked ok * except for one specific problem case: numbers. So needed to make sure * that all 4 least-significant bits participate in hash. Couple of ways * to work it out, but this is the simplest, fast and seems to do ok. */ hash += (hash >>> 16); // to xor hi- and low- 16-bits hash ^= (hash << 3); // shuffle back a bit hash += (hash >>> 12); // and bit more return hash; } public int calcHash(int q1, int q2) { // For two quads, let's change algorithm a bit, to spice // things up (can do bit more processing anyway) int hash = q1; hash += (hash >>> 15); // try mixing first and second byte pairs first hash ^= (hash >>> 9); // as well as lowest 2 bytes hash += (q2 * MULT); // then add second quad hash ^= _seed; hash += (hash >>> 16); // and shuffle some more hash ^= (hash >>> 4); hash += (hash << 3); return hash; } public int calcHash(int q1, int q2, int q3) { // use same algorithm as multi-byte, tested to work well int hash = q1 ^ _seed; hash += (hash >>> 9); hash *= MULT3; hash += q2; hash *= MULT; hash += (hash >>> 15); hash ^= q3; // 26-Mar-2015, tatu: As per two-quad case, a short shift seems to help more here hash += (hash >>> 4); hash += (hash >>> 15); hash ^= (hash << 9); return hash; } public int calcHash(int[] q, int qlen) { if (qlen < 4) { throw new IllegalArgumentException(); } /* And then change handling again for "multi-quad" case; mostly * to make calculation of collisions less fun. For example, * add seed bit later in the game, and switch plus/xor around, * use different shift lengths. */ int hash = q[0] ^ _seed; hash += (hash >>> 9); hash += q[1]; hash += (hash >>> 15); hash *= MULT; hash ^= q[2]; hash += (hash >>> 4); for (int i = 3; i < qlen; ++i) { int next = q[i]; next = next ^ (next >> 21); hash += next; } hash *= MULT2; // and finally shuffle some more once done hash += (hash >>> 19); hash ^= (hash << 5); return hash; } /* /********************************************************** /* Rehashing /********************************************************** */ private void rehash() { // Note: since we'll make copies, no need to unshare, can just mark as such: _hashShared = false; // And then we can first deal with the main hash area. Since we are expanding // linearly (double up), we know there'll be no collisions during this phase. final int[] oldHashArea = _hashArea; final String[] oldNames = _names; final int oldSize = _hashSize; final int oldCount = _count; final int newSize = oldSize + oldSize; final int oldEnd = _spilloverEnd; /* 13-Mar-2010, tatu: Let's guard against OOME that could be caused by * large documents with unique (or mostly so) names */ if (newSize > MAX_T_SIZE) { nukeSymbols(true); return; } // double up main hash area, but do not expand long-name area: _hashArea = new int[oldHashArea.length + (oldSize<<3)]; _hashSize = newSize; _secondaryStart = (newSize << 2); // 4 ints per entry _tertiaryStart = _secondaryStart + (_secondaryStart >> 1); // right after secondary _tertiaryShift = _calcTertiaryShift(newSize); // and simply double up name array _names = new String[oldNames.length << 1]; nukeSymbols(false); // Plus we can scan only through the primary hash area, looking for non-empty // slots, without worrying about ordering. This should never reduce priority // of existing entries: primaries remain primaries; however, due to increased // space, secondaries may become primaries etc int copyCount = 0; int[] q = new int[16]; for (int offset = 0, end = oldEnd; offset < end; offset += 4) { int len = oldHashArea[offset+3]; if (len == 0) { // empty slot, skip continue; } ++copyCount; String name = oldNames[offset>>2]; switch (len) { case 1: q[0] = oldHashArea[offset]; addName(name, q, 1); break; case 2: q[0] = oldHashArea[offset]; q[1] = oldHashArea[offset+1]; addName(name, q, 2); break; case 3: q[0] = oldHashArea[offset]; q[1] = oldHashArea[offset+1]; q[2] = oldHashArea[offset+2]; addName(name, q, 3); break; default: if (len > q.length) { q = new int[len]; } // #0 is hash, #1 offset int qoff = oldHashArea[offset+1]; System.arraycopy(oldHashArea, qoff, q, 0, len); addName(name, q, len); break; } } // Sanity checks: since corruption difficult to detect, assert explicitly // with production code if (copyCount != oldCount) { throw new IllegalStateException("Failed rehash(): old count="+oldCount+", copyCount="+copyCount); } } /** * Helper method called to empty all shared symbols, but to leave * arrays allocated */ private void nukeSymbols(boolean fill) { _count = 0; // reset spill-over to empty (starting at 7/8 of hash area) _spilloverEnd = _spilloverStart(); // and long name area to empty, starting immediately after hash area _longNameOffset = _hashSize << 3; if (fill) { Arrays.fill(_hashArea, 0); Arrays.fill(_names, null); } } /* /********************************************************** /* Helper methods /********************************************************** */ /** * Helper method that calculates start of the spillover area */ private final int _spilloverStart() { // we'll need slot at 1.75x of hashSize, but with 4-ints per slot. // So basically multiply by 7 int offset = _hashSize; return (offset << 3) - offset; } protected void _reportTooManyCollisions() { // First: do not fuzz about small symbol tables; may get balanced by doubling up if (_hashSize <= 1024) { // would have spill-over area of 128 entries return; } throw new IllegalStateException("Spill-over slots in symbol table with "+_count +" entries, hash area of "+_hashSize+" slots is now full (all " +(_hashSize >> 3)+" slots -- suspect a DoS attack based on hash collisions." +" You can disable the check via `JsonFactory.Feature.FAIL_ON_SYMBOL_HASH_OVERFLOW`"); } static int _calcTertiaryShift(int primarySlots) { // first: we only get 1/4 of slots of primary, to divide int tertSlots = (primarySlots) >> 2; // default is for buckets of 4 slots (each 4 ints, i.e. 1 << 4) if (tertSlots < 64) { return 4; } if (tertSlots <= 256) { // buckets of 8 slots (up to 256 == 32 x 8) return 5; } if (tertSlots <= 1024) { // buckets of 16 slots (up to 1024 == 64 x 16) return 6; } // and biggest buckets have 32 slots return 7; } /* /********************************************************** /* Helper classes /********************************************************** */ /** * Immutable value class used for sharing information as efficiently * as possible, by only require synchronization of reference manipulation * but not access to contents. * * @since 2.1 */ private final static class TableInfo { public final int size; public final int count; public final int tertiaryShift; public final int[] mainHash; public final String[] names; public final int spilloverEnd; public final int longNameOffset; public TableInfo(int size, int count, int tertiaryShift, int[] mainHash, String[] names, int spilloverEnd, int longNameOffset) { this.size = size; this.count = count; this.tertiaryShift = tertiaryShift; this.mainHash = mainHash; this.names = names; this.spilloverEnd = spilloverEnd; this.longNameOffset = longNameOffset; } public TableInfo(ByteQuadsCanonicalizer src) { size = src._hashSize; count = src._count; tertiaryShift = src._tertiaryShift; mainHash = src._hashArea; names = src._names; spilloverEnd = src._spilloverEnd; longNameOffset = src._longNameOffset; } public static TableInfo createInitial(int sz) { int hashAreaSize = sz << 3; int tertShift = _calcTertiaryShift(sz); return new TableInfo(sz, // hashSize 0, // count tertShift, new int[hashAreaSize], // mainHash, 2x slots, 4 ints per slot new String[sz << 1], // names == 2x slots hashAreaSize - sz, // at 7/8 of the total area hashAreaSize // longNameOffset, immediately after main hashes ); } } } CharsToNameCanonicalizer.java000066400000000000000000000734261356164247300346150ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sympackage com.fasterxml.jackson.core.sym; import java.util.Arrays; import java.util.BitSet; import java.util.concurrent.atomic.AtomicReference; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.util.InternCache; /** * This class is a kind of specialized type-safe Map, from char array to * String value. Specialization means that in addition to type-safety * and specific access patterns (key char array, Value optionally interned * String; values added on access if necessary), and that instances are * meant to be used concurrently, but by using well-defined mechanisms * to obtain such concurrently usable instances. Main use for the class * is to store symbol table information for things like compilers and * parsers; especially when number of symbols (keywords) is limited. *

* For optimal performance, usage pattern should be one where matches * should be very common (especially after "warm-up"), and as with most hash-based * maps/sets, that hash codes are uniformly distributed. Also, collisions * are slightly more expensive than with HashMap or HashSet, since hash codes * are not used in resolving collisions; that is, equals() comparison is * done with all symbols in same bucket index.
* Finally, rehashing is also more expensive, as hash codes are not * stored; rehashing requires all entries' hash codes to be recalculated. * Reason for not storing hash codes is reduced memory usage, hoping * for better memory locality. *

* Usual usage pattern is to create a single "master" instance, and either * use that instance in sequential fashion, or to create derived "child" * instances, which after use, are asked to return possible symbol additions * to master instance. In either case benefit is that symbol table gets * initialized so that further uses are more efficient, as eventually all * symbols needed will already be in symbol table. At that point no more * Symbol String allocations are needed, nor changes to symbol table itself. *

* Note that while individual SymbolTable instances are NOT thread-safe * (much like generic collection classes), concurrently used "child" * instances can be freely used without synchronization. However, using * master table concurrently with child instances can only be done if * access to master instance is read-only (i.e. no modifications done). */ public final class CharsToNameCanonicalizer { /* If we use "multiply-add" based hash algorithm, this is the multiplier * we use. *

* Note that JDK uses 31; but it seems that 33 produces fewer collisions, * at least with tests we have. */ public final static int HASH_MULT = 33; /** * Default initial table size. Shouldn't be miniscule (as there's * cost to both array realloc and rehashing), but let's keep * it reasonably small. For systems that properly * reuse factories it doesn't matter either way; but when * recreating factories often, initial overhead may dominate. */ 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. */ 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. */ static final int MAX_ENTRIES_FOR_REUSE = 12000; /** * Also: to thwart attacks based on hash collisions (which may or may not * be cheap to calculate), we will need to detect "too long" * collision chains. Let's start with static value of 100 entries * for the longest legal chain. *

* Note: longest chain we have been able to produce without malicious * intent has been 38 (with "com.fasterxml.jackson.core.main.TestWithTonsaSymbols"); * our setting should be reasonable here. * * @since 2.1 */ static final int MAX_COLL_CHAIN_LENGTH = 100; /* /********************************************************** /* Configuration /********************************************************** */ /** * Sharing of learnt symbols is done by optional linking of symbol * table instances with their parents. When parent linkage is * defined, and child instance is released (call to release), * parent's shared tables may be updated from the child instance. */ final private CharsToNameCanonicalizer _parent; /** * Member that is only used by the root table instance: root * passes immutable state info child instances, and children * may return new state if they add entries to the table. * Child tables do NOT use the reference. */ final private AtomicReference _tableInfo; /** * Seed value we use as the base to make hash codes non-static between * different runs, but still stable for lifetime of a single symbol table * instance. * This is done for security reasons, to avoid potential DoS attack via * hash collisions. * * @since 2.1 */ final private int _seed; final private int _flags; /** * Whether any canonicalization should be attempted (whether using * intern or not. *

* NOTE: non-final since we may need to disable this with overflow. */ private boolean _canonicalize; /* /********************************************************** /* Actual symbol table data /********************************************************** */ /** * Primary matching symbols; it's expected most match occur from * here. */ private String[] _symbols; /** * Overflow buckets; if primary doesn't match, lookup is done * from here. *

* Note: Number of buckets is half of number of symbol entries, on * assumption there's less need for buckets. */ private Bucket[] _buckets; /** * Current size (number of entries); needed to know if and when * rehash. */ 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. */ private int _sizeThreshold; /** * Mask used to get index from hash values; equal to * _buckets.length - 1, when _buckets.length is * a power of two. */ private int _indexMask; /** * We need to keep track of the longest collision list; this is needed * both to indicate problems with attacks and to allow flushing for * other cases. * * @since 2.1 */ private int _longestCollisionList; /* /********************************************************** /* State regarding shared arrays /********************************************************** */ /** * Flag that indicates whether underlying data structures for * the main hash area are shared or not. If they are, then they * need to be handled in copy-on-write way, i.e. if they need * to be modified, a copy needs to be made first; at this point * it will not be shared any more, and can be modified. *

* This flag needs to be checked both when adding new main entries, * and when adding new collision list queues (i.e. creating a new * collision list head entry) */ private boolean _hashShared; /* /********************************************************** /* Bit of DoS detection goodness /********************************************************** */ /** * Lazily constructed structure that is used to keep track of * collision buckets that have overflowed once: this is used * to detect likely attempts at denial-of-service attacks that * uses hash collisions. * * @since 2.4 */ private BitSet _overflows; /* /********************************************************** /* Life-cycle: constructors /********************************************************** */ /** * Main method for constructing a root symbol table instance. */ private CharsToNameCanonicalizer(int seed) { _parent = null; _seed = seed; // these settings don't really matter for the bootstrap instance _canonicalize = true; _flags = -1; // And we'll also set flags so no copying of buckets is needed: _hashShared = false; // doesn't really matter for root instance _longestCollisionList = 0; _tableInfo = new AtomicReference(TableInfo.createInitial(DEFAULT_T_SIZE)); // and actually do NOT assign buffers so we'll find if anyone tried to // use root instance } /** * Internal constructor used when creating child instances. */ private CharsToNameCanonicalizer(CharsToNameCanonicalizer parent, int flags, int seed, TableInfo parentState) { _parent = parent; _seed = seed; _tableInfo = null; // not used by child tables _flags = flags; _canonicalize = JsonFactory.Feature.CANONICALIZE_FIELD_NAMES.enabledIn(flags); // Then copy shared state _symbols = parentState.symbols; _buckets = parentState.buckets; _size = parentState.size; _longestCollisionList = parentState.longestCollisionList; // Hard-coded fill factor, 75% int arrayLen = (_symbols.length); _sizeThreshold = _thresholdSize(arrayLen); _indexMask = (arrayLen - 1); // Need to make copies of arrays, if/when adding new entries _hashShared = true; } private static int _thresholdSize(int hashAreaSize) { return hashAreaSize - (hashAreaSize >> 2); } /* /********************************************************** /* Life-cycle: factory methods, merging /********************************************************** */ /** * Method called to create root canonicalizer for a {@link com.fasterxml.jackson.core.JsonFactory} * instance. Root instance is never used directly; its main use is for * storing and sharing underlying symbol arrays as needed. */ public static CharsToNameCanonicalizer createRoot() { // Need to use a variable seed, to thwart hash-collision based attacks. // 14-Feb-2017, tatu: not sure it actually helps, at all, since it won't // change mixing or any of the steps. Should likely just remove in future. long now = System.currentTimeMillis(); // ensure it's not 0; and might as well require to be odd so: int seed = (((int) now) + ((int) (now >>> 32))) | 1; return createRoot(seed); } protected static CharsToNameCanonicalizer createRoot(int seed) { return new CharsToNameCanonicalizer(seed); } /** * "Factory" method; will create a new child instance of this symbol * table. It will be a copy-on-write instance, ie. it will only use * read-only copy of parent's data, but when changes are needed, a * copy will be created. *

* Note: while this method is synchronized, it is generally not * safe to both use makeChild/mergeChild, AND to use instance * actively. Instead, a separate 'root' instance should be used * on which only makeChild/mergeChild are called, but instance itself * is not used as a symbol table. */ public CharsToNameCanonicalizer makeChild(int flags) { return new CharsToNameCanonicalizer(this, flags, _seed, _tableInfo.get()); } /** * Method called by the using code to indicate it is done with this instance. * This lets instance merge accumulated changes into parent (if need be), * safely and efficiently, and without calling code having to know about parent * information. */ public void release() { // If nothing has been added, nothing to do if (!maybeDirty()) { return; } // we will try to merge if child table has new entries if (_parent != null && _canonicalize) { // canonicalize set to false if max size was reached _parent.mergeChild(new TableInfo(this)); // Let's also mark this instance as dirty, so that just in // case release was too early, there's no corruption of possibly shared data. _hashShared = true; } } /** * Method that allows contents of child table to potentially be * "merged in" with contents of this symbol table. *

* Note that caller has to make sure symbol table passed in is * really a child or sibling of this symbol table. */ private void mergeChild(TableInfo childState) { final int childCount = childState.size; TableInfo currState = _tableInfo.get(); // Should usually grow; but occasionally could also shrink if (but only if) // collision list overflow ends up clearing some collision lists. if (childCount == currState.size) { return; } // One caveat: let's try to avoid problems with degenerate cases of documents with // generated "random" names: for these, symbol tables would bloat indefinitely. // One way to do this is to just purge tables if they grow // too large, and that's what we'll do here. if (childCount > MAX_ENTRIES_FOR_REUSE) { // At any rate, need to clean up the tables childState = TableInfo.createInitial(DEFAULT_T_SIZE); } _tableInfo.compareAndSet(currState, childState); } /* /********************************************************** /* Public API, generic accessors: /********************************************************** */ public int size() { if (_tableInfo != null) { // root table return _tableInfo.get().size; } // nope, child table return _size; } /** * Method for checking number of primary hash buckets this symbol * table uses. * * @since 2.1 */ public int bucketCount() { return _symbols.length; } public boolean maybeDirty() { return !_hashShared; } public int hashSeed() { return _seed; } /** * Method mostly needed by unit tests; calculates number of * entries that are in collision list. Value can be at most * ({@link #size} - 1), but should usually be much lower, ideally 0. * * @since 2.1 */ public int collisionCount() { int count = 0; for (Bucket bucket : _buckets) { if (bucket != null) { count += bucket.length; } } return count; } /** * Method mostly needed by unit tests; calculates length of the * longest collision chain. This should typically be a low number, * but may be up to {@link #size} - 1 in the pathological case * * @since 2.1 */ public int maxCollisionLength() { return _longestCollisionList; } /* /********************************************************** /* Public API, accessing symbols: /********************************************************** */ public String findSymbol(char[] buffer, int start, int len, int h) { if (len < 1) { // empty Strings are simplest to handle up front return ""; } if (!_canonicalize) { // [JACKSON-259] return new String(buffer, start, len); } /* Related to problems with sub-standard hashing (somewhat * relevant for collision attacks too), let's try little * bit of shuffling to improve hash codes. * (note, however, that this can't help with full collisions) */ int index = _hashToIndex(h); String sym = _symbols[index]; // Optimal case; checking existing primary symbol for hash index: if (sym != null) { // Let's inline primary String equality checking: if (sym.length() == len) { int i = 0; while (sym.charAt(i) == buffer[start+i]) { // Optimal case; primary match found if (++i == len) { return sym; } } } Bucket b = _buckets[index>>1]; if (b != null) { sym = b.has(buffer, start, len); if (sym != null) { return sym; } sym = _findSymbol2(buffer, start, len, b.next); if (sym != null) { return sym; } } } return _addSymbol(buffer, start, len, h, index); } private String _findSymbol2(char[] buffer, int start, int len, Bucket b) { while (b != null) { String sym = b.has(buffer, start, len); if (sym != null) { return sym; } b = b.next; } return null; } private String _addSymbol(char[] buffer, int start, int len, int h, int index) { if (_hashShared) { //need to do copy-on-write? copyArrays(); _hashShared = false; } else if (_size >= _sizeThreshold) { // Need to expand? rehash(); // Need to recalc hash; rare occurrence (index mask has been // recalculated as part of rehash) index = _hashToIndex(calcHash(buffer, start, len)); } String newSymbol = new String(buffer, start, len); if (JsonFactory.Feature.INTERN_FIELD_NAMES.enabledIn(_flags)) { newSymbol = InternCache.instance.intern(newSymbol); } ++_size; // Ok; do we need to add primary entry, or a bucket? if (_symbols[index] == null) { _symbols[index] = newSymbol; } else { final int bix = (index >> 1); Bucket newB = new Bucket(newSymbol, _buckets[bix]); int collLen = newB.length; if (collLen > MAX_COLL_CHAIN_LENGTH) { // 23-May-2014, tatu: Instead of throwing an exception right away, // let's handle in bit smarter way. _handleSpillOverflow(bix, newB, index); } else { _buckets[bix] = newB; _longestCollisionList = Math.max(collLen, _longestCollisionList); } } return newSymbol; } /** * Method called when an overflow bucket has hit the maximum expected length: * this may be a case of DoS attack. Deal with it based on settings by either * clearing up bucket (to avoid indefinite expansion) or throwing exception. * Currently the first overflow for any single bucket DOES NOT throw an exception, * only second time (per symbol table instance) */ private void _handleSpillOverflow(int bucketIndex, Bucket newBucket, int mainIndex) { if (_overflows == null) { _overflows = new BitSet(); _overflows.set(bucketIndex); } else { if (_overflows.get(bucketIndex)) { // Has happened once already for this bucket index, so probably not coincidental... if (JsonFactory.Feature.FAIL_ON_SYMBOL_HASH_OVERFLOW.enabledIn(_flags)) { reportTooManyCollisions(MAX_COLL_CHAIN_LENGTH); } // but even if we don't fail, we will stop canonicalizing as safety measure // (so as not to cause problems with PermGen) _canonicalize = false; } else { _overflows.set(bucketIndex); } } // regardless, if we get this far, clear up the bucket, adjust size appropriately. _symbols[mainIndex] = newBucket.symbol; _buckets[bucketIndex] = null; // newBucket contains new symbol; but we will _size -= (newBucket.length); // we could calculate longest; but for now just mark as invalid _longestCollisionList = -1; } /** * Helper method that takes in a "raw" hash value, shuffles it as necessary, * and truncates to be used as the index. */ public int _hashToIndex(int rawHash) { // doing these seems to help a bit rawHash += (rawHash >>> 15); rawHash ^= (rawHash << 7); rawHash += (rawHash >>> 3); return (rawHash & _indexMask); } /** * Implementation of a hashing method for variable length * Strings. Most of the time intention is that this calculation * is done by caller during parsing, not here; however, sometimes * it needs to be done for parsed "String" too. * * @param len Length of String; has to be at least 1 (caller guarantees * this pre-condition) */ public int calcHash(char[] buffer, int start, int len) { int hash = _seed; for (int i = start, end = start+len; i < end; ++i) { hash = (hash * HASH_MULT) + (int) buffer[i]; } // NOTE: shuffling, if any, is done in 'findSymbol()', not here: return (hash == 0) ? 1 : hash; } public int calcHash(String key) { final int len = key.length(); int hash = _seed; for (int i = 0; i < len; ++i) { hash = (hash * HASH_MULT) + (int) key.charAt(i); } // NOTE: shuffling, if any, is done in 'findSymbol()', not here: return (hash == 0) ? 1 : hash; } /* /********************************************************** /* Internal methods /********************************************************** */ /** * Method called when copy-on-write is needed; generally when first * change is made to a derived symbol table. */ private void copyArrays() { final String[] oldSyms = _symbols; _symbols = Arrays.copyOf(oldSyms, oldSyms.length); final Bucket[] oldBuckets = _buckets; _buckets = Arrays.copyOf(oldBuckets, oldBuckets.length); } /** * Method called when size (number of entries) of symbol table grows * so big that load factor is exceeded. Since size has to remain * power of two, arrays will then always be doubled. Main work * is really redistributing old entries into new String/Bucket * entries. */ private void rehash() { final int size = _symbols.length; int newSize = size + size; /* 12-Mar-2010, tatu: Let's actually limit maximum size we are * prepared to use, to guard against OOME in case of unbounded * name sets (unique [non-repeating] names) */ if (newSize > MAX_T_SIZE) { // If this happens, there's no point in either growing or shrinking hash areas. // Rather, let's just cut our losses and stop canonicalizing. _size = 0; _canonicalize = false; // in theory, could just leave these as null, but... _symbols = new String[DEFAULT_T_SIZE]; _buckets = new Bucket[DEFAULT_T_SIZE>>1]; _indexMask = DEFAULT_T_SIZE-1; _hashShared = false; return; } final String[] oldSyms = _symbols; final Bucket[] oldBuckets = _buckets; _symbols = new String[newSize]; _buckets = new Bucket[newSize >> 1]; // Let's update index mask, threshold, now (needed for rehashing) _indexMask = newSize - 1; _sizeThreshold = _thresholdSize(newSize); int count = 0; // let's do sanity check // Need to do two loops, unfortunately, since spill-over area is // only half the size: int maxColl = 0; for (int i = 0; i < size; ++i) { String symbol = oldSyms[i]; if (symbol != null) { ++count; int index = _hashToIndex(calcHash(symbol)); if (_symbols[index] == null) { _symbols[index] = symbol; } else { int bix = (index >> 1); Bucket newB = new Bucket(symbol, _buckets[bix]); _buckets[bix] = newB; maxColl = Math.max(maxColl, newB.length); } } } final int bucketSize = (size >> 1); for (int i = 0; i < bucketSize; ++i) { Bucket b = oldBuckets[i]; while (b != null) { ++count; String symbol = b.symbol; int index = _hashToIndex(calcHash(symbol)); if (_symbols[index] == null) { _symbols[index] = symbol; } else { int bix = (index >> 1); Bucket newB = new Bucket(symbol, _buckets[bix]); _buckets[bix] = newB; maxColl = Math.max(maxColl, newB.length); } b = b.next; } } _longestCollisionList = maxColl; _overflows = null; if (count != _size) { throw new IllegalStateException(String.format( "Internal error on SymbolTable.rehash(): had %d entries; now have %d", _size, count)); } } /** * @since 2.1 */ protected void reportTooManyCollisions(int maxLen) { throw new IllegalStateException("Longest collision chain in symbol table (of size "+_size +") now exceeds maximum, "+maxLen+" -- suspect a DoS attack based on hash collisions"); } // since 2.10, for tests only /** * Diagnostics method that will verify that internal data structures are consistent; * not meant as user-facing method but only for test suites and possible troubleshooting. * * @since 2.10 */ protected void verifyInternalConsistency() { int count = 0; final int size = _symbols.length; for (int i = 0; i < size; ++i) { String symbol = _symbols[i]; if (symbol != null) { ++count; } } final int bucketSize = (size >> 1); for (int i = 0; i < bucketSize; ++i) { for (Bucket b = _buckets[i]; b != null; b = b.next) { ++count; } } if (count != _size) { throw new IllegalStateException(String.format("Internal error: expected internal size %d vs calculated count %d", _size, count)); } } // For debugging, comment out /* @Override public String toString() { StringBuilder sb = new StringBuilder(); int primaryCount = 0; for (String s : _symbols) { if (s != null) ++primaryCount; } sb.append("[BytesToNameCanonicalizer, size: "); sb.append(_size); sb.append('/'); sb.append(_symbols.length); sb.append(", "); sb.append(primaryCount); sb.append('/'); sb.append(_size - primaryCount); sb.append(" coll; avg length: "); // Average length: minimum of 1 for all (1 == primary hit); // and then 1 per each traversal for collisions/buckets //int maxDist = 1; int pathCount = _size; for (Bucket b : _buckets) { if (b != null) { int spillLen = b.length; for (int j = 1; j <= spillLen; ++j) { pathCount += j; } } } double avgLength; if (_size == 0) { avgLength = 0.0; } else { avgLength = (double) pathCount / (double) _size; } // let's round up a bit (two 2 decimal places) //avgLength -= (avgLength % 0.01); sb.append(avgLength); sb.append(']'); return sb.toString(); } */ /* /********************************************************** /* Helper classes /********************************************************** */ /** * This class is a symbol table entry. Each entry acts as a node * in a linked list. */ static final class Bucket { public final String symbol; public final Bucket next; public final int length; public Bucket(String s, Bucket n) { symbol = s; next = n; length = (n == null) ? 1 : n.length+1; } public String has(char[] buf, int start, int len) { if (symbol.length() != len) { return null; } int i = 0; do { if (symbol.charAt(i) != buf[start+i]) { return null; } } while (++i < len); return symbol; } } /** * Immutable value class used for sharing information as efficiently * as possible, by only require synchronization of reference manipulation * but not access to contents. * * @since 2.8.7 */ private final static class TableInfo { final int size; final int longestCollisionList; final String[] symbols; final Bucket[] buckets; public TableInfo(int size, int longestCollisionList, String[] symbols, Bucket[] buckets) { this.size = size; this.longestCollisionList = longestCollisionList; this.symbols = symbols; this.buckets = buckets; } public TableInfo(CharsToNameCanonicalizer src) { this.size = src._size; this.longestCollisionList = src._longestCollisionList; this.symbols = src._symbols; this.buckets = src._buckets; } public static TableInfo createInitial(int sz) { return new TableInfo(0, 0, // longestCollisionList new String[sz], new Bucket[sz >> 1]); } } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sym/Name.java000066400000000000000000000025511356164247300306750ustar00rootroot00000000000000package com.fasterxml.jackson.core.sym; /** * Base class for tokenized names (key strings in objects) that have * been tokenized from byte-based input sources (like * {@link java.io.InputStream}. * * @author Tatu Saloranta */ public abstract class Name { protected final String _name; protected final int _hashCode; protected Name(String name, int hashCode) { _name = name; _hashCode = hashCode; } public String getName() { return _name; } /* /********************************************************** /* Methods for package/core parser /********************************************************** */ public abstract boolean equals(int q1); public abstract boolean equals(int q1, int q2); /** * @since 2.6 */ public abstract boolean equals(int q1, int q2, int q3); public abstract boolean equals(int[] quads, int qlen); /* /********************************************************** /* Overridden standard methods /********************************************************** */ @Override public String toString() { return _name; } @Override public final int hashCode() { return _hashCode; } @Override public boolean equals(Object o) { // Canonical instances, can usually just do identity comparison return (o == this); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sym/Name1.java000066400000000000000000000020271356164247300307540ustar00rootroot00000000000000package com.fasterxml.jackson.core.sym; /** * Specialized implementation of PName: can be used for short Strings * that consists of at most 4 bytes. Usually this means short * ascii-only names. *

* The reason for such specialized classes is mostly space efficiency; * and to a lesser degree performance. Both are achieved for short * Strings by avoiding another level of indirection (via quad arrays) */ public final class Name1 extends Name { private final static Name1 EMPTY = new Name1("", 0, 0); private final int q; Name1(String name, int hash, int quad) { super(name, hash); q = quad; } public static Name1 getEmptyName() { return EMPTY; } @Override public boolean equals(int quad) { return (quad == q); } @Override public boolean equals(int quad1, int quad2) { return (quad1 == q) && (quad2 == 0); } @Override public boolean equals(int q1, int q2, int q3) { return false; } @Override public boolean equals(int[] quads, int qlen) { return (qlen == 1 && quads[0] == q); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sym/Name2.java000066400000000000000000000017701356164247300307610ustar00rootroot00000000000000package com.fasterxml.jackson.core.sym; /** * Specialized implementation of PName: can be used for short Strings * that consists of 5 to 8 bytes. Usually this means relatively short * ascii-only names. *

* The reason for such specialized classes is mostly space efficiency; * and to a lesser degree performance. Both are achieved for short * Strings by avoiding another level of indirection (via quad arrays) */ public final class Name2 extends Name { private final int q1, q2; Name2(String name, int hash, int quad1, int quad2) { super(name, hash); q1 = quad1; q2 = quad2; } @Override public boolean equals(int quad) { return false; } @Override public boolean equals(int quad1, int quad2) { return (quad1 == q1) && (quad2 == q2); } @Override public boolean equals(int quad1, int quad2, int q3) { return false; } @Override public boolean equals(int[] quads, int qlen) { return (qlen == 2 && quads[0] == q1 && quads[1] == q2); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sym/Name3.java000066400000000000000000000017771356164247300307710ustar00rootroot00000000000000package com.fasterxml.jackson.core.sym; /** * Specialized implementation of PName: can be used for short Strings * that consists of 9 to 12 bytes. It's the longest special purpose * implementaion; longer ones are expressed using {@link NameN}. */ public final class Name3 extends Name { private final int q1, q2, q3; Name3(String name, int hash, int i1, int i2, int i3) { super(name, hash); q1 = i1; q2 = i2; q3 = i3; } // Implies quad length == 1, never matches @Override public boolean equals(int quad) { return false; } // Implies quad length == 2, never matches @Override public boolean equals(int quad1, int quad2) { return false; } @Override public boolean equals(int quad1, int quad2, int quad3) { return (q1 == quad1) && (q2 == quad2) && (q3 == quad3); } @Override public boolean equals(int[] quads, int qlen) { return (qlen == 3) && (quads[0] == q1) && (quads[1] == q2) && (quads[2] == q3); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sym/NameN.java000066400000000000000000000051601356164247300310120ustar00rootroot00000000000000package com.fasterxml.jackson.core.sym; import java.util.Arrays; /** * Generic implementation of PName used for "long" names, where long * means that its byte (UTF-8) representation is 13 bytes or more. */ public final class NameN extends Name { private final int q1, q2, q3, q4; // first four quads private final int qlen; // total number of quads (4 + q.length) private final int[] q; NameN(String name, int hash, int q1, int q2, int q3, int q4, int[] quads, int quadLen) { super(name, hash); this.q1 = q1; this.q2 = q2; this.q3 = q3; this.q4 = q4; q = quads; qlen = quadLen; } public static NameN construct(String name, int hash, int[] q, int qlen) { /* We have specialized implementations for shorter * names, so let's not allow runt instances here */ if (qlen < 4) { throw new IllegalArgumentException(); } int q1 = q[0]; int q2 = q[1]; int q3 = q[2]; int q4 = q[3]; int rem = qlen - 4; int[] buf; if (rem > 0) { buf = Arrays.copyOfRange(q, 4, qlen); } else { buf = null; } return new NameN(name, hash, q1, q2, q3, q4, buf, qlen); } // Implies quad length == 1, never matches @Override public boolean equals(int quad) { return false; } // Implies quad length == 2, never matches @Override public boolean equals(int quad1, int quad2) { return false; } // Implies quad length == 3, never matches @Override public boolean equals(int quad1, int quad2, int quad3) { return false; } @Override public boolean equals(int[] quads, int len) { if (len != qlen) { return false; } // Will always have >= 4 quads, can unroll if (quads[0] != q1) return false; if (quads[1] != q2) return false; if (quads[2] != q3) return false; if (quads[3] != q4) return false; switch (len) { default: return _equals2(quads); case 8: if (quads[7] != q[3]) return false; case 7: if (quads[6] != q[2]) return false; case 6: if (quads[5] != q[1]) return false; case 5: if (quads[4] != q[0]) return false; case 4: } return true; } private final boolean _equals2(int[] quads) { final int end = qlen-4; for (int i = 0; i < end; ++i) { if (quads[i+4] != q[i]) { return false; } } return true; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sym/package-info.java000066400000000000000000000002341356164247300323350ustar00rootroot00000000000000/** * Internal implementation classes for efficient handling of * of symbols in JSON (field names in Objects) */ package com.fasterxml.jackson.core.sym; jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/type/000077500000000000000000000000001356164247300273205ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/type/ResolvedType.java000066400000000000000000000127331356164247300326160ustar00rootroot00000000000000package com.fasterxml.jackson.core.type; /** * Type abstraction that represents Java type that has been resolved * (i.e. has all generic information, if any, resolved to concrete * types). * Note that this is an intermediate type, and all concrete instances * MUST be of type JavaType from "databind" bundle -- this * abstraction is only needed so that types can be passed through * {@link com.fasterxml.jackson.core.JsonParser#readValueAs} methods. * * @since 2.0 */ public abstract class ResolvedType { /* /********************************************************** /* Public API, simple property accessors /********************************************************** */ /** * Accessor for type-erased {@link Class} of resolved type. */ public abstract Class getRawClass(); public abstract boolean hasRawClass(Class clz); public abstract boolean isAbstract(); public abstract boolean isConcrete(); public abstract boolean isThrowable(); public abstract boolean isArrayType(); public abstract boolean isEnumType(); public abstract boolean isInterface(); public abstract boolean isPrimitive(); public abstract boolean isFinal(); public abstract boolean isContainerType(); public abstract boolean isCollectionLikeType(); /** * Whether this type is a referential type, meaning that values are * basically pointers to "real" values (or null) and not regular * values themselves. Typical examples include things like * {@link java.util.concurrent.atomic.AtomicReference}, and various * Optional types (in JDK8, Guava). * * @since 2.6 */ public boolean isReferenceType() { return getReferencedType() != null; } public abstract boolean isMapLikeType(); /* /********************************************************** /* Public API, type parameter access /********************************************************** */ /** * Method that can be used to find out if the type directly declares generic * parameters (for its direct super-class and/or super-interfaces). */ public abstract boolean hasGenericTypes(); /** * Accessor that can be used to find out type for which parameterization * is applied: this is often NOT same as what {@link #getRawClass} returns, * but rather one of it supertype. *

* For example: for type like {@link java.util.HashMap}, raw type is * {@link java.util.HashMap}; but this method would return * {@link java.util.Map}, because relevant type parameters that are * resolved (and accessible using {@link #containedType(int)} and * {@link #getKeyType()}) are parameter for {@link java.util.Map} * (which may or may not be same as type parameters for subtype; * in case of {@link java.util.HashMap} they are, but for further * subtypes they may be different parameters or possibly none at all). * * @since 2.5 * * @deprecated Since 2.7: does not have meaning as parameters depend on type * resolved. */ @Deprecated // since 2.7 public Class getParameterSource() { return null; } /** * Method for accessing key type for this type, assuming type * has such a concept (only Map types do) */ public abstract ResolvedType getKeyType(); /** * Method for accessing content type of this type, if type has * such a thing: simple types do not, structured types do * (like arrays, Collections and Maps) */ public abstract ResolvedType getContentType(); /** * Method for accessing type of value that instances of this * type references, if any. * * @return Referenced type, if any; null if not. * * @since 2.6 */ public abstract ResolvedType getReferencedType(); /** * Method for checking how many contained types this type * has. Contained types are usually generic types, so that * generic Maps have 2 contained types. */ public abstract int containedTypeCount(); /** * Method for accessing definitions of contained ("child") * types. * * @param index Index of contained type to return * * @return Contained type at index, or null if no such type * exists (no exception thrown) */ public abstract ResolvedType containedType(int index); /** * Method for accessing name of type variable in indicated * position. If no name is bound, will use placeholders (derived * from 0-based index); if no type variable or argument exists * with given index, null is returned. * * @param index Index of contained type to return * * @return Contained type at index, or null if no such type * exists (no exception thrown) */ public abstract String containedTypeName(int index); /* /********************************************************** /* Public API, other /********************************************************** */ /** * Method that can be used to serialize type into form from which * it can be fully deserialized from at a later point (using * TypeFactory from mapper package). * For simple types this is same as calling * {@link Class#getName}, but for structured types it may additionally * contain type information about contents. */ public abstract String toCanonical(); } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/type/TypeReference.java000066400000000000000000000046111356164247300327250ustar00rootroot00000000000000package com.fasterxml.jackson.core.type; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; /** * This generic abstract class is used for obtaining full generics type information * by sub-classing; it must be converted to {@link ResolvedType} implementation * (implemented by JavaType from "databind" bundle) to be used. * Class is based on ideas from * http://gafter.blogspot.com/2006/12/super-type-tokens.html, * Additional idea (from a suggestion made in comments of the article) * is to require bogus implementation of Comparable * (any such generic interface would do, as long as it forces a method * with generic type to be implemented). * to ensure that a Type argument is indeed given. *

* Usage is by sub-classing: here is one way to instantiate reference * to generic type List<Integer>: *

 *  TypeReference ref = new TypeReference<List<Integer>>() { };
 *
* which can be passed to methods that accept TypeReference, or resolved * using TypeFactory to obtain {@link ResolvedType}. */ public abstract class TypeReference implements Comparable> { protected final Type _type; protected TypeReference() { Type superClass = getClass().getGenericSuperclass(); if (superClass instanceof Class) { // sanity check, should never happen throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information"); } /* 22-Dec-2008, tatu: Not sure if this case is safe -- I suspect * it is possible to make it fail? * But let's deal with specific * case when we know an actual use case, and thereby suitable * workarounds for valid case(s) and/or error to throw * on invalid one(s). */ _type = ((ParameterizedType) superClass).getActualTypeArguments()[0]; } public Type getType() { return _type; } /** * The only reason we define this method (and require implementation * of Comparable) is to prevent constructing a * reference without type information. */ @Override public int compareTo(TypeReference o) { return 0; } // just need an implementation, not a good one... hence ^^^ } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/type/WritableTypeId.java000066400000000000000000000164571356164247300330700ustar00rootroot00000000000000package com.fasterxml.jackson.core.type; import com.fasterxml.jackson.core.JsonToken; /** * This is a simple value class used between core streaming and higher level * databinding to pass information about type ids to write. * Properties are exposed and mutable on purpose: they are only used for communication * over serialization of a single value, and neither retained across calls nor shared * between threads. *

* Usual usage pattern is such that instance of this class is passed on two calls that are * needed for outputting type id (and possible additional wrapping, depending on format; * JSON, for example, requires wrapping as type id is part of regular data): first, a "prefix" * write (which usually includes actual id), performed before value write; and then * matching "suffix" write after value serialization. * * @since 2.9 */ public class WritableTypeId { /** * Enumeration of values that matches enum `As` from annotation * `JsonTypeInfo`: separate definition to avoid dependency between * streaming core and annotations packages; also allows more flexibility * in case new values needed at this level of internal API. *

* NOTE: in most cases this only matters with formats that do NOT have native * type id capabilities, and require type id to be included within regular * data (whether exposed as Java properties or not). Formats with native * types usually use native type id functionality regardless, unless * overridden by a feature to use "non-native" type inclusion. */ public enum Inclusion { /** * Inclusion as wrapper Array (1st element type id, 2nd element value). *

* Corresponds to JsonTypeInfo.As.WRAPPER_ARRAY. */ WRAPPER_ARRAY, /** * Inclusion as wrapper Object that has one key/value pair where type id * is the key for typed value. *

* Corresponds to JsonTypeInfo.As.WRAPPER_OBJECT. */ WRAPPER_OBJECT, /** * Inclusion as a property within Object to write, but logically as separate * metadata that is not exposed as payload to caller: that is, does not match * any of visible properties value object has. *

* NOTE: if shape of typed value to write is NOT Object, will instead use * {@link #WRAPPER_ARRAY} inclusion. *

* Corresponds to JsonTypeInfo.As.PROPERTY. */ METADATA_PROPERTY, /** * Inclusion as a "regular" property within Object to write; this implies that * its value should come from regular POJO property on serialization, and * be deserialized into such property. This handling, however, is up to databinding. *

* Regarding handling, type id is ONLY written as native type id; if no native * type ids available, caller is assumed to handle output some other way. * This is different from {@link #METADATA_PROPERTY}. *

* NOTE: if shape of typed value to write is NOT Object, will instead use * {@link #WRAPPER_ARRAY} inclusion. *

* Corresponds to JsonTypeInfo.As.EXISTING_PROPERTY. */ PAYLOAD_PROPERTY, /** * Inclusion as a property within "parent" Object of value Object to write. * This typically requires slightly convoluted processing in which property * that contains type id is actually written after typed value object * itself is written. *
* Note that it is illegal to call write method if the current (parent) write context * is not Object: no coercion is done for other inclusion types (unlike with * other xxx_PROPERTY choices. * This also means that root values MAY NOT use this type id inclusion mechanism * (as they have no parent context). *

* Corresponds to JsonTypeInfo.As.EXTERNAL_PROPERTY. */ PARENT_PROPERTY; public boolean requiresObjectContext() { return (this == METADATA_PROPERTY) || (this == PAYLOAD_PROPERTY); } } /** * Java object for which type id is being written. Not needed by default handling, * but may be useful for customized format handling. */ public Object forValue; /** * (optional) Super-type of {@link #forValue} to use for type id generation (if no * explicit id passed): used instead of actual class of {@link #forValue} in cases * where we do not want to use the "real" type but something more generic, usually * to work around specific problem with implementation type, or its deserializer. */ public Class forValueType; /** * Actual type id to use: usually {link java.lang.String}. */ public Object id; /** * If type id is to be embedded as a regular property, name of the property; * otherwise `null`. *

* NOTE: if "wrap-as-Object" is used, this does NOT contain property name to * use but `null`. */ public String asProperty; /** * Property used to indicate style of inclusion for this type id, in cases where * no native type id may be used (either because format has none, like JSON; or * because use of native type ids is disabled [with YAML]). */ public Inclusion include; /** * Information about intended shape of the value being written (that is, {@link #forValue}); * in case of structured values, start token of the structure; for scalars, value token. * Main difference is between structured values * ({@link JsonToken#START_ARRAY}, {@link JsonToken#START_OBJECT}) * and scalars ({@link JsonToken#VALUE_STRING}): specific scalar type may not be * important for processing. */ public JsonToken valueShape; /** * Flag that can be set to indicate that wrapper structure was written (during * prefix-writing); used to determine if suffix requires matching close markers. */ public boolean wrapperWritten; /** * Optional additional information that generator may add during "prefix write", * to be available on matching "suffix write". */ public Object extra; public WritableTypeId() { } /** * Constructor used when calling a method for generating and writing Type Id; * caller only knows value object and its intended shape. */ public WritableTypeId(Object value, JsonToken valueShape0) { this(value, valueShape0, null); } /** * Constructor used when calling a method for generating and writing Type Id, * but where actual type to use for generating id is NOT the type of value * (but its supertype). */ public WritableTypeId(Object value, Class valueType0, JsonToken valueShape0) { this(value, valueShape0, null); forValueType = valueType0; } /** * Constructor used when calling a method for writing Type Id; * caller knows value object, its intended shape as well as id to * use; but not details of wrapping (if any). */ public WritableTypeId(Object value, JsonToken valueShape0, Object id0) { forValue = value; id = id0; valueShape = valueShape0; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/type/package-info.java000066400000000000000000000011571356164247300325130ustar00rootroot00000000000000/** * Contains classes needed for type introspection, mostly used by data binding * functionality. Most of this functionality is needed to properly handled * generic types, and to simplify and unify processing of things Jackson needs * to determine how contained types (of {@link java.util.Collection} and * {@link java.util.Map} classes) are to be handled. *

* With 2.9, an additional type ({@link com.fasterxml.jackson.core.type.WritableTypeId}) * was added to help handling of type identifiers needed to support polymorphic * type serialization, deserialization. */ package com.fasterxml.jackson.core.type; jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/000077500000000000000000000000001356164247300273145ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/BufferRecycler.java000066400000000000000000000137301356164247300330650ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import java.util.concurrent.atomic.AtomicReferenceArray; /** * This is a small utility class, whose main functionality is to allow * simple reuse of raw byte/char buffers. It is usually used through * ThreadLocal member of the owning class pointing to * instance of this class through a SoftReference. The * end result is a low-overhead GC-cleanable recycling: hopefully * ideal for use by stream readers. *

* Rewritten in 2.10 to be thread-safe (see [jackson-core#479] for details), * to not rely on {@code ThreadLocal} access. */ public class BufferRecycler { /** * Buffer used for reading byte-based input. */ public final static int BYTE_READ_IO_BUFFER = 0; /** * Buffer used for temporarily storing encoded content; used * for example by UTF-8 encoding writer */ public final static int BYTE_WRITE_ENCODING_BUFFER = 1; /** * Buffer used for temporarily concatenating output; used for * example when requesting output as byte array. */ public final static int BYTE_WRITE_CONCAT_BUFFER = 2; /** * Buffer used for concatenating binary data that is either being * encoded as base64 output, or decoded from base64 input. * * @since 2.1 */ public final static int BYTE_BASE64_CODEC_BUFFER = 3; /** * Buffer used as input buffer for tokenization for character-based parsers. */ public final static int CHAR_TOKEN_BUFFER = 0; /** * Buffer used by generators; for byte-backed generators for buffering of * {@link String} values to output (before encoding into UTF-8), * and for char-backed generators as actual concatenation buffer. */ public final static int CHAR_CONCAT_BUFFER = 1; /** * Used through {@link TextBuffer}: directly by parsers (to concatenate * String values) * and indirectly via * {@link com.fasterxml.jackson.core.io.SegmentedStringWriter} * when serializing (databind level {@code ObjectMapper} and * {@code ObjectWriter}). In both cases used as segments (and not for whole value), * but may result in retention of larger chunks for big content * (long text values during parsing; bigger output documents for generation). */ public final static int CHAR_TEXT_BUFFER = 2; /** * For parsers, temporary buffer into which {@code char[]} for names is copied * when requested as such; for {@code WriterBasedGenerator} used for buffering * during {@code writeString(Reader)} operation (not commonly used). */ public final static int CHAR_NAME_COPY_BUFFER = 3; // Buffer lengths private final static int[] BYTE_BUFFER_LENGTHS = new int[] { 8000, 8000, 2000, 2000 }; private final static int[] CHAR_BUFFER_LENGTHS = new int[] { 4000, 4000, 200, 200 }; // Note: changed from simple array in 2.10: protected final AtomicReferenceArray _byteBuffers; // Note: changed from simple array in 2.10: protected final AtomicReferenceArray _charBuffers; /* /********************************************************** /* Construction /********************************************************** */ /** * Default constructor used for creating instances of this default * implementation. */ public BufferRecycler() { this(4, 4); } /** * Alternate constructor to be used by sub-classes, to allow customization * of number of low-level buffers in use. * * @since 2.4 */ protected BufferRecycler(int bbCount, int cbCount) { _byteBuffers = new AtomicReferenceArray(bbCount); _charBuffers = new AtomicReferenceArray(cbCount); } /* /********************************************************** /* Public API, byte buffers /********************************************************** */ /** * @param ix One of READ_IO_BUFFER constants. */ public final byte[] allocByteBuffer(int ix) { return allocByteBuffer(ix, 0); } public byte[] allocByteBuffer(int ix, int minSize) { final int DEF_SIZE = byteBufferLength(ix); if (minSize < DEF_SIZE) { minSize = DEF_SIZE; } byte[] buffer = _byteBuffers.getAndSet(ix, null); if (buffer == null || buffer.length < minSize) { buffer = balloc(minSize); } return buffer; } public void releaseByteBuffer(int ix, byte[] buffer) { _byteBuffers.set(ix, buffer); } /* /********************************************************** /* Public API, char buffers /********************************************************** */ public final char[] allocCharBuffer(int ix) { return allocCharBuffer(ix, 0); } public char[] allocCharBuffer(int ix, int minSize) { final int DEF_SIZE = charBufferLength(ix); if (minSize < DEF_SIZE) { minSize = DEF_SIZE; } char[] buffer = _charBuffers.getAndSet(ix, null); if (buffer == null || buffer.length < minSize) { buffer = calloc(minSize); } return buffer; } public void releaseCharBuffer(int ix, char[] buffer) { _charBuffers.set(ix, buffer); } /* /********************************************************** /* Overridable helper methods /********************************************************** */ protected int byteBufferLength(int ix) { return BYTE_BUFFER_LENGTHS[ix]; } protected int charBufferLength(int ix) { return CHAR_BUFFER_LENGTHS[ix]; } /* /********************************************************** /* Actual allocations separated for easier debugging/profiling /********************************************************** */ protected byte[] balloc(int size) { return new byte[size]; } protected char[] calloc(int size) { return new char[size]; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclers.java000066400000000000000000000064251356164247300332530ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import java.lang.ref.SoftReference; /** * Helper entity used to control access to simple buffer recyling scheme used for * some encoding, decoding tasks. * * @see BufferRecycler * * @since 2.9.2 */ public class BufferRecyclers { /** * System property that is checked to see if recycled buffers (see {@link BufferRecycler}) * should be tracked, for purpose of forcing release of all such buffers, typically * during major classloading. * * @since 2.9.6 */ public final static String SYSTEM_PROPERTY_TRACK_REUSABLE_BUFFERS = "com.fasterxml.jackson.core.util.BufferRecyclers.trackReusableBuffers"; /* /********************************************************** /* Life-cycle /********************************************************** */ /** * Flag that indicates whether {@link BufferRecycler} instances should be tracked. */ private final static ThreadLocalBufferManager _bufferRecyclerTracker; static { _bufferRecyclerTracker = "true".equals(System.getProperty(SYSTEM_PROPERTY_TRACK_REUSABLE_BUFFERS)) ? ThreadLocalBufferManager.instance() : null; } /* /********************************************************** /* BufferRecyclers for parsers, generators /********************************************************** */ /** * This ThreadLocal contains a {@link java.lang.ref.SoftReference} * to a {@link BufferRecycler} used to provide a low-cost * buffer recycling between reader and writer instances. */ final protected static ThreadLocal> _recyclerRef = new ThreadLocal>(); /** * Main accessor to call for accessing possibly recycled {@link BufferRecycler} instance. */ public static BufferRecycler getBufferRecycler() { SoftReference ref = _recyclerRef.get(); BufferRecycler br = (ref == null) ? null : ref.get(); if (br == null) { br = new BufferRecycler(); if (_bufferRecyclerTracker != null) { ref = _bufferRecyclerTracker.wrapAndTrack(br); } else { ref = new SoftReference(br); } _recyclerRef.set(ref); } return br; } /** * Specialized method that will release all recycled {@link BufferRecycler} if * (and only if) recycler tracking has been enabled * (see {@link #SYSTEM_PROPERTY_TRACK_REUSABLE_BUFFERS}). * This method is usually called on shutdown of the container like Application Server * to ensure that no references are reachable via {@link ThreadLocal}s as this may cause * unintentional retention of sizable amounts of memory. It may also be called regularly * if GC for some reason does not clear up {@link SoftReference}s aggressively enough. * * @return Number of buffers released, if tracking enabled (zero or more); -1 if tracking not enabled. * * @since 2.9.6 */ public static int releaseBuffers() { if (_bufferRecyclerTracker != null) { return _bufferRecyclerTracker.releaseBuffers(); } return -1; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/ByteArrayBuilder.java000066400000000000000000000224131356164247300333720ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core.util; import java.io.OutputStream; import java.util.*; /** * Helper class that is similar to {@link java.io.ByteArrayOutputStream} * in usage, but more geared to Jackson use cases internally. * Specific changes include segment storage (no need to have linear * backing buffer, can avoid reallocations, copying), as well API * not based on {@link java.io.OutputStream}. In short, a very much * specialized builder object. *

* Also implements {@link OutputStream} to allow * efficient aggregation of output content as a byte array, similar * to how {@link java.io.ByteArrayOutputStream} works, but somewhat more * efficiently for many use cases. *

* NOTE: maximum size limited to Java Array maximum, 2 gigabytes: this * because usage pattern is to collect content for a `byte[]` and so although * theoretically this builder can aggregate more content it will not be usable * as things are. Behavior may be improved if we solve the access problem. */ public final class ByteArrayBuilder extends OutputStream { public final static byte[] NO_BYTES = new byte[0]; // Size of the first block we will allocate. private final static int INITIAL_BLOCK_SIZE = 500; // Maximum block size we will use for individual non-aggregated blocks. // For 2.10, let's limit to using 128k chunks (was 256k up to 2.9) private final static int MAX_BLOCK_SIZE = (1 << 17); final static int DEFAULT_BLOCK_ARRAY_SIZE = 40; // Optional buffer recycler instance that we can use for allocating the first block. private final BufferRecycler _bufferRecycler; private final LinkedList _pastBlocks = new LinkedList(); // Number of bytes within byte arrays in {@link _pastBlocks}. 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); } public ByteArrayBuilder(BufferRecycler br, int firstBlockSize) { _bufferRecycler = br; _currBlock = (br == null) ? new byte[firstBlockSize] : br.allocByteBuffer(BufferRecycler.BYTE_WRITE_CONCAT_BUFFER); } private ByteArrayBuilder(BufferRecycler br, byte[] initialBlock, int initialLen) { _bufferRecycler = null; _currBlock = initialBlock; _currBlockPtr = initialLen; } public static ByteArrayBuilder fromInitial(byte[] initialBlock, int length) { return new ByteArrayBuilder(null, initialBlock, length); } public void reset() { _pastLen = 0; _currBlockPtr = 0; if (!_pastBlocks.isEmpty()) { _pastBlocks.clear(); } } /** * @since 2.9 */ public int size() { return _pastLen + _currBlockPtr; } /** * Clean up method to call to release all buffers this object may be * using. After calling the method, no other accessors can be used (and * attempt to do so may result in an exception) */ public void release() { reset(); if (_bufferRecycler != null && _currBlock != null) { _bufferRecycler.releaseByteBuffer(BufferRecycler.BYTE_WRITE_CONCAT_BUFFER, _currBlock); _currBlock = null; } } public void append(int i) { if (_currBlockPtr >= _currBlock.length) { _allocMore(); } _currBlock[_currBlockPtr++] = (byte) i; } public void appendTwoBytes(int b16) { if ((_currBlockPtr + 1) < _currBlock.length) { _currBlock[_currBlockPtr++] = (byte) (b16 >> 8); _currBlock[_currBlockPtr++] = (byte) b16; } else { append(b16 >> 8); append(b16); } } public void appendThreeBytes(int b24) { if ((_currBlockPtr + 2) < _currBlock.length) { _currBlock[_currBlockPtr++] = (byte) (b24 >> 16); _currBlock[_currBlockPtr++] = (byte) (b24 >> 8); _currBlock[_currBlockPtr++] = (byte) b24; } else { append(b24 >> 16); append(b24 >> 8); append(b24); } } /** * @since 2.9 */ public void appendFourBytes(int b32) { if ((_currBlockPtr + 3) < _currBlock.length) { _currBlock[_currBlockPtr++] = (byte) (b32 >> 24); _currBlock[_currBlockPtr++] = (byte) (b32 >> 16); _currBlock[_currBlockPtr++] = (byte) (b32 >> 8); _currBlock[_currBlockPtr++] = (byte) b32; } else { append(b32 >> 24); append(b32 >> 16); append(b32 >> 8); append(b32); } } /** * Method called when results are finalized and we can get the * full aggregated result buffer to return to the caller */ public byte[] toByteArray() { int totalLen = _pastLen + _currBlockPtr; if (totalLen == 0) { // quick check: nothing aggregated? return NO_BYTES; } byte[] result = new byte[totalLen]; int offset = 0; for (byte[] block : _pastBlocks) { int len = block.length; System.arraycopy(block, 0, result, offset, len); offset += len; } System.arraycopy(_currBlock, 0, result, offset, _currBlockPtr); offset += _currBlockPtr; if (offset != totalLen) { // just a sanity check throw new RuntimeException("Internal error: total len assumed to be "+totalLen+", copied "+offset+" bytes"); } // Let's only reset if there's sizable use, otherwise will get reset later on if (!_pastBlocks.isEmpty()) { reset(); } return result; } /* /********************************************************** /* Non-stream API (similar to TextBuffer) /********************************************************** */ /** * Method called when starting "manual" output: will clear out * current state and return the first segment buffer to fill */ public byte[] resetAndGetFirstSegment() { reset(); return _currBlock; } /** * Method called when the current segment buffer is full; will * append to current contents, allocate a new segment buffer * and return it */ public byte[] finishCurrentSegment() { _allocMore(); return _currBlock; } /** * Method that will complete "manual" output process, coalesce * content (if necessary) and return results as a contiguous buffer. * * @param lastBlockLength Amount of content in the current segment * buffer. * * @return Coalesced contents */ public byte[] completeAndCoalesce(int lastBlockLength) { _currBlockPtr = lastBlockLength; return toByteArray(); } public byte[] getCurrentSegment() { return _currBlock; } public void setCurrentSegmentLength(int len) { _currBlockPtr = len; } public int getCurrentSegmentLength() { return _currBlockPtr; } /* /********************************************************** /* OutputStream implementation /********************************************************** */ @Override public void write(byte[] b) { write(b, 0, b.length); } @Override public void write(byte[] b, int off, int len) { while (true) { int max = _currBlock.length - _currBlockPtr; int toCopy = Math.min(max, len); if (toCopy > 0) { System.arraycopy(b, off, _currBlock, _currBlockPtr, toCopy); off += toCopy; _currBlockPtr += toCopy; len -= toCopy; } if (len <= 0) break; _allocMore(); } } @Override public void write(int b) { append(b); } @Override public void close() { /* NOP */ } @Override public void flush() { /* NOP */ } /* /********************************************************** /* Internal methods /********************************************************** */ private void _allocMore() { final int newPastLen = _pastLen + _currBlock.length; // 13-Feb-2016, tatu: As per [core#351] let's try to catch problem earlier; // for now we are strongly limited by 2GB limit of Java arrays if (newPastLen < 0) { throw new IllegalStateException("Maximum Java array size (2GB) exceeded by `ByteArrayBuilder`"); } _pastLen = newPastLen; /* Let's allocate block that's half the total size, except * never smaller than twice the initial block size. * The idea is just to grow with reasonable rate, to optimize * between minimal number of chunks and minimal amount of * wasted space. */ int newSize = Math.max((_pastLen >> 1), (INITIAL_BLOCK_SIZE + INITIAL_BLOCK_SIZE)); // plus not to exceed max we define... if (newSize > MAX_BLOCK_SIZE) { newSize = MAX_BLOCK_SIZE; } _pastBlocks.add(_currBlock); _currBlock = new byte[newSize]; _currBlockPtr = 0; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/DefaultIndenter.java000066400000000000000000000055741356164247300332470ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import java.io.IOException; import com.fasterxml.jackson.core.JsonGenerator; /** * Default linefeed-based indenter, used by {@link DefaultPrettyPrinter} (unless * overridden). Uses system-specific linefeeds and 2 spaces for indentation per level. * * @since 2.5 */ public class DefaultIndenter extends DefaultPrettyPrinter.NopIndenter { private static final long serialVersionUID = 1L; public final static String SYS_LF; static { String lf; try { lf = System.getProperty("line.separator"); } catch (Throwable t) { lf = "\n"; // fallback when security manager denies access } SYS_LF = lf; } public static final DefaultIndenter SYSTEM_LINEFEED_INSTANCE = new DefaultIndenter(" ", SYS_LF); /** * We expect to rarely get indentation deeper than this number of levels, * and try not to pre-generate more indentations than needed. */ private final static int INDENT_LEVELS = 16; private final char[] indents; private final int charsPerLevel; private final String eol; /** * Indent with two spaces and the system's default line feed */ public DefaultIndenter() { this(" ", SYS_LF); } /** * Create an indenter which uses the indent string to indent one level * and the eol string to separate lines. */ public DefaultIndenter(String indent, String eol) { charsPerLevel = indent.length(); indents = new char[indent.length() * INDENT_LEVELS]; int offset = 0; for (int i=0; i 0) { // should we err on negative values (as there's some flaw?) level *= charsPerLevel; while (level > indents.length) { // unlike to happen but just in case jg.writeRaw(indents, 0, indents.length); level -= indents.length; } jg.writeRaw(indents, 0, level); } } public String getEol() { return eol; } public String getIndent() { return new String(indents, 0, charsPerLevel); } }DefaultPrettyPrinter.java000066400000000000000000000305051356164247300342430ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.SerializedString; /** * Default {@link PrettyPrinter} implementation that uses 2-space * indentation with platform-default linefeeds. * Usually this class is not instantiated directly, but instead * method {@link JsonGenerator#useDefaultPrettyPrinter} is * used, which will use an instance of this class for operation. */ @SuppressWarnings("serial") public class DefaultPrettyPrinter implements PrettyPrinter, Instantiatable, java.io.Serializable { private static final long serialVersionUID = 1; /** * Constant that specifies default "root-level" separator to use between * root values: a single space character. * * @since 2.1 */ public final static SerializedString DEFAULT_ROOT_VALUE_SEPARATOR = new SerializedString(" "); /** * Interface that defines objects that can produce indentation used * to separate object entries and array values. Indentation in this * context just means insertion of white space, independent of whether * linefeeds are output. */ public interface Indenter { void writeIndentation(JsonGenerator g, int level) throws IOException; /** * @return True if indenter is considered inline (does not add linefeeds), * false otherwise */ boolean isInline(); } // // // Config, indentation /** * By default, let's use only spaces to separate array values. */ protected Indenter _arrayIndenter = FixedSpaceIndenter.instance; /** * By default, let's use linefeed-adding indenter for separate * object entries. We'll further configure indenter to use * system-specific linefeeds, and 2 spaces per level (as opposed to, * say, single tabs) */ protected Indenter _objectIndenter = DefaultIndenter.SYSTEM_LINEFEED_INSTANCE; /** * String printed between root-level values, if any. */ protected final SerializableString _rootSeparator; // // // Config, other white space configuration /** * By default we will add spaces around colons used to * separate object fields and values. * If disabled, will not use spaces around colon. */ protected boolean _spacesInObjectEntries = true; // // // State: /** * Number of open levels of nesting. Used to determine amount of * indentation to use. */ protected transient int _nesting; /** * @since 2.9 */ protected Separators _separators; /** * @since 2.9 */ protected String _objectFieldValueSeparatorWithSpaces; /* /********************************************************** /* Life-cycle (construct, configure) /********************************************************** */ public DefaultPrettyPrinter() { this(DEFAULT_ROOT_VALUE_SEPARATOR); } /** * Constructor that specifies separator String to use between root values; * if null, no separator is printed. *

* Note: simply constructs a {@link SerializedString} out of parameter, * calls {@link #DefaultPrettyPrinter(SerializableString)} * * @param rootSeparator * * @since 2.1 */ public DefaultPrettyPrinter(String rootSeparator) { this((rootSeparator == null) ? null : new SerializedString(rootSeparator)); } /** * Constructor that specifies separator String to use between root values; * if null, no separator is printed. * * @param rootSeparator * * @since 2.1 */ public DefaultPrettyPrinter(SerializableString rootSeparator) { _rootSeparator = rootSeparator; withSeparators(DEFAULT_SEPARATORS); } public DefaultPrettyPrinter(DefaultPrettyPrinter base) { this(base, base._rootSeparator); } public DefaultPrettyPrinter(DefaultPrettyPrinter base, SerializableString rootSeparator) { _arrayIndenter = base._arrayIndenter; _objectIndenter = base._objectIndenter; _spacesInObjectEntries = base._spacesInObjectEntries; _nesting = base._nesting; _separators = base._separators; _objectFieldValueSeparatorWithSpaces = base._objectFieldValueSeparatorWithSpaces; _rootSeparator = rootSeparator; } public DefaultPrettyPrinter withRootSeparator(SerializableString rootSeparator) { if (_rootSeparator == rootSeparator || (rootSeparator != null && rootSeparator.equals(_rootSeparator))) { return this; } return new DefaultPrettyPrinter(this, rootSeparator); } /** * @since 2.6 */ public DefaultPrettyPrinter withRootSeparator(String rootSeparator) { return withRootSeparator((rootSeparator == null) ? null : new SerializedString(rootSeparator)); } public void indentArraysWith(Indenter i) { _arrayIndenter = (i == null) ? NopIndenter.instance : i; } public void indentObjectsWith(Indenter i) { _objectIndenter = (i == null) ? NopIndenter.instance : i; } /** * @since 2.3 */ public DefaultPrettyPrinter withArrayIndenter(Indenter i) { if (i == null) { i = NopIndenter.instance; } if (_arrayIndenter == i) { return this; } DefaultPrettyPrinter pp = new DefaultPrettyPrinter(this); pp._arrayIndenter = i; return pp; } /** * @since 2.3 */ public DefaultPrettyPrinter withObjectIndenter(Indenter i) { if (i == null) { i = NopIndenter.instance; } if (_objectIndenter == i) { return this; } DefaultPrettyPrinter pp = new DefaultPrettyPrinter(this); pp._objectIndenter = i; return pp; } /** * "Mutant factory" method that will return a pretty printer instance * that does use spaces inside object entries; if 'this' instance already * does this, it is returned; if not, a new instance will be constructed * and returned. * * @since 2.3 */ public DefaultPrettyPrinter withSpacesInObjectEntries() { return _withSpaces(true); } /** * "Mutant factory" method that will return a pretty printer instance * that does not use spaces inside object entries; if 'this' instance already * does this, it is returned; if not, a new instance will be constructed * and returned. * * @since 2.3 */ public DefaultPrettyPrinter withoutSpacesInObjectEntries() { return _withSpaces(false); } protected DefaultPrettyPrinter _withSpaces(boolean state) { if (_spacesInObjectEntries == state) { return this; } DefaultPrettyPrinter pp = new DefaultPrettyPrinter(this); pp._spacesInObjectEntries = state; return pp; } /** * @since 2.9 */ public DefaultPrettyPrinter withSeparators(Separators separators) { _separators = separators; _objectFieldValueSeparatorWithSpaces = " " + separators.getObjectFieldValueSeparator() + " "; return this; } /* /********************************************************** /* Instantiatable impl /********************************************************** */ @Override public DefaultPrettyPrinter createInstance() { if (getClass() != DefaultPrettyPrinter.class) { // since 2.10 throw new IllegalStateException("Failed `createInstance()`: "+getClass().getName() +" does not override method; it has to"); } return new DefaultPrettyPrinter(this); } /* /********************************************************** /* PrettyPrinter impl /********************************************************** */ @Override public void writeRootValueSeparator(JsonGenerator g) throws IOException { if (_rootSeparator != null) { g.writeRaw(_rootSeparator); } } @Override public void writeStartObject(JsonGenerator g) throws IOException { g.writeRaw('{'); if (!_objectIndenter.isInline()) { ++_nesting; } } @Override public void beforeObjectEntries(JsonGenerator g) throws IOException { _objectIndenter.writeIndentation(g, _nesting); } /** * Method called after an object field has been output, but * before the value is output. *

* Default handling (without pretty-printing) will output a single * colon to separate the two. Pretty-printer is * to output a colon as well, but can surround that with other * (white-space) decoration. */ @Override public void writeObjectFieldValueSeparator(JsonGenerator g) throws IOException { if (_spacesInObjectEntries) { g.writeRaw(_objectFieldValueSeparatorWithSpaces); } else { g.writeRaw(_separators.getObjectFieldValueSeparator()); } } /** * Method called after an object entry (field:value) has been completely * output, and before another value is to be output. *

* Default handling (without pretty-printing) will output a single * comma to separate the two. Pretty-printer is * to output a comma as well, but can surround that with other * (white-space) decoration. */ @Override public void writeObjectEntrySeparator(JsonGenerator g) throws IOException { g.writeRaw(_separators.getObjectEntrySeparator()); _objectIndenter.writeIndentation(g, _nesting); } @Override public void writeEndObject(JsonGenerator g, int nrOfEntries) throws IOException { if (!_objectIndenter.isInline()) { --_nesting; } if (nrOfEntries > 0) { _objectIndenter.writeIndentation(g, _nesting); } else { g.writeRaw(' '); } g.writeRaw('}'); } @Override public void writeStartArray(JsonGenerator g) throws IOException { if (!_arrayIndenter.isInline()) { ++_nesting; } g.writeRaw('['); } @Override public void beforeArrayValues(JsonGenerator g) throws IOException { _arrayIndenter.writeIndentation(g, _nesting); } /** * Method called after an array value has been completely * output, and before another value is to be output. *

* Default handling (without pretty-printing) will output a single * comma to separate the two. Pretty-printer is * to output a comma as well, but can surround that with other * (white-space) decoration. */ @Override public void writeArrayValueSeparator(JsonGenerator g) throws IOException { g.writeRaw(_separators.getArrayValueSeparator()); _arrayIndenter.writeIndentation(g, _nesting); } @Override public void writeEndArray(JsonGenerator g, int nrOfValues) throws IOException { if (!_arrayIndenter.isInline()) { --_nesting; } if (nrOfValues > 0) { _arrayIndenter.writeIndentation(g, _nesting); } else { g.writeRaw(' '); } g.writeRaw(']'); } /* /********************************************************** /* Helper classes /********************************************************** */ /** * Dummy implementation that adds no indentation whatsoever */ public static class NopIndenter implements Indenter, java.io.Serializable { public static final NopIndenter instance = new NopIndenter(); @Override public void writeIndentation(JsonGenerator g, int level) throws IOException { } @Override public boolean isInline() { return true; } } /** * This is a very simple indenter that only adds a * single space for indentation. It is used as the default * indenter for array values. */ public static class FixedSpaceIndenter extends NopIndenter { public static final FixedSpaceIndenter instance = new FixedSpaceIndenter(); @Override public void writeIndentation(JsonGenerator g, int level) throws IOException { g.writeRaw(' '); } @Override public boolean isInline() { return true; } } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/Instantiatable.java000066400000000000000000000013741356164247300331260ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; /** * Add-on interface used to indicate things that may be "blueprint" objects * which can not be used as is, but are used for creating usable per-process * (serialization, deserialization) instances, using * {@link #createInstance} method. *

* Note that some implementations may choose to implement {@link #createInstance} * by simply returning 'this': this is acceptable if instances are stateless. * * @see DefaultPrettyPrinter * * @since 2.1 */ public interface Instantiatable { /** * Method called to ensure that we have a non-blueprint object to use; * it is either this object (if stateless), or a newly created object * with separate state. */ T createInstance(); } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/InternCache.java000066400000000000000000000045171356164247300323510ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import java.util.LinkedHashMap; import java.util.concurrent.ConcurrentHashMap; /** * Singleton class that adds a simple first-level cache in front of * regular String.intern() functionality. This is done as a minor * performance optimization, to avoid calling native intern() method * in cases where same String is being interned multiple times. *

* Note: that this class extends {@link LinkedHashMap} is an implementation * detail -- no code should ever directly call Map methods. */ public final class InternCache extends ConcurrentHashMap // since 2.3 { private static final long serialVersionUID = 1L; /** * Size to use is somewhat arbitrary, so let's choose something that's * neither too small (low hit ratio) nor too large (waste of memory). *

* One consideration is possible attack via colliding {@link String#hashCode}; * because of this, limit to reasonably low setting. */ private final static int MAX_ENTRIES = 180; public final static InternCache instance = new InternCache(); /** * As minor optimization let's try to avoid "flush storms", * cases where multiple threads might try to concurrently * flush the map. */ private final Object lock = new Object(); private InternCache() { super(MAX_ENTRIES, 0.8f, 4); } public String intern(String input) { String result = get(input); if (result != null) { return result; } /* 18-Sep-2013, tatu: We used to use LinkedHashMap, which has simple LRU * method. No such functionality exists with CHM; and let's use simplest * possible limitation: just clear all contents. This because otherwise * we are simply likely to keep on clearing same, commonly used entries. */ if (size() >= MAX_ENTRIES) { /* Not incorrect wrt well-known double-locking anti-pattern because underlying * storage gives close enough answer to real one here; and we are * more concerned with flooding than starvation. */ synchronized (lock) { if (size() >= MAX_ENTRIES) { clear(); } } } result = input.intern(); put(result, result); return result; } } JsonGeneratorDelegate.java000066400000000000000000000361041356164247300343170ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.CharacterEscapes; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; import java.math.BigInteger; public class JsonGeneratorDelegate extends JsonGenerator { /** * Delegate object that method calls are delegated to. */ protected JsonGenerator delegate; /** * Whether copy methods * ({@link #copyCurrentEvent}, {@link #copyCurrentStructure}, {@link #writeTree} and {@link #writeObject}) * are to be called (true), or handled by this object (false). */ protected boolean delegateCopyMethods; /* /********************************************************** /* Construction, initialization /********************************************************** */ public JsonGeneratorDelegate(JsonGenerator d) { this(d, true); } /** * @param delegateCopyMethods Flag assigned to delagateCopyMethod * and which defines whether copy methods are handled locally (false), or * delegated to configured */ public JsonGeneratorDelegate(JsonGenerator d, boolean delegateCopyMethods) { delegate = d; this.delegateCopyMethods = delegateCopyMethods; } @Override public Object getCurrentValue() { return delegate.getCurrentValue(); } @Override public void setCurrentValue(Object v) { delegate.setCurrentValue(v); } /* /********************************************************** /* Extended API /********************************************************** */ public JsonGenerator getDelegate() { return delegate; } /* /********************************************************** /* Public API, metadata /********************************************************** */ @Override public ObjectCodec getCodec() { return delegate.getCodec(); } @Override public JsonGenerator setCodec(ObjectCodec oc) { delegate.setCodec(oc); return this; } @Override public void setSchema(FormatSchema schema) { delegate.setSchema(schema); } @Override public FormatSchema getSchema() { return delegate.getSchema(); } @Override public Version version() { return delegate.version(); } @Override public Object getOutputTarget() { return delegate.getOutputTarget(); } @Override public int getOutputBuffered() { return delegate.getOutputBuffered(); } /* /********************************************************** /* Public API, capability introspection (since 2.3, mostly) /********************************************************** */ @Override public boolean canUseSchema(FormatSchema schema) { return delegate.canUseSchema(schema); } @Override public boolean canWriteTypeId() { return delegate.canWriteTypeId(); } @Override public boolean canWriteObjectId() { return delegate.canWriteObjectId(); } @Override public boolean canWriteBinaryNatively() { return delegate.canWriteBinaryNatively(); } @Override public boolean canOmitFields() { return delegate.canOmitFields(); } /* /********************************************************** /* Public API, configuration /********************************************************** */ @Override public JsonGenerator enable(Feature f) { delegate.enable(f); return this; } @Override public JsonGenerator disable(Feature f) { delegate.disable(f); return this; } @Override public boolean isEnabled(Feature f) { return delegate.isEnabled(f); } // final, can't override (and no need to) //public final JsonGenerator configure(Feature f, boolean state) @Override public int getFeatureMask() { return delegate.getFeatureMask(); } @Override @Deprecated public JsonGenerator setFeatureMask(int mask) { delegate.setFeatureMask(mask); return this; } @Override public JsonGenerator overrideStdFeatures(int values, int mask) { delegate.overrideStdFeatures(values, mask); return this; } @Override public JsonGenerator overrideFormatFeatures(int values, int mask) { delegate.overrideFormatFeatures(values, mask); return this; } /* /********************************************************** /* Configuring generator /********************************************************** */ @Override public JsonGenerator setPrettyPrinter(PrettyPrinter pp) { delegate.setPrettyPrinter(pp); return this; } @Override public PrettyPrinter getPrettyPrinter() { return delegate.getPrettyPrinter(); } @Override public JsonGenerator useDefaultPrettyPrinter() { delegate.useDefaultPrettyPrinter(); return this; } @Override public JsonGenerator setHighestNonEscapedChar(int charCode) { delegate.setHighestNonEscapedChar(charCode); return this; } @Override public int getHighestEscapedChar() { return delegate.getHighestEscapedChar(); } @Override public CharacterEscapes getCharacterEscapes() { return delegate.getCharacterEscapes(); } @Override public JsonGenerator setCharacterEscapes(CharacterEscapes esc) { delegate.setCharacterEscapes(esc); return this; } @Override public JsonGenerator setRootValueSeparator(SerializableString sep) { delegate.setRootValueSeparator(sep); return this; } /* /********************************************************** /* Public API, write methods, structural /********************************************************** */ @Override public void writeStartArray() throws IOException { delegate.writeStartArray(); } @Override public void writeStartArray(int size) throws IOException { delegate.writeStartArray(size); } @Override public void writeStartArray(Object forValue) throws IOException { delegate.writeStartArray(forValue); } @Override public void writeStartArray(Object forValue, int size) throws IOException { delegate.writeStartArray(forValue, size); } @Override public void writeEndArray() throws IOException { delegate.writeEndArray(); } @Override public void writeStartObject() throws IOException { delegate.writeStartObject(); } @Override public void writeStartObject(Object forValue) throws IOException { delegate.writeStartObject(forValue); } @Override public void writeStartObject(Object forValue, int size) throws IOException { delegate.writeStartObject(forValue, size); } @Override public void writeEndObject() throws IOException { delegate.writeEndObject(); } @Override public void writeFieldName(String name) throws IOException { delegate.writeFieldName(name); } @Override 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 /********************************************************** */ @Override public void writeString(String text) throws IOException { delegate.writeString(text); } @Override public void writeString(Reader reader, int len) throws IOException { delegate.writeString(reader, len); } @Override public void writeString(char[] text, int offset, int len) throws IOException { delegate.writeString(text, offset, len); } @Override public void writeString(SerializableString text) throws IOException { delegate.writeString(text); } @Override public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException { delegate.writeRawUTF8String(text, offset, length); } @Override public void writeUTF8String(byte[] text, int offset, int length) throws IOException { delegate.writeUTF8String(text, offset, length); } /* /********************************************************** /* Public API, write methods, binary/raw content /********************************************************** */ @Override public void writeRaw(String text) throws IOException { delegate.writeRaw(text); } @Override public void writeRaw(String text, int offset, int len) throws IOException { delegate.writeRaw(text, offset, len); } @Override public void writeRaw(SerializableString raw) throws IOException { delegate.writeRaw(raw); } @Override public void writeRaw(char[] text, int offset, int len) throws IOException { delegate.writeRaw(text, offset, len); } @Override public void writeRaw(char c) throws IOException { delegate.writeRaw(c); } @Override public void writeRawValue(String text) throws IOException { delegate.writeRawValue(text); } @Override public void writeRawValue(String text, int offset, int len) throws IOException { delegate.writeRawValue(text, offset, len); } @Override public void writeRawValue(char[] text, int offset, int len) throws IOException { delegate.writeRawValue(text, offset, len); } @Override public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException { delegate.writeBinary(b64variant, data, offset, len); } @Override public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) throws IOException { return delegate.writeBinary(b64variant, data, dataLength); } /* /********************************************************** /* Public API, write methods, other value types /********************************************************** */ @Override public void writeNumber(short v) throws IOException { delegate.writeNumber(v); } @Override public void writeNumber(int v) throws IOException { delegate.writeNumber(v); } @Override public void writeNumber(long v) throws IOException { delegate.writeNumber(v); } @Override public void writeNumber(BigInteger v) throws IOException { delegate.writeNumber(v); } @Override public void writeNumber(double v) throws IOException { delegate.writeNumber(v); } @Override public void writeNumber(float v) throws IOException { delegate.writeNumber(v); } @Override public void writeNumber(BigDecimal v) throws IOException { delegate.writeNumber(v); } @Override public void writeNumber(String encodedValue) throws IOException, UnsupportedOperationException { delegate.writeNumber(encodedValue); } @Override public void writeBoolean(boolean state) throws IOException { delegate.writeBoolean(state); } @Override public void writeNull() throws IOException { delegate.writeNull(); } /* /********************************************************** /* Overridden field methods /********************************************************** */ @Override public void writeOmittedField(String fieldName) throws IOException { delegate.writeOmittedField(fieldName); } /* /********************************************************** /* Public API, write methods, Native Ids /********************************************************** */ @Override public void writeObjectId(Object id) throws IOException { delegate.writeObjectId(id); } @Override public void writeObjectRef(Object id) throws IOException { delegate.writeObjectRef(id); } @Override public void writeTypeId(Object id) throws IOException { delegate.writeTypeId(id); } @Override public void writeEmbeddedObject(Object object) throws IOException { delegate.writeEmbeddedObject(object); } /* /********************************************************** /* Public API, write methods, serializing Java objects /********************************************************** */ @Override public void writeObject(Object pojo) throws IOException { if (delegateCopyMethods) { delegate.writeObject(pojo); return; } if (pojo == null) { writeNull(); } else { ObjectCodec c = getCodec(); if (c != null) { c.writeValue(this, pojo); return; } _writeSimpleObject(pojo); } } @Override public void writeTree(TreeNode tree) throws IOException { if (delegateCopyMethods) { delegate.writeTree(tree); return; } // As with 'writeObject()', we are not check if write would work if (tree == null) { writeNull(); } else { ObjectCodec c = getCodec(); if (c == null) { throw new IllegalStateException("No ObjectCodec defined"); } c.writeTree(this, tree); } } /* /********************************************************** /* Public API, convenience field write methods /********************************************************** */ // // These are fine, just delegate to other methods... /* /********************************************************** /* Public API, copy-through methods /********************************************************** */ @Override public void copyCurrentEvent(JsonParser p) throws IOException { if (delegateCopyMethods) delegate.copyCurrentEvent(p); else super.copyCurrentEvent(p); } @Override public void copyCurrentStructure(JsonParser p) throws IOException { if (delegateCopyMethods) delegate.copyCurrentStructure(p); else super.copyCurrentStructure(p); } /* /********************************************************** /* Public API, context access /********************************************************** */ @Override public JsonStreamContext getOutputContext() { return delegate.getOutputContext(); } /* /********************************************************** /* Public API, buffer handling /********************************************************** */ @Override public void flush() throws IOException { delegate.flush(); } @Override public void close() throws IOException { delegate.close(); } /* /********************************************************** /* Closeable implementation /********************************************************** */ @Override public boolean isClosed() { return delegate.isClosed(); } } JsonParserDelegate.java000066400000000000000000000250341356164247300336250ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.core.*; /** * Helper class that implements * delegation pattern for {@link JsonParser}, * to allow for simple overridability of basic parsing functionality. * The idea is that any functionality to be modified can be simply * overridden; and anything else will be delegated by default. */ public class JsonParserDelegate extends JsonParser { /** * Delegate object that method calls are delegated to. */ protected JsonParser delegate; public JsonParserDelegate(JsonParser d) { delegate = d; } @Override public Object getCurrentValue() { return delegate.getCurrentValue(); } @Override public void setCurrentValue(Object v) { delegate.setCurrentValue(v); } /* /********************************************************** /* Public API, configuration /********************************************************** */ @Override public void setCodec(ObjectCodec c) { delegate.setCodec(c); } @Override public ObjectCodec getCodec() { return delegate.getCodec(); } @Override public JsonParser enable(Feature f) { delegate.enable(f); return this; } @Override public JsonParser disable(Feature f) { delegate.disable(f); return this; } @Override public boolean isEnabled(Feature f) { return delegate.isEnabled(f); } @Override public int getFeatureMask() { return delegate.getFeatureMask(); } @Override @Deprecated // since 2.7 public JsonParser setFeatureMask(int mask) { delegate.setFeatureMask(mask); return this; } @Override public JsonParser overrideStdFeatures(int values, int mask) { delegate.overrideStdFeatures(values, mask); return this; } @Override public JsonParser overrideFormatFeatures(int values, int mask) { delegate.overrideFormatFeatures(values, mask); return this; } @Override public FormatSchema getSchema() { return delegate.getSchema(); } @Override public void setSchema(FormatSchema schema) { delegate.setSchema(schema); } @Override public boolean canUseSchema(FormatSchema schema) { return delegate.canUseSchema(schema); } @Override public Version version() { return delegate.version(); } @Override public Object getInputSource() { return delegate.getInputSource(); } /* /********************************************************** /* Capability introspection /********************************************************** */ @Override public boolean requiresCustomCodec() { return delegate.requiresCustomCodec(); } /* /********************************************************** /* Closeable impl /********************************************************** */ @Override public void close() throws IOException { delegate.close(); } @Override public boolean isClosed() { return delegate.isClosed(); } /* /********************************************************** /* 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(); } @Override public boolean hasTokenId(int id) { return delegate.hasTokenId(id); } @Override public boolean hasToken(JsonToken t) { return delegate.hasToken(t); } @Override public String getCurrentName() throws IOException { return delegate.getCurrentName(); } @Override public JsonLocation getCurrentLocation() { return delegate.getCurrentLocation(); } @Override public JsonStreamContext getParsingContext() { return delegate.getParsingContext(); } @Override public boolean isExpectedStartArrayToken() { return delegate.isExpectedStartArrayToken(); } @Override public boolean isExpectedStartObjectToken() { return delegate.isExpectedStartObjectToken(); } @Override public boolean isNaN() throws IOException { return delegate.isNaN(); } /* /********************************************************** /* Public API, token state overrides /********************************************************** */ @Override public void clearCurrentToken() { delegate.clearCurrentToken(); } @Override public JsonToken getLastClearedToken() { return delegate.getLastClearedToken(); } @Override public void overrideCurrentName(String name) { delegate.overrideCurrentName(name); } /* /********************************************************** /* Public API, access to token information, text /********************************************************** */ @Override public String getText() throws IOException { return delegate.getText(); } @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); } @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); } /* /********************************************************** /* Public API, access to token information, numeric /********************************************************** */ @Override public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); } @Override public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); } @Override public byte getByteValue() throws IOException { return delegate.getByteValue(); } @Override public short getShortValue() throws IOException { return delegate.getShortValue(); } @Override public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); } @Override public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); } @Override public float getFloatValue() throws IOException { return delegate.getFloatValue(); } @Override public int getIntValue() throws IOException { return delegate.getIntValue(); } @Override public long getLongValue() throws IOException { return delegate.getLongValue(); } @Override public NumberType getNumberType() throws IOException { return delegate.getNumberType(); } @Override public Number getNumberValue() throws IOException { return delegate.getNumberValue(); } /* /********************************************************** /* Public API, access to token information, coercion/conversion /********************************************************** */ @Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); } @Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); } @Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); } @Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); } @Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); } @Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); } @Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); } @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); } @Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); } @Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); } /* /********************************************************** /* Public API, access to token values, other /********************************************************** */ @Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); } @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); } @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); } @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 { delegate.skipChildren(); // NOTE: must NOT delegate this method to delegate, needs to be self-reference for chaining return this; } /* /********************************************************** /* Public API, Native Ids (type, object) /********************************************************** */ @Override public boolean canReadObjectId() { return delegate.canReadObjectId(); } @Override public boolean canReadTypeId() { return delegate.canReadTypeId(); } @Override public Object getObjectId() throws IOException { return delegate.getObjectId(); } @Override public Object getTypeId() throws IOException { return delegate.getTypeId(); } /* /********************************************************** /* Extended API /********************************************************** */ /** * Accessor for getting the immediate {@link JsonParser} this parser delegates calls to. * * @since 2.10 */ public JsonParser delegate() { return delegate; } } JsonParserSequence.java000066400000000000000000000170701356164247300336640ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; import java.io.IOException; import java.util.*; import com.fasterxml.jackson.core.*; /** * Helper class that can be used to sequence multiple physical * {@link JsonParser}s to create a single logical sequence of * tokens, as a single {@link JsonParser}. *

* Fairly simple use of {@link JsonParserDelegate}: only need * to override {@link #nextToken} to handle transition */ public class JsonParserSequence extends JsonParserDelegate { /** * Parsers other than the first one (which is initially assigned * 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 true), 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. *

* Default setting is false (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 _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 ******************************************************* */ @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; _nextParserIndex = 1; } /** * Method that will construct a parser (possibly a sequence) that * contains all given sub-parsers. * All parsers given are checked to see if they are sequences: and * if so, they will be "flattened", that is, contained parsers are * directly added in a new sequence instead of adding sequences * within sequences. This is done to minimize delegation depth, * ideally only having just a single level of delegation. */ public static JsonParserSequence createFlattened(boolean checkForExistingToken, JsonParser first, JsonParser second) { if (!(first instanceof JsonParserSequence || second instanceof JsonParserSequence)) { return new JsonParserSequence(checkForExistingToken, new JsonParser[] { first, second }); } ArrayList p = new ArrayList(); if (first instanceof JsonParserSequence) { ((JsonParserSequence) first).addFlattenedActiveParsers(p); } else { p.add(first); } if (second instanceof JsonParserSequence) { ((JsonParserSequence) second).addFlattenedActiveParsers(p); } else { p.add(second); } 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 listToAddIn) { for (int i = _nextParserIndex-1, len = _parsers.length; i < len; ++i) { JsonParser p = _parsers[i]; if (p instanceof JsonParserSequence) { ((JsonParserSequence) p).addFlattenedActiveParsers(listToAddIn); } else { listToAddIn.add(p); } } } /* /******************************************************* /* 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 { if (delegate == null) { return null; } if (_hasToken) { _hasToken = false; return delegate.currentToken(); } JsonToken t = delegate.nextToken(); if (t == null) { return switchAndReturnNext(); } return t; } /** * Need to override, re-implement similar to how method defined in * {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep * state correct here. */ @Override public JsonParser skipChildren() throws IOException { if ((delegate.currentToken() != JsonToken.START_OBJECT) && (delegate.currentToken() != JsonToken.START_ARRAY)) { return this; } int open = 1; // Since proper matching of start/end markers is handled // by nextToken(), we'll just count nesting levels here while (true) { JsonToken t = nextToken(); if (t == null) { // not ideal but for now, just return return this; } if (t.isStructStart()) { ++open; } else if (t.isStructEnd()) { if (--open == 0) { return this; } } } } /* /******************************************************* /* Additional extended API /******************************************************* */ /** * Method that is most useful for debugging or testing; * returns actual number of underlying parsers sequence * was constructed with (nor just ones remaining active) */ public int containedParsersCount() { return _parsers.length; } /* /******************************************************* /* Helper methods /******************************************************* */ /** * 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 (_nextParserIndex < _parsers.length) { delegate = _parsers[_nextParserIndex++]; 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; } } MinimalPrettyPrinter.java000066400000000000000000000105221356164247300342420ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; import java.io.IOException; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.PrettyPrinter; /** * {@link PrettyPrinter} implementation that adds no indentation, * just implements everything necessary for value output to work * as expected, and provide simpler extension points to allow * for creating simple custom implementations that add specific * decoration or overrides. Since behavior then is very similar * to using no pretty printer at all, usually sub-classes are used. *

* Beyond purely minimal implementation, there is limited amount of * configurability which may be useful for actual use: for example, * it is possible to redefine separator used between root-level * values (default is single space; can be changed to line-feed). *

* Note: does NOT implement {@link Instantiatable} since this is * a stateless implementation; that is, a single instance can be * shared between threads. */ public class MinimalPrettyPrinter implements PrettyPrinter, java.io.Serializable { private static final long serialVersionUID = 1L; protected String _rootValueSeparator; /** * @since 2.9 */ protected Separators _separators; /* /********************************************************** /* Life-cycle, construction, configuration /********************************************************** */ public MinimalPrettyPrinter() { this(DEFAULT_ROOT_VALUE_SEPARATOR.toString()); } public MinimalPrettyPrinter(String rootValueSeparator) { _rootValueSeparator = rootValueSeparator; _separators = DEFAULT_SEPARATORS; } public void setRootValueSeparator(String sep) { _rootValueSeparator = sep; } /** * @since 2.9 */ public MinimalPrettyPrinter setSeparators(Separators separators) { _separators = separators; return this; } /* /********************************************************** /* PrettyPrinter impl /********************************************************** */ @Override public void writeRootValueSeparator(JsonGenerator g) throws IOException { if (_rootValueSeparator != null) { g.writeRaw(_rootValueSeparator); } } @Override public void writeStartObject(JsonGenerator g) throws IOException { g.writeRaw('{'); } @Override public void beforeObjectEntries(JsonGenerator g) throws IOException { // nothing special, since no indentation is added } /** * Method called after an object field has been output, but * before the value is output. *

* Default handling will just output a single * colon to separate the two, without additional spaces. */ @Override public void writeObjectFieldValueSeparator(JsonGenerator g) throws IOException { g.writeRaw(_separators.getObjectFieldValueSeparator()); } /** * Method called after an object entry (field:value) has been completely * output, and before another value is to be output. *

* Default handling (without pretty-printing) will output a single * comma to separate the two. */ @Override public void writeObjectEntrySeparator(JsonGenerator g) throws IOException { g.writeRaw(_separators.getObjectEntrySeparator()); } @Override public void writeEndObject(JsonGenerator g, int nrOfEntries) throws IOException { g.writeRaw('}'); } @Override public void writeStartArray(JsonGenerator g) throws IOException { g.writeRaw('['); } @Override public void beforeArrayValues(JsonGenerator g) throws IOException { // nothing special, since no indentation is added } /** * Method called after an array value has been completely * output, and before another value is to be output. *

* Default handling (without pretty-printing) will output a single * comma to separate values. */ @Override public void writeArrayValueSeparator(JsonGenerator g) throws IOException { g.writeRaw(_separators.getArrayValueSeparator()); } @Override public void writeEndArray(JsonGenerator g, int nrOfValues) throws IOException { g.writeRaw(']'); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/RequestPayload.java000066400000000000000000000035271356164247300331300ustar00rootroot00000000000000package 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(); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/Separators.java000066400000000000000000000036021356164247300323030ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import java.io.Serializable; /** * Value class used with some {@link com.fasterxml.jackson.core.PrettyPrinter} * implements * * @see com.fasterxml.jackson.core.util.DefaultPrettyPrinter * @see com.fasterxml.jackson.core.util.MinimalPrettyPrinter * * @since 2.9 */ public class Separators implements Serializable { private static final long serialVersionUID = 1; private final char objectFieldValueSeparator; private final char objectEntrySeparator; private final char arrayValueSeparator; public static Separators createDefaultInstance() { return new Separators(); } public Separators() { this(':', ',', ','); } public Separators(char objectFieldValueSeparator, char objectEntrySeparator, char arrayValueSeparator) { this.objectFieldValueSeparator = objectFieldValueSeparator; this.objectEntrySeparator = objectEntrySeparator; this.arrayValueSeparator = arrayValueSeparator; } public Separators withObjectFieldValueSeparator(char sep) { return (objectFieldValueSeparator == sep) ? this : new Separators(sep, objectEntrySeparator, arrayValueSeparator); } public Separators withObjectEntrySeparator(char sep) { return (objectEntrySeparator == sep) ? this : new Separators(objectFieldValueSeparator, sep, arrayValueSeparator); } public Separators withArrayValueSeparator(char sep) { return (arrayValueSeparator == sep) ? this : new Separators(objectFieldValueSeparator, objectEntrySeparator, sep); } public char getObjectFieldValueSeparator() { return objectFieldValueSeparator; } public char getObjectEntrySeparator() { return objectEntrySeparator; } public char getArrayValueSeparator() { return arrayValueSeparator; } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java000066400000000000000000000654741356164247300322550ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import java.io.*; import java.math.BigDecimal; import java.util.*; import com.fasterxml.jackson.core.io.NumberInput; /** * TextBuffer is a class similar to {@link StringBuffer}, with * following differences: *

    *
  • TextBuffer uses segments character arrays, to avoid having * to do additional array copies when array is not big enough. * This means that only reallocating that is necessary is done only once: * if and when caller * wants to access contents in a linear array (char[], String). *
  • *
  • TextBuffer can also be initialized in "shared mode", in which * it will just act as a wrapper to a single char array managed * by another object (like parser that owns it) *
  • *
  • TextBuffer is not synchronized. *
  • *
*/ public final class TextBuffer { final static char[] NO_CHARS = new char[0]; /** * Let's start with sizable but not huge buffer, will grow as necessary *

* Reduced from 1000 down to 500 in 2.10. */ final static int MIN_SEGMENT_LEN = 500; /** * Let's limit maximum segment length to something sensible. * For 2.10, let's limit to using 64kc chunks (128 kB) -- was 256kC/512kB up to 2.9 */ final static int MAX_SEGMENT_LEN = 0x10000; /* /********************************************************** /* Configuration: /********************************************************** */ private final BufferRecycler _allocator; /* /********************************************************** /* Shared input buffers /********************************************************** */ /** * Shared input buffer; stored here in case some input can be returned * as is, without being copied to collector's own buffers. Note that * this is read-only for this Object. */ private char[] _inputBuffer; /** * Character offset of first char in input buffer; -1 to indicate * that input buffer currently does not contain any useful char data */ private int _inputStart; private int _inputLen; /* /********************************************************** /* Aggregation segments (when not using input buf) /********************************************************** */ /** * List of segments prior to currently active segment. */ private ArrayList _segments; /** * Flag that indicates whether _seqments is non-empty */ private boolean _hasSegments; // // // Currently used segment; not (yet) contained in _seqments /** * Amount of characters in segments in {@link _segments} */ private int _segmentSize; private char[] _currentSegment; /** * Number of characters in currently active (last) segment */ private int _currentSize; /* /********************************************************** /* Caching of results /********************************************************** */ /** * String that will be constructed when the whole contents are * needed; will be temporarily stored in case asked for again. */ private String _resultString; private char[] _resultArray; /* /********************************************************** /* Life-cycle /********************************************************** */ public TextBuffer(BufferRecycler allocator) { _allocator = allocator; } /** * @since 2.10 */ protected TextBuffer(BufferRecycler allocator, char[] initialSegment) { _allocator = allocator; _currentSegment = initialSegment; _currentSize = initialSegment.length; _inputStart = -1; } /** * Factory method for constructing an instance with no allocator, and * with initial full segment. * * @since 2.10 */ public static TextBuffer fromInitial(char[] initialSegment) { return new TextBuffer(null, initialSegment); } /** * Method called to indicate that the underlying buffers should now * be recycled if they haven't yet been recycled. Although caller * can still use this text buffer, it is not advisable to call this * method if that is likely, since next time a buffer is needed, * buffers need to reallocated. * Note: calling this method automatically also clears contents * of the buffer. */ public void releaseBuffers() { if (_allocator == null) { resetWithEmpty(); } else { if (_currentSegment != null) { // First, let's get rid of all but the largest char array resetWithEmpty(); // And then return that array char[] buf = _currentSegment; _currentSegment = null; _allocator.releaseCharBuffer(BufferRecycler.CHAR_TEXT_BUFFER, buf); } } } /** * Method called to clear out any content text buffer may have, and * initializes buffer to use non-shared data. */ public void resetWithEmpty() { _inputStart = -1; // indicates shared buffer not used _currentSize = 0; _inputLen = 0; _inputBuffer = null; _resultString = null; _resultArray = null; // And then reset internal input buffers, if necessary: if (_hasSegments) { clearSegments(); } } /** * @since 2.9 */ public void resetWith(char ch) { _inputStart = -1; _inputLen = 0; _resultString = null; _resultArray = null; if (_hasSegments) { clearSegments(); } else if (_currentSegment == null) { _currentSegment = buf(1); } _currentSegment[0] = ch; _currentSize = _segmentSize = 1; } /** * Method called to initialize the buffer with a shared copy of data; * this means that buffer will just have pointers to actual data. It * also means that if anything is to be appended to the buffer, it * will first have to unshare it (make a local copy). */ public void resetWithShared(char[] buf, int start, int len) { // First, let's clear intermediate values, if any: _resultString = null; _resultArray = null; // Then let's mark things we need about input buffer _inputBuffer = buf; _inputStart = start; _inputLen = len; // And then reset internal input buffers, if necessary: if (_hasSegments) { clearSegments(); } } public void resetWithCopy(char[] buf, int start, int len) { _inputBuffer = null; _inputStart = -1; // indicates shared buffer not used _inputLen = 0; _resultString = null; _resultArray = null; // And then reset internal input buffers, if necessary: if (_hasSegments) { clearSegments(); } else if (_currentSegment == null) { _currentSegment = buf(len); } _currentSize = _segmentSize = 0; append(buf, start, len); } /** * @since 2.9 */ public void resetWithCopy(String text, int start, int len) { _inputBuffer = null; _inputStart = -1; _inputLen = 0; _resultString = null; _resultArray = null; if (_hasSegments) { clearSegments(); } else if (_currentSegment == null) { _currentSegment = buf(len); } _currentSize = _segmentSize = 0; append(text, start, len); } public void resetWithString(String value) { _inputBuffer = null; _inputStart = -1; _inputLen = 0; _resultString = value; _resultArray = null; if (_hasSegments) { clearSegments(); } _currentSize = 0; } /** * @since 2.9 */ public char[] getBufferWithoutReset() { return _currentSegment; } /** * Helper method used to find a buffer to use, ideally one * recycled earlier. */ private char[] buf(int needed) { if (_allocator != null) { return _allocator.allocCharBuffer(BufferRecycler.CHAR_TEXT_BUFFER, needed); } return new char[Math.max(needed, MIN_SEGMENT_LEN)]; } private void clearSegments() { _hasSegments = false; /* Let's start using _last_ segment from list; for one, it's * the biggest one, and it's also most likely to be cached */ /* 28-Aug-2009, tatu: Actually, the current segment should * be the biggest one, already */ //_currentSegment = _segments.get(_segments.size() - 1); _segments.clear(); _currentSize = _segmentSize = 0; } /* /********************************************************** /* Accessors for implementing public interface /********************************************************** */ /** * @return Number of characters currently stored by this collector */ public int size() { if (_inputStart >= 0) { // shared copy from input buf return _inputLen; } if (_resultArray != null) { return _resultArray.length; } if (_resultString != null) { return _resultString.length(); } // local segmented buffers return _segmentSize + _currentSize; } public int getTextOffset() { /* Only shared input buffer can have non-zero offset; buffer * segments start at 0, and if we have to create a combo buffer, * that too will start from beginning of the buffer */ return (_inputStart >= 0) ? _inputStart : 0; } /** * Method that can be used to check whether textual contents can * be efficiently accessed using {@link #getTextBuffer}. */ public boolean hasTextAsCharacters() { // if we have array in some form, sure if (_inputStart >= 0 || _resultArray != null) return true; // not if we have String as value if (_resultString != null) return false; return true; } /** * Accessor that may be used to get the contents of this buffer in a single * char array regardless of whether they were collected in a segmented * fashion or not. */ public char[] getTextBuffer() { // Are we just using shared input buffer? if (_inputStart >= 0) return _inputBuffer; if (_resultArray != null) return _resultArray; if (_resultString != null) { return (_resultArray = _resultString.toCharArray()); } // Nope; but does it fit in just one segment? if (!_hasSegments) { return (_currentSegment == null) ? NO_CHARS : _currentSegment; } // Nope, need to have/create a non-segmented array and return it return contentsAsArray(); } /* /********************************************************** /* Other accessors: /********************************************************** */ public String contentsAsString() { if (_resultString == null) { // Has array been requested? Can make a shortcut, if so: if (_resultArray != null) { _resultString = new String(_resultArray); } else { // Do we use shared array? if (_inputStart >= 0) { if (_inputLen < 1) { return (_resultString = ""); } _resultString = new String(_inputBuffer, _inputStart, _inputLen); } else { // nope... need to copy // But first, let's see if we have just one buffer int segLen = _segmentSize; int currLen = _currentSize; if (segLen == 0) { // yup _resultString = (currLen == 0) ? "" : new String(_currentSegment, 0, currLen); } else { // no, need to combine StringBuilder sb = new StringBuilder(segLen + currLen); // First stored segments if (_segments != null) { for (int i = 0, len = _segments.size(); i < len; ++i) { char[] curr = _segments.get(i); sb.append(curr, 0, curr.length); } } // And finally, current segment: sb.append(_currentSegment, 0, _currentSize); _resultString = sb.toString(); } } } } return _resultString; } public char[] contentsAsArray() { char[] result = _resultArray; if (result == null) { _resultArray = result = resultArray(); } return result; } /** * Convenience method for converting contents of the buffer * into a {@link BigDecimal}. */ public BigDecimal contentsAsDecimal() throws NumberFormatException { // Already got a pre-cut array? if (_resultArray != null) { return NumberInput.parseBigDecimal(_resultArray); } // Or a shared buffer? if ((_inputStart >= 0) && (_inputBuffer != null)) { return NumberInput.parseBigDecimal(_inputBuffer, _inputStart, _inputLen); } // Or if not, just a single buffer (the usual case) if ((_segmentSize == 0) && (_currentSegment != null)) { return NumberInput.parseBigDecimal(_currentSegment, 0, _currentSize); } // If not, let's just get it aggregated... return NumberInput.parseBigDecimal(contentsAsArray()); } /** * Convenience method for converting contents of the buffer * into a Double value. */ public double contentsAsDouble() throws NumberFormatException { return NumberInput.parseDouble(contentsAsString()); } /** * Specialized convenience method that will decode a 32-bit int, * of at most 9 digits (and possible leading minus sign). * * @param neg Whether contents start with a minus sign * * @since 2.9 */ public int contentsAsInt(boolean neg) { if ((_inputStart >= 0) && (_inputBuffer != null)) { if (neg) { return -NumberInput.parseInt(_inputBuffer, _inputStart+1, _inputLen-1); } return NumberInput.parseInt(_inputBuffer, _inputStart, _inputLen); } if (neg) { return -NumberInput.parseInt(_currentSegment, 1, _currentSize-1); } return NumberInput.parseInt(_currentSegment, 0, _currentSize); } /** * Specialized convenience method that will decode a 64-bit int, * of at most 18 digits (and possible leading minus sign). * * @param neg Whether contents start with a minus sign * * @since 2.9 */ public long contentsAsLong(boolean neg) { if ((_inputStart >= 0) && (_inputBuffer != null)) { if (neg) { return -NumberInput.parseLong(_inputBuffer, _inputStart+1, _inputLen-1); } return NumberInput.parseLong(_inputBuffer, _inputStart, _inputLen); } if (neg) { return -NumberInput.parseLong(_currentSegment, 1, _currentSize-1); } return NumberInput.parseLong(_currentSegment, 0, _currentSize); } /** * @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: /********************************************************** */ /** * Method called to make sure that buffer is not using shared input * buffer; if it is, it will copy such contents to private buffer. */ public void ensureNotShared() { if (_inputStart >= 0) { unshare(16); } } public void append(char c) { // Using shared buffer so far? if (_inputStart >= 0) { unshare(16); } _resultString = null; _resultArray = null; // Room in current segment? char[] curr = _currentSegment; if (_currentSize >= curr.length) { expand(1); curr = _currentSegment; } curr[_currentSize++] = c; } public void append(char[] c, int start, int len) { // Can't append to shared buf (sanity check) if (_inputStart >= 0) { unshare(len); } _resultString = null; _resultArray = null; // Room in current segment? char[] curr = _currentSegment; int max = curr.length - _currentSize; if (max >= len) { System.arraycopy(c, start, curr, _currentSize, len); _currentSize += len; return; } // No room for all, need to copy part(s): if (max > 0) { System.arraycopy(c, start, curr, _currentSize, max); start += max; len -= max; } // And then allocate new segment; we are guaranteed to now // have enough room in segment. do { expand(len); int amount = Math.min(_currentSegment.length, len); System.arraycopy(c, start, _currentSegment, 0, amount); _currentSize += amount; start += amount; len -= amount; } while (len > 0); } public void append(String str, int offset, int len) { // Can't append to shared buf (sanity check) if (_inputStart >= 0) { unshare(len); } _resultString = null; _resultArray = null; // Room in current segment? char[] curr = _currentSegment; int max = curr.length - _currentSize; if (max >= len) { str.getChars(offset, offset+len, curr, _currentSize); _currentSize += len; return; } // No room for all, need to copy part(s): if (max > 0) { str.getChars(offset, offset+max, curr, _currentSize); len -= max; offset += max; } // And then allocate new segment; we are guaranteed to now // have enough room in segment. do { expand(len); int amount = Math.min(_currentSegment.length, len); str.getChars(offset, offset+amount, _currentSegment, 0); _currentSize += amount; offset += amount; len -= amount; } while (len > 0); } /* /********************************************************** /* Raw access, for high-performance use: /********************************************************** */ public char[] getCurrentSegment() { /* Since the intention of the caller is to directly add stuff into * buffers, we should NOT have anything in shared buffer... ie. may * need to unshare contents. */ if (_inputStart >= 0) { unshare(1); } else { char[] curr = _currentSegment; if (curr == null) { _currentSegment = buf(0); } else if (_currentSize >= curr.length) { // Plus, we better have room for at least one more char expand(1); } } return _currentSegment; } public char[] emptyAndGetCurrentSegment() { // inlined 'resetWithEmpty()' _inputStart = -1; // indicates shared buffer not used _currentSize = 0; _inputLen = 0; _inputBuffer = null; _resultString = null; _resultArray = null; // And then reset internal input buffers, if necessary: if (_hasSegments) { clearSegments(); } char[] curr = _currentSegment; if (curr == null) { _currentSegment = curr = buf(0); } return curr; } public int getCurrentSegmentSize() { return _currentSize; } public void setCurrentLength(int len) { _currentSize = len; } /** * @since 2.6 */ public String setCurrentAndReturn(int len) { _currentSize = len; // We can simplify handling here compared to full `contentsAsString()`: if (_segmentSize > 0) { // longer text; call main method return contentsAsString(); } // more common case: single segment int currLen = _currentSize; String str = (currLen == 0) ? "" : new String(_currentSegment, 0, currLen); _resultString = str; return str; } public char[] finishCurrentSegment() { if (_segments == null) { _segments = new ArrayList(); } _hasSegments = true; _segments.add(_currentSegment); int oldLen = _currentSegment.length; _segmentSize += oldLen; _currentSize = 0; // Let's grow segments by 50% int newLen = oldLen + (oldLen >> 1); if (newLen < MIN_SEGMENT_LEN) { newLen = MIN_SEGMENT_LEN; } else if (newLen > MAX_SEGMENT_LEN) { newLen = MAX_SEGMENT_LEN; } char[] curr = carr(newLen); _currentSegment = curr; return curr; } /** * Method called to expand size of the current segment, to * accommodate for more contiguous content. Usually only * used when parsing tokens like names if even then. */ public char[] expandCurrentSegment() { final char[] curr = _currentSegment; // Let's grow by 50% by default final int len = curr.length; int newLen = len + (len >> 1); // but above intended maximum, slow to increase by 25% if (newLen > MAX_SEGMENT_LEN) { newLen = len + (len >> 2); } return (_currentSegment = Arrays.copyOf(curr, newLen)); } /** * Method called to expand size of the current segment, to * accommodate for more contiguous content. Usually only * used when parsing tokens like names if even then. * * @param minSize Required minimum strength of the current segment * * @since 2.4.0 */ public char[] expandCurrentSegment(int minSize) { char[] curr = _currentSegment; if (curr.length >= minSize) return curr; _currentSegment = curr = Arrays.copyOf(curr, minSize); return curr; } /* /********************************************************** /* Standard methods: /********************************************************** */ /** * Note: calling this method may not be as efficient as calling * {@link #contentsAsString}, since it's not guaranteed that resulting * String is cached. */ @Override public String toString() { return contentsAsString(); } /* /********************************************************** /* Internal methods: /********************************************************** */ /** * Method called if/when we need to append content when we have been * initialized to use shared buffer. */ private void unshare(int needExtra) { int sharedLen = _inputLen; _inputLen = 0; char[] inputBuf = _inputBuffer; _inputBuffer = null; int start = _inputStart; _inputStart = -1; // Is buffer big enough, or do we need to reallocate? int needed = sharedLen+needExtra; if (_currentSegment == null || needed > _currentSegment.length) { _currentSegment = buf(needed); } if (sharedLen > 0) { System.arraycopy(inputBuf, start, _currentSegment, 0, sharedLen); } _segmentSize = 0; _currentSize = sharedLen; } /** * Method called when current segment is full, to allocate new * segment. */ private void expand(int minNewSegmentSize) { // First, let's move current segment to segment list: if (_segments == null) { _segments = new ArrayList(); } char[] curr = _currentSegment; _hasSegments = true; _segments.add(curr); _segmentSize += curr.length; _currentSize = 0; int oldLen = curr.length; // Let's grow segments by 50% minimum int newLen = oldLen + (oldLen >> 1); if (newLen < MIN_SEGMENT_LEN) { newLen = MIN_SEGMENT_LEN; } else if (newLen > MAX_SEGMENT_LEN) { newLen = MAX_SEGMENT_LEN; } _currentSegment = carr(newLen); } private char[] resultArray() { if (_resultString != null) { // Can take a shortcut... return _resultString.toCharArray(); } // Do we use shared array? if (_inputStart >= 0) { final int len = _inputLen; if (len < 1) { return NO_CHARS; } final int start = _inputStart; if (start == 0) { return Arrays.copyOf(_inputBuffer, len); } return Arrays.copyOfRange(_inputBuffer, start, start+len); } // nope, not shared int size = size(); if (size < 1) { return NO_CHARS; } int offset = 0; final char[] result = carr(size); if (_segments != null) { for (int i = 0, len = _segments.size(); i < len; ++i) { char[] curr = _segments.get(i); int currLen = curr.length; System.arraycopy(curr, 0, result, offset, currLen); offset += currLen; } } System.arraycopy(_currentSegment, 0, result, offset, _currentSize); return result; } private char[] carr(int len) { return new char[len]; } } ThreadLocalBufferManager.java000066400000000000000000000116051356164247300347120ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * For issue [jackson-core#400] We keep a separate Set of all SoftReferences to BufferRecyclers * which are (also) referenced using `ThreadLocals`. * We do this to be able to release them (dereference) in `releaseBuffers()` and `shutdown()` * method to reduce heap consumption during hot reloading of services where otherwise * {@link ClassLoader} would have dangling reference via {@link ThreadLocal}s. * When gc clears a SoftReference, it puts it on a newly introduced referenceQueue. * We use this queue to release the inactive SoftReferences from the Set. * * @since 2.9.6 */ class ThreadLocalBufferManager { /** * A lock to make sure releaseBuffers is only executed by one thread at a time * since it iterates over and modifies the allSoftBufRecyclers. */ private final Object RELEASE_LOCK = new Object(); /** * A set of all SoftReferences to all BufferRecyclers to be able to release them on shutdown. * 'All' means the ones created by this class, in this classloader. * There may be more from other classloaders. * We use a HashSet to have quick O(1) add and remove operations. *

* NOTE: assumption is that {@link SoftReference} has its {@code equals()} and * {@code hashCode()} implementations defined so that they use object identity, so * we do not need to use something like {@link IdentityHashMap} */ private final Map,Boolean> _trackedRecyclers = new ConcurrentHashMap, Boolean>(); /** * Queue where gc will put just-cleared SoftReferences, previously referencing BufferRecyclers. * We use it to remove the cleared softRefs from the above set. */ private final ReferenceQueue _refQueue = new ReferenceQueue(); /* /********************************************************** /* Public API /********************************************************** */ /** * Returns the lazily initialized singleton instance */ public static ThreadLocalBufferManager instance() { return ThreadLocalBufferManagerHolder.manager; } /** * Releases the buffers retained in ThreadLocals. To be called for instance on shutdown event of applications which make use of * an environment like an appserver which stays alive and uses a thread pool that causes ThreadLocals created by the * application to survive much longer than the application itself. * It will clear all bufRecyclers from the SoftRefs and release all SoftRefs itself from our set. */ public int releaseBuffers() { synchronized (RELEASE_LOCK) { int count = 0; // does this need to be in sync block too? Looping over Map definitely has to but... removeSoftRefsClearedByGc(); // make sure the refQueue is empty for (SoftReference ref : _trackedRecyclers.keySet()) { ref.clear(); // possibly already cleared by gc, nothing happens in that case ++count; } _trackedRecyclers.clear(); //release cleared SoftRefs return count; } } public SoftReference wrapAndTrack(BufferRecycler br) { SoftReference newRef; newRef = new SoftReference(br, _refQueue); // also retain softRef to br in a set to be able to release it on shutdown _trackedRecyclers.put(newRef, true); // gc may have cleared one or more SoftRefs, clean them up to avoid a memleak removeSoftRefsClearedByGc(); return newRef; } /* /********************************************************** /* Internal methods /********************************************************** */ /** * Remove cleared (inactive) SoftRefs from our set. Gc may have cleared one or more, * and made them inactive. We minimize contention by keeping synchronized sections short: * the poll/remove methods */ private void removeSoftRefsClearedByGc() { SoftReference clearedSoftRef; while ((clearedSoftRef = (SoftReference) _refQueue.poll()) != null) { // uses reference-equality, quick, and O(1) removal by HashSet _trackedRecyclers.remove(clearedSoftRef); } } /** * ThreadLocalBufferManagerHolder uses the thread-safe initialize-on-demand, holder class idiom that implicitly * incorporates lazy initialization by declaring a static variable within a static Holder inner class */ private static final class ThreadLocalBufferManagerHolder { static final ThreadLocalBufferManager manager = new ThreadLocalBufferManager(); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/VersionUtil.java000066400000000000000000000140651356164247300324500ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import java.io.*; import java.util.Properties; import java.util.regex.Pattern; import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.core.Versioned; /** * Functionality for supporting exposing of component {@link Version}s. * Also contains other misc methods that have no other place to live in. *

* Note that this class can be used in two roles: first, as a static * utility class for loading purposes, and second, as a singleton * loader of per-module version information. *

* Note that method for accessing version information changed between versions * 2.1 and 2.2; earlier code used file named "VERSION.txt"; but this has serious * performance issues on some platforms (Android), so a replacement system * was implemented to use class generation and dynamic class loading. *

* Note that functionality for reading "VERSION.txt" was removed completely * from Jackson 2.6. */ public class VersionUtil { private final static Pattern V_SEP = Pattern.compile("[-_./;:]"); /* /********************************************************** /* Instance life-cycle /********************************************************** */ protected VersionUtil() { } @Deprecated // since 2.9 public Version version() { return Version.unknownVersion(); } /* /********************************************************** /* Static load methods /********************************************************** */ /** * Helper method that will try to load version information for specified * class. Implementation is as follows: * * First, tries to load version info from a class named * "PackageVersion" in the same package as the class. * * If no version information is found, {@link Version#unknownVersion()} is returned. */ public static Version versionFor(Class cls) { Version version = packageVersionFor(cls); return version == null ? Version.unknownVersion() : version; } /** * Loads version information by introspecting a class named * "PackageVersion" in the same package as the given class. *

* If the class could not be found or does not have a public * static Version field named "VERSION", returns null. */ public static Version packageVersionFor(Class cls) { Version v = null; try { String versionInfoClassName = cls.getPackage().getName() + ".PackageVersion"; Class vClass = Class.forName(versionInfoClassName, true, cls.getClassLoader()); // However, if class exists, it better work correctly, no swallowing exceptions try { v = ((Versioned) vClass.getDeclaredConstructor().newInstance()).version(); } catch (Exception e) { throw new IllegalArgumentException("Failed to get Versioned out of "+vClass); } } catch (Exception e) { // ok to be missing (not good but acceptable) ; } return (v == null) ? Version.unknownVersion() : v; } /** * Will attempt to load the maven version for the given groupId and * artifactId. Maven puts a pom.properties file in * META-INF/maven/groupId/artifactId, containing the groupId, * artifactId and version of the library. * * @param cl the ClassLoader to load the pom.properties file from * @param groupId the groupId of the library * @param artifactId the artifactId of the library * @return The version * * @deprecated Since 2.6: functionality not used by any official Jackson component, should be * moved out if anyone needs it */ @SuppressWarnings("resource") @Deprecated // since 2.6 public static Version mavenVersionFor(ClassLoader cl, String groupId, String artifactId) { InputStream pomProperties = cl.getResourceAsStream("META-INF/maven/" + groupId.replaceAll("\\.", "/")+ "/" + artifactId + "/pom.properties"); if (pomProperties != null) { try { Properties props = new Properties(); props.load(pomProperties); String versionStr = props.getProperty("version"); String pomPropertiesArtifactId = props.getProperty("artifactId"); String pomPropertiesGroupId = props.getProperty("groupId"); return parseVersion(versionStr, pomPropertiesGroupId, pomPropertiesArtifactId); } catch (IOException e) { // Ignore } finally { _close(pomProperties); } } return Version.unknownVersion(); } /** * Method used by PackageVersion classes to decode version injected by Maven build. */ public static Version parseVersion(String s, String groupId, String artifactId) { if (s != null && (s = s.trim()).length() > 0) { String[] parts = V_SEP.split(s); return new Version(parseVersionPart(parts[0]), (parts.length > 1) ? parseVersionPart(parts[1]) : 0, (parts.length > 2) ? parseVersionPart(parts[2]) : 0, (parts.length > 3) ? parts[3] : null, groupId, artifactId); } return Version.unknownVersion(); } protected static int parseVersionPart(String s) { int number = 0; for (int i = 0, len = s.length(); i < len; ++i) { char c = s.charAt(i); if (c > '9' || c < '0') break; number = (number * 10) + (c - '0'); } return number; } private final static void _close(Closeable c) { try { c.close(); } catch (IOException e) { } } /* /********************************************************** /* Orphan utility methods /********************************************************** */ public final static void throwInternal() { throw new RuntimeException("Internal error: this code path should never get executed"); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/package-info.java000066400000000000000000000001501356164247300324770ustar00rootroot00000000000000/** * Utility classes used by Jackson Core functionality. */ package com.fasterxml.jackson.core.util; jackson-core-jackson-core-2.10.1/src/main/resources/000077500000000000000000000000001356164247300222655ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/resources/META-INF/000077500000000000000000000000001356164247300234255ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/resources/META-INF/LICENSE000066400000000000000000000005141356164247300244320ustar00rootroot00000000000000This copy of Jackson JSON processor streaming parser/generator is licensed under the Apache (Software) License, version 2.0 ("the License"). See the License for details about distribution rights, and the specific rights regarding derivate works. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 jackson-core-jackson-core-2.10.1/src/main/resources/META-INF/NOTICE000066400000000000000000000014661356164247300243400ustar00rootroot00000000000000# Jackson JSON processor Jackson is a high-performance, Free/Open Source JSON processing library. It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has been in development since 2007. It is currently developed by a community of developers, as well as supported commercially by FasterXML.com. ## Licensing Jackson core and extension components may licensed under different licenses. To find the details that apply to this artifact see the accompanying LICENSE file. For more information, including possible other licensing options, contact FasterXML.com (http://fasterxml.com). ## Credits A list of contributors may be found from CREDITS file, which is included in some artifacts (usually source distributions); but is always available from the source code management (SCM) system project uses. jackson-core-jackson-core-2.10.1/src/main/resources/META-INF/services/000077500000000000000000000000001356164247300252505ustar00rootroot00000000000000com.fasterxml.jackson.core.JsonFactory000066400000000000000000000000471356164247300345150ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/main/resources/META-INF/servicescom.fasterxml.jackson.core.JsonFactory jackson-core-jackson-core-2.10.1/src/moditect/000077500000000000000000000000001356164247300211375ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/moditect/module-info.java000066400000000000000000000021271356164247300242220ustar00rootroot00000000000000// Generated 08-Mar-2019 using Moditect maven plugin module com.fasterxml.jackson.core { // 08-Mar-2019, tatu: Ugh. Can not use wildcards, stupid ass JDK 9+ module system... // So, for 2.x core need to make sure we manually include everything. // Worse, there is only syntactic validation, not contents, so we can both miss // AND add bogus packages. // However: at least syntax is verified; and this works with JKD8 exports com.fasterxml.jackson.core; exports com.fasterxml.jackson.core.async; exports com.fasterxml.jackson.core.base; exports com.fasterxml.jackson.core.exc; exports com.fasterxml.jackson.core.filter; exports com.fasterxml.jackson.core.format; exports com.fasterxml.jackson.core.io; exports com.fasterxml.jackson.core.json; exports com.fasterxml.jackson.core.json.async; exports com.fasterxml.jackson.core.sym; exports com.fasterxml.jackson.core.type; exports com.fasterxml.jackson.core.util; // 03-Oct-2019, tatu: [core#567] Add self-use to avoid warnings uses com.fasterxml.jackson.core.ObjectCodec; } jackson-core-jackson-core-2.10.1/src/site/000077500000000000000000000000001356164247300202735ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/site/site.xml000066400000000000000000000016611356164247300217650ustar00rootroot00000000000000 org.apache.maven.skins maven-fluido-skin 1.1 true false cowtowncoder true true

jackson-core-jackson-core-2.10.1/src/test/000077500000000000000000000000001356164247300203065ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/000077500000000000000000000000001356164247300212275ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/000077500000000000000000000000001356164247300220055ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/000077500000000000000000000000001356164247300240125ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/000077500000000000000000000000001356164247300254425ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/000077500000000000000000000000001356164247300263725ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/BaseTest.java000066400000000000000000000505131356164247300307530ustar00rootroot00000000000000package com.fasterxml.jackson.core; 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 }; /* /********************************************************** /* Some sample documents: /********************************************************** */ protected final static int SAMPLE_SPEC_VALUE_WIDTH = 800; protected final static int SAMPLE_SPEC_VALUE_HEIGHT = 600; protected final static String SAMPLE_SPEC_VALUE_TITLE = "View from 15th Floor"; protected final static String SAMPLE_SPEC_VALUE_TN_URL = "http://www.example.com/image/481989943"; protected final static int SAMPLE_SPEC_VALUE_TN_HEIGHT = 125; protected final static String SAMPLE_SPEC_VALUE_TN_WIDTH = "100"; protected final static int SAMPLE_SPEC_VALUE_TN_ID1 = 116; protected final static int SAMPLE_SPEC_VALUE_TN_ID2 = 943; protected final static int SAMPLE_SPEC_VALUE_TN_ID3 = 234; protected final static int SAMPLE_SPEC_VALUE_TN_ID4 = 38793; protected final static String SAMPLE_DOC_JSON_SPEC = "{\n" +" \"Image\" : {\n" +" \"Width\" : "+SAMPLE_SPEC_VALUE_WIDTH+",\n" +" \"Height\" : "+SAMPLE_SPEC_VALUE_HEIGHT+"," +"\"Title\" : \""+SAMPLE_SPEC_VALUE_TITLE+"\",\n" +" \"Thumbnail\" : {\n" +" \"Url\" : \""+SAMPLE_SPEC_VALUE_TN_URL+"\",\n" +"\"Height\" : "+SAMPLE_SPEC_VALUE_TN_HEIGHT+",\n" +" \"Width\" : \""+SAMPLE_SPEC_VALUE_TN_WIDTH+"\"\n" +" },\n" +" \"IDs\" : ["+SAMPLE_SPEC_VALUE_TN_ID1+","+SAMPLE_SPEC_VALUE_TN_ID2+","+SAMPLE_SPEC_VALUE_TN_ID3+","+SAMPLE_SPEC_VALUE_TN_ID4+"]\n" +" }" +"}" ; /* /********************************************************** /* Helper classes (beans) /********************************************************** */ /** * Sample class from Jackson tutorial ("JacksonInFiveMinutes") */ protected static class FiveMinuteUser { public enum Gender { MALE, FEMALE }; public static class Name { private String _first, _last; public Name() { } public Name(String f, String l) { _first = f; _last = l; } public String getFirst() { return _first; } public String getLast() { return _last; } public void setFirst(String s) { _first = s; } public void setLast(String s) { _last = s; } @Override public boolean equals(Object o) { if (o == this) return true; if (o == null || o.getClass() != getClass()) return false; Name other = (Name) o; return _first.equals(other._first) && _last.equals(other._last); } } private Gender _gender; private Name _name; private boolean _isVerified; private byte[] _userImage; public FiveMinuteUser() { } public FiveMinuteUser(String first, String last, boolean verified, Gender g, byte[] data) { _name = new Name(first, last); _isVerified = verified; _gender = g; _userImage = data; } public Name getName() { return _name; } public boolean isVerified() { return _isVerified; } public Gender getGender() { return _gender; } public byte[] getUserImage() { return _userImage; } public void setName(Name n) { _name = n; } public void setVerified(boolean b) { _isVerified = b; } public void setGender(Gender g) { _gender = g; } public void setUserImage(byte[] b) { _userImage = b; } @Override public boolean equals(Object o) { if (o == this) return true; if (o == null || o.getClass() != getClass()) return false; FiveMinuteUser other = (FiveMinuteUser) o; if (_isVerified != other._isVerified) return false; if (_gender != other._gender) return false; if (!_name.equals(other._name)) return false; byte[] otherImage = other._userImage; if (otherImage.length != _userImage.length) return false; for (int i = 0, len = _userImage.length; i < len; ++i) { if (_userImage[i] != otherImage[i]) { return false; } } return true; } } protected final JsonFactory JSON_FACTORY = new JsonFactory(); /* /********************************************************** /* High-level helpers /********************************************************** */ protected void verifyJsonSpecSampleDoc(JsonParser p, boolean verifyContents) throws IOException { verifyJsonSpecSampleDoc(p, verifyContents, true); } protected void verifyJsonSpecSampleDoc(JsonParser p, boolean verifyContents, boolean requireNumbers) throws IOException { if (!p.hasCurrentToken()) { p.nextToken(); } assertToken(JsonToken.START_OBJECT, p.currentToken()); // main object assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Image' if (verifyContents) { verifyFieldName(p, "Image"); } assertToken(JsonToken.START_OBJECT, p.nextToken()); // 'image' object assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Width' if (verifyContents) { verifyFieldName(p, "Width"); } verifyIntToken(p.nextToken(), requireNumbers); if (verifyContents) { verifyIntValue(p, SAMPLE_SPEC_VALUE_WIDTH); } assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Height' if (verifyContents) { verifyFieldName(p, "Height"); } verifyIntToken(p.nextToken(), requireNumbers); if (verifyContents) { verifyIntValue(p, SAMPLE_SPEC_VALUE_HEIGHT); } assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Title' if (verifyContents) { verifyFieldName(p, "Title"); } assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals(SAMPLE_SPEC_VALUE_TITLE, getAndVerifyText(p)); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Thumbnail' if (verifyContents) { verifyFieldName(p, "Thumbnail"); } assertToken(JsonToken.START_OBJECT, p.nextToken()); // 'thumbnail' object assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Url' if (verifyContents) { verifyFieldName(p, "Url"); } assertToken(JsonToken.VALUE_STRING, p.nextToken()); if (verifyContents) { assertEquals(SAMPLE_SPEC_VALUE_TN_URL, getAndVerifyText(p)); } assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Height' if (verifyContents) { verifyFieldName(p, "Height"); } verifyIntToken(p.nextToken(), requireNumbers); if (verifyContents) { verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_HEIGHT); } assertToken(JsonToken.FIELD_NAME, p.nextToken()); // 'Width' if (verifyContents) { verifyFieldName(p, "Width"); } // Width value is actually a String in the example assertToken(JsonToken.VALUE_STRING, p.nextToken()); if (verifyContents) { assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, getAndVerifyText(p)); } 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(p, SAMPLE_SPEC_VALUE_TN_ID1); } verifyIntToken(p.nextToken(), requireNumbers); // ids[1] if (verifyContents) { verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_ID2); } verifyIntToken(p.nextToken(), requireNumbers); // ids[2] if (verifyContents) { verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_ID3); } verifyIntToken(p.nextToken(), requireNumbers); // ids[3] if (verifyContents) { verifyIntValue(p, SAMPLE_SPEC_VALUE_TN_ID4); } assertToken(JsonToken.END_ARRAY, p.nextToken()); // 'ids' array assertToken(JsonToken.END_OBJECT, p.nextToken()); // 'image' object assertToken(JsonToken.END_OBJECT, p.nextToken()); // main object } private void verifyIntToken(JsonToken t, boolean requireNumbers) { if (t == JsonToken.VALUE_NUMBER_INT) { return; } if (requireNumbers) { // to get error assertToken(JsonToken.VALUE_NUMBER_INT, t); } // if not number, must be String if (t != JsonToken.VALUE_STRING) { fail("Expected INT or STRING value, got "+t); } } protected void verifyFieldName(JsonParser p, String expName) throws IOException { assertEquals(expName, p.getText()); assertEquals(expName, p.getCurrentName()); } protected void verifyIntValue(JsonParser p, long expValue) throws IOException { // First, via textual assertEquals(String.valueOf(expValue), p.getText()); } /* /********************************************************** /* Parser construction /********************************************************** */ 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 { return f.createParser(new StringReader(input)); } protected JsonParser createParserUsingStream(String input, String encoding) throws IOException { return createParserUsingStream(new JsonFactory(), input, encoding); } protected JsonParser createParserUsingStream(JsonFactory f, String input, String encoding) throws IOException { /* 23-Apr-2008, tatus: UTF-32 is not supported by JDK, have to * use our own codec too (which is not optimal since there's * a chance both encoder and decoder might have bugs, but ones * that cancel each other out or such) */ byte[] data; if (encoding.equalsIgnoreCase("UTF-32")) { data = encodeInUTF32BE(input); } else { data = input.getBytes(encoding); } InputStream is = new ByteArrayInputStream(data); return f.createParser(is); } protected JsonParser createParserForDataInput(JsonFactory f, DataInput input) throws IOException { return f.createParser(input); } /* /********************************************************** /* Generator construction /********************************************************** */ protected JsonGenerator createGenerator(OutputStream out) throws IOException { return createGenerator(JSON_FACTORY, out); } protected JsonGenerator createGenerator(TokenStreamFactory f, OutputStream out) throws IOException { return f.createGenerator(out); } protected JsonGenerator createGenerator(Writer w) throws IOException { return createGenerator(JSON_FACTORY, w); } protected JsonGenerator createGenerator(TokenStreamFactory f, Writer w) throws IOException { return f.createGenerator(w); } /* /********************************************************** /* Helper read/write methods /********************************************************** */ protected void writeJsonDoc(JsonFactory f, String doc, Writer w) throws IOException { writeJsonDoc(f, doc, f.createGenerator(w)); } protected void writeJsonDoc(JsonFactory f, String doc, JsonGenerator g) throws IOException { JsonParser p = f.createParser(aposToQuotes(doc)); while (p.nextToken() != null) { g.copyCurrentStructure(p); } p.close(); g.close(); } protected String readAndWrite(JsonFactory f, JsonParser p) throws IOException { StringWriter sw = new StringWriter(100); JsonGenerator g = f.createGenerator(sw); g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); try { while (p.nextToken() != null) { g.copyCurrentEvent(p); } } catch (IOException e) { g.flush(); fail("Unexpected problem during `readAndWrite`. Output so far: '"+sw+"'; problem: "+e); } p.close(); g.close(); return sw.toString(); } /* /********************************************************** /* Additional assertion methods /********************************************************** */ protected void assertToken(JsonToken expToken, JsonToken actToken) { if (actToken != expToken) { fail("Expected token "+expToken+", current token "+actToken); } } protected void assertToken(JsonToken expToken, JsonParser p) { assertToken(expToken, p.currentToken()); } protected void assertType(Object ob, Class expType) { if (ob == null) { fail("Expected an object of type "+expType.getName()+", got null"); } Class cls = ob.getClass(); if (!expType.isAssignableFrom(cls)) { fail("Expected type "+expType.getName()+", got "+cls.getName()); } } protected void verifyException(Throwable e, String... matches) { String msg = e.getMessage(); String lmsg = (msg == null) ? "" : msg.toLowerCase(); for (String match : matches) { String lmatch = match.toLowerCase(); if (lmsg.indexOf(lmatch) >= 0) { return; } } fail("Expected an exception with one of substrings ("+Arrays.asList(matches)+"): got one with message \""+msg+"\""); } /** * Method that gets textual contents of the current token using * available methods, and ensures results are consistent, before * returning them */ protected String getAndVerifyText(JsonParser p) throws IOException { // Ok, let's verify other accessors 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 (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); return str; } /* /********************************************************** /* And other helpers /********************************************************** */ protected static String quote(String str) { return '"'+str+'"'; } protected static String aposToQuotes(String json) { return json.replace("'", "\""); } protected byte[] encodeInUTF32BE(String input) { int len = input.length(); byte[] result = new byte[len * 4]; int ptr = 0; for (int i = 0; i < len; ++i, ptr += 4) { char c = input.charAt(i); result[ptr] = result[ptr+1] = (byte) 0; result[ptr+2] = (byte) (c >> 8); result[ptr+3] = (byte) c; } return result; } // @since 2.9.7 protected static byte[] utf8Bytes(String str) { try { return str.getBytes("UTF-8"); } catch (IOException e) { throw new RuntimeException(e); } } protected void fieldNameFor(StringBuilder sb, int index) { /* let's do something like "f1.1" to exercise different * field names (important for byte-based codec) * Other name shuffling done mostly just for fun... :) */ sb.append(FIELD_BASENAME); sb.append(index); if (index > 50) { sb.append('.'); if (index > 200) { sb.append(index); if (index > 4000) { // and some even longer symbols... sb.append(".").append(index); } } else { sb.append(index >> 3); // divide by 8 } } } // @since 2.9.7 protected JsonFactory sharedStreamFactory() { return JSON_FACTORY; } // @since 2.9.7 protected JsonFactory newStreamFactory() { return new JsonFactory(); } // @since 2.9.8 protected JsonFactoryBuilder streamFactoryBuilder() { return (JsonFactoryBuilder) JsonFactory.builder(); } protected String fieldNameFor(int index) { StringBuilder sb = new StringBuilder(16); fieldNameFor(sb, index); return sb.toString(); } protected int[] calcQuads(byte[] wordBytes) { int blen = wordBytes.length; int[] result = new int[(blen + 3) / 4]; for (int i = 0; i < blen; ++i) { int x = wordBytes[i] & 0xFF; if (++i < blen) { x = (x << 8) | (wordBytes[i] & 0xFF); if (++i < blen) { x = (x << 8) | (wordBytes[i] & 0xFF); if (++i < blen) { x = (x << 8) | (wordBytes[i] & 0xFF); } } } result[i >> 2] = x; } return result; } } JsonpCharacterEscapesTest.java000066400000000000000000000015001356164247300342240ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/corepackage com.fasterxml.jackson.core; import com.fasterxml.jackson.core.io.SerializedString; import org.junit.Test; import static org.junit.Assert.assertEquals; /** * Unit tests for class {@link JsonpCharacterEscapes}. * * @date 2017-07-20 * @see JsonpCharacterEscapes * **/ public class JsonpCharacterEscapesTest{ @Test public void testGetEscapeSequenceOne() { JsonpCharacterEscapes jsonpCharacterEscapes = JsonpCharacterEscapes.instance(); assertEquals(new SerializedString("\\u2028"),jsonpCharacterEscapes.getEscapeSequence(0x2028)); } @Test public void testGetEscapeSequenceTwo() { JsonpCharacterEscapes jsonpCharacterEscapes = JsonpCharacterEscapes.instance(); assertEquals(new SerializedString("\\u2029"),jsonpCharacterEscapes.getEscapeSequence(0x2029)); } }PointerFromContextTest.java000066400000000000000000000307631356164247300336400ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/corepackage com.fasterxml.jackson.core; import java.io.StringWriter; public class PointerFromContextTest extends BaseTest { /* /********************************************************** /* Test methods, basic /********************************************************** */ private final JsonFactory JSON_F = new JsonFactory(); public void testViaParser() throws Exception { final String SIMPLE = aposToQuotes("{'a':123,'array':[1,2,[3],5,{'obInArray':4}]," +"'ob':{'first':[false,true],'second':{'sub':37}},'b':true}"); JsonParser p = JSON_F.createParser(SIMPLE); // by default should just get "empty" assertSame(JsonPointer.EMPTY, p.getParsingContext().pathAsPointer()); // let's just traverse, then: assertToken(JsonToken.START_OBJECT, p.nextToken()); assertSame(JsonPointer.EMPTY, p.getParsingContext().pathAsPointer()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // a assertEquals("/a", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals("/a", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // array assertEquals("/array", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertEquals("/array", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 1 assertEquals("/array/0", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 2 assertEquals("/array/1", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertEquals("/array/2", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 3 assertEquals("/array/2/0", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEquals("/array/2", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 5 assertEquals("/array/3", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertEquals("/array/4", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // obInArray assertEquals("/array/4/obInArray", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 4 assertEquals("/array/4/obInArray", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertEquals("/array/4", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.END_ARRAY, p.nextToken()); // /array assertEquals("/array", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // ob assertEquals("/ob", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertEquals("/ob", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // first assertEquals("/ob/first", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertEquals("/ob/first", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_FALSE, p.nextToken()); assertEquals("/ob/first/0", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertEquals("/ob/first/1", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEquals("/ob/first", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // second assertEquals("/ob/second", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertEquals("/ob/second", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // sub assertEquals("/ob/second/sub", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 37 assertEquals("/ob/second/sub", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertEquals("/ob/second", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.END_OBJECT, p.nextToken()); // /ob assertEquals("/ob", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // b assertEquals("/b", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertEquals("/b", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertSame(JsonPointer.EMPTY, p.getParsingContext().pathAsPointer()); assertNull(p.nextToken()); p.close(); } public void testViaGenerator() throws Exception { StringWriter w = new StringWriter(); JsonGenerator g = JSON_F.createGenerator(w); assertSame(JsonPointer.EMPTY, g.getOutputContext().pathAsPointer()); g.writeStartArray(); // no path yet assertSame(JsonPointer.EMPTY, g.getOutputContext().pathAsPointer()); g.writeBoolean(true); assertEquals("/0", g.getOutputContext().pathAsPointer().toString()); g.writeStartObject(); assertEquals("/1", g.getOutputContext().pathAsPointer().toString()); g.writeFieldName("x"); assertEquals("/1/x", g.getOutputContext().pathAsPointer().toString()); g.writeString("foo"); assertEquals("/1/x", g.getOutputContext().pathAsPointer().toString()); g.writeFieldName("stats"); assertEquals("/1/stats", g.getOutputContext().pathAsPointer().toString()); g.writeStartObject(); assertEquals("/1/stats", g.getOutputContext().pathAsPointer().toString()); g.writeFieldName("rate"); assertEquals("/1/stats/rate", g.getOutputContext().pathAsPointer().toString()); g.writeNumber(13); assertEquals("/1/stats/rate", g.getOutputContext().pathAsPointer().toString()); g.writeEndObject(); assertEquals("/1/stats", g.getOutputContext().pathAsPointer().toString()); g.writeEndObject(); assertEquals("/1", g.getOutputContext().pathAsPointer().toString()); g.writeEndArray(); assertSame(JsonPointer.EMPTY, g.getOutputContext().pathAsPointer()); g.close(); w.close(); } /* /********************************************************** /* Test methods, root-offset /********************************************************** */ public void testParserWithRoot() throws Exception { final String JSON = aposToQuotes("{'a':1,'b':3}\n" +"{'a':5,'c':[1,2]}\n[1,2]\n"); JsonParser p = JSON_F.createParser(JSON); // before pointing to anything, we have no path to point to assertSame(JsonPointer.EMPTY, p.getParsingContext().pathAsPointer(true)); // but immediately after advancing we do assertToken(JsonToken.START_OBJECT, p.nextToken()); assertEquals("/0", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // a assertEquals("/0/a", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // a:1 assertEquals("/0/a", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // b assertEquals("/0/b", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // a:1 assertEquals("/0/b", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertEquals("/0", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertEquals("/1", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // a assertEquals("/1/a", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // a:1 assertEquals("/1/a", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // c assertEquals("/1/c", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertEquals("/1/c", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals("/1/c/0", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals("/1/c/1", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEquals("/1/c", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertEquals("/1", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertEquals("/2", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals("/2/0", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals("/2/1", p.getParsingContext().pathAsPointer(true).toString()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEquals("/2", p.getParsingContext().pathAsPointer(true).toString()); assertNull(p.nextToken()); // 21-Mar-2017, tatu: This is not entirely satisfactory: ideally should get // EMPTY here as well. But context doesn't really get reset at the end // and it's not 100% clear what is the best path forward. So, for now... // just verify current sub-optimal behavior assertEquals("/2", p.getParsingContext().pathAsPointer(true).toString()); p.close(); } public void testGeneratorWithRoot() throws Exception { StringWriter w = new StringWriter(); JsonGenerator g = JSON_F.createGenerator(w); assertSame(JsonPointer.EMPTY, g.getOutputContext().pathAsPointer(true)); g.writeStartArray(); assertEquals("/0", g.getOutputContext().pathAsPointer(true).toString()); g.writeBoolean(true); assertEquals("/0/0", g.getOutputContext().pathAsPointer(true).toString()); g.writeStartObject(); assertEquals("/0/1", g.getOutputContext().pathAsPointer(true).toString()); g.writeFieldName("x"); assertEquals("/0/1/x", g.getOutputContext().pathAsPointer(true).toString()); g.writeString("foo"); assertEquals("/0/1/x", g.getOutputContext().pathAsPointer(true).toString()); g.writeEndObject(); assertEquals("/0/1", g.getOutputContext().pathAsPointer(true).toString()); g.writeEndArray(); assertEquals("/0", g.getOutputContext().pathAsPointer(true).toString()); g.writeBoolean(true); assertEquals("/1", g.getOutputContext().pathAsPointer(true).toString()); g.writeStartArray(); assertEquals("/2", g.getOutputContext().pathAsPointer(true).toString()); g.writeString("foo"); assertEquals("/2/0", g.getOutputContext().pathAsPointer(true).toString()); g.writeString("bar"); assertEquals("/2/1", g.getOutputContext().pathAsPointer(true).toString()); g.writeEndArray(); assertEquals("/2", g.getOutputContext().pathAsPointer(true).toString()); // as earlier, not optimal result, but verify it's stable: assertEquals("/2", g.getOutputContext().pathAsPointer(true).toString()); g.close(); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/TestExceptions.java000066400000000000000000000077371356164247300322340ustar00rootroot00000000000000package com.fasterxml.jackson.core; import java.io.StringWriter; import com.fasterxml.jackson.core.io.JsonEOFException; public class TestExceptions extends BaseTest { private final JsonFactory JSON_F = new JsonFactory(); // For [core#10] public void testOriginalMesssage() { JsonProcessingException exc = new JsonParseException(null, "Foobar", JsonLocation.NA); String msg = exc.getMessage(); String orig = exc.getOriginalMessage(); assertEquals("Foobar", orig); assertTrue(msg.length() > orig.length()); // and another JsonProcessingException exc2 = new JsonProcessingException("Second", JsonLocation.NA, exc); assertSame(exc, exc2.getCause()); exc2.clearLocation(); assertNull(exc2.getLocation()); // and yet with null JsonProcessingException exc3 = new JsonProcessingException(exc); assertNull(exc3.getOriginalMessage()); assertEquals("N/A", exc3.getMessage()); assertEquals("com.fasterxml.jackson.core.JsonProcessingException: N/A", exc3.toString()); } // [core#198] public void testAccessToParser() throws Exception { JsonParser p = JSON_F.createParser("{}"); assertToken(JsonToken.START_OBJECT, p.nextToken()); JsonParseException e = new JsonParseException(p, "Test!"); assertSame(p, e.getProcessor()); assertEquals("Test!", e.getOriginalMessage()); JsonLocation loc = e.getLocation(); assertNotNull(loc); assertEquals(2, loc.getColumnNr()); assertEquals(1, loc.getLineNr()); p.close(); } // [core#198] public void testAccessToGenerator() throws Exception { StringWriter w = new StringWriter(); JsonGenerator g = JSON_F.createGenerator(w); g.writeStartObject(); JsonGenerationException e = new JsonGenerationException("Test!", g); assertSame(g, e.getProcessor()); 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? } } TestJDKSerializability.java000066400000000000000000000117171356164247300335160ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/corepackage com.fasterxml.jackson.core; import java.io.*; import com.fasterxml.jackson.core.Base64Variant; import com.fasterxml.jackson.core.Base64Variants; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.PrettyPrinter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; /** * Unit tests for [core#31] (https://github.com/FasterXML/jackson-core/issues/31) */ public class TestJDKSerializability extends BaseTest { public void testJsonFactorySerializable() throws Exception { JsonFactory f = new JsonFactory(); String origJson = "{\"simple\":[1,true,{}]}"; assertEquals(origJson, _copyJson(f, origJson, false)); // Ok: freeze dry factory, thaw, and try to use again: byte[] frozen = jdkSerialize(f); JsonFactory f2 = jdkDeserialize(frozen); assertNotNull(f2); assertEquals(origJson, _copyJson(f2, origJson, false)); // Let's also try byte-based variant, for fun... assertEquals(origJson, _copyJson(f2, origJson, true)); } public void testBase64Variant() throws Exception { Base64Variant orig = Base64Variants.PEM; byte[] stuff = jdkSerialize(orig); Base64Variant back = jdkDeserialize(stuff); assertSame(orig, back); } public void testPrettyPrinter() throws Exception { PrettyPrinter p = new DefaultPrettyPrinter(); byte[] stuff = jdkSerialize(p); PrettyPrinter back = jdkDeserialize(stuff); // what should we test? assertNotNull(back); } public void testLocation() throws Exception { JsonFactory jf = new JsonFactory(); JsonParser jp = jf.createParser(" { }"); assertToken(JsonToken.START_OBJECT, jp.nextToken()); JsonLocation loc = jp.getCurrentLocation(); byte[] stuff = jdkSerialize(loc); JsonLocation loc2 = jdkDeserialize(stuff); assertNotNull(loc2); assertEquals(loc.getLineNr(), loc2.getLineNr()); assertEquals(loc.getColumnNr(), loc2.getColumnNr()); jp.close(); } public void testParseException() throws Exception { JsonFactory jf = new JsonFactory(); JsonParser p = jf.createParser(" { garbage! }"); JsonParseException exc = null; try { p.nextToken(); p.nextToken(); fail("Should not get here"); } catch (JsonParseException e) { exc = e; } p.close(); byte[] stuff = jdkSerialize(exc); JsonParseException result = jdkDeserialize(stuff); assertNotNull(result); } public void testGenerationException() throws Exception { JsonFactory jf = new JsonFactory(); JsonGenerator g = jf.createGenerator(new ByteArrayOutputStream()); JsonGenerationException exc = null; g.writeStartObject(); try { g.writeNumber(4); fail("Should not get here"); } catch (JsonGenerationException e) { exc = e; } g.close(); byte[] stuff = jdkSerialize(exc); JsonGenerationException result = jdkDeserialize(stuff); assertNotNull(result); } /* /********************************************************** /* Helper methods /********************************************************** */ protected byte[] jdkSerialize(Object o) throws IOException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(1000); ObjectOutputStream obOut = new ObjectOutputStream(bytes); obOut.writeObject(o); obOut.close(); return bytes.toByteArray(); } @SuppressWarnings("unchecked") protected T jdkDeserialize(byte[] raw) throws IOException { ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(raw)); try { return (T) objIn.readObject(); } catch (ClassNotFoundException e) { fail("Missing class: "+e.getMessage()); return null; } finally { objIn.close(); } } @SuppressWarnings("resource") protected String _copyJson(JsonFactory f, String json, boolean useBytes) throws IOException { if (useBytes) { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); JsonGenerator jg = f.createGenerator(bytes); _copyJson(f, json, jg); return bytes.toString("UTF-8"); } StringWriter sw = new StringWriter(); JsonGenerator jg = f.createGenerator(sw); _copyJson(f, json, jg); return sw.toString(); } protected void _copyJson(JsonFactory f, String json, JsonGenerator g) throws IOException { JsonParser p = f.createParser(json); while (p.nextToken() != null) { g.copyCurrentEvent(p); } p.close(); g.close(); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/TestJsonPointer.java000066400000000000000000000153321356164247300323530ustar00rootroot00000000000000package com.fasterxml.jackson.core; public class TestJsonPointer extends BaseTest { public void testSimplePath() throws Exception { final String INPUT = "/Image/15/name"; JsonPointer ptr = JsonPointer.compile(INPUT); assertFalse(ptr.matches()); assertEquals(-1, ptr.getMatchingIndex()); assertEquals("Image", ptr.getMatchingProperty()); assertEquals("/Image/15", ptr.head().toString()); assertEquals(INPUT, ptr.toString()); ptr = ptr.tail(); assertNotNull(ptr); assertFalse(ptr.matches()); assertEquals(15, ptr.getMatchingIndex()); assertEquals("15", ptr.getMatchingProperty()); assertEquals("/15/name", ptr.toString()); assertEquals("/15", ptr.head().toString()); assertEquals("", ptr.head().head().toString()); assertNull(ptr.head().head().head()); ptr = ptr.tail(); assertNotNull(ptr); assertFalse(ptr.matches()); assertEquals(-1, ptr.getMatchingIndex()); assertEquals("name", ptr.getMatchingProperty()); assertEquals("/name", ptr.toString()); assertEquals("", ptr.head().toString()); assertSame(JsonPointer.EMPTY, ptr.head()); // done! ptr = ptr.tail(); assertTrue(ptr.matches()); assertNull(ptr.tail()); assertNull(ptr.head()); assertEquals("", ptr.getMatchingProperty()); assertEquals(-1, ptr.getMatchingIndex()); } public void testSimplePathLonger() throws Exception { final String INPUT = "/a/b/c/d/e/f/0"; JsonPointer ptr = JsonPointer.compile(INPUT); assertFalse(ptr.matches()); assertEquals(-1, ptr.getMatchingIndex()); assertEquals("a", ptr.getMatchingProperty()); assertEquals("/a/b/c/d/e/f", ptr.head().toString()); assertEquals("/b/c/d/e/f/0", ptr.tail().toString()); assertEquals("/0", ptr.last().toString()); assertEquals(INPUT, ptr.toString()); } public void testWonkyNumber173() throws Exception { JsonPointer ptr = JsonPointer.compile("/1e0"); assertFalse(ptr.matches()); } // [core#176]: do not allow leading zeroes public void testIZeroIndex() throws Exception { JsonPointer ptr = JsonPointer.compile("/0"); assertEquals(0, ptr.getMatchingIndex()); ptr = JsonPointer.compile("/00"); assertEquals(-1, ptr.getMatchingIndex()); } public void testLast() { final String INPUT = "/Image/15/name"; JsonPointer ptr = JsonPointer.compile(INPUT); JsonPointer leaf = ptr.last(); assertEquals("name", leaf.getMatchingProperty()); } public void testEmpty() { assertSame(JsonPointer.EMPTY, JsonPointer.empty()); assertSame(JsonPointer.EMPTY, JsonPointer.compile("")); } public void testEmptyName() { // note: this is acceptable, to match property in '{"":3}', for example // and NOT same as what empty point, "", is. JsonPointer ptr = JsonPointer.compile("/"); assertNotNull(ptr); assertNotSame(JsonPointer.EMPTY, ptr); assertEquals("/", ptr.toString()); } // mostly for test coverage, really... public void testEquality() { assertFalse(JsonPointer.empty().equals(JsonPointer.compile("/"))); assertEquals(JsonPointer.compile("/foo/3"), JsonPointer.compile("/foo/3")); assertFalse(JsonPointer.empty().equals(JsonPointer.compile("/12"))); assertFalse(JsonPointer.compile("/12").equals(JsonPointer.empty())); // expr != String assertFalse(JsonPointer.empty().equals("/")); } public void testProperties() { assertTrue(JsonPointer.compile("/foo").mayMatchProperty()); assertFalse(JsonPointer.compile("/foo").mayMatchElement()); assertTrue(JsonPointer.compile("/12").mayMatchElement()); // Interestingly enough, since Json Pointer is just String, could // ALSO match property with name "12" assertTrue(JsonPointer.compile("/12").mayMatchProperty()); } public void testAppend() { final String INPUT = "/Image/15/name"; final String APPEND = "/extension"; JsonPointer ptr = JsonPointer.compile(INPUT); JsonPointer apd = JsonPointer.compile(APPEND); JsonPointer appended = ptr.append(apd); assertEquals("extension", appended.last().getMatchingProperty()); } public void testAppendWithFinalSlash() { final String INPUT = "/Image/15/name/"; final String APPEND = "/extension"; JsonPointer ptr = JsonPointer.compile(INPUT); JsonPointer apd = JsonPointer.compile(APPEND); JsonPointer appended = ptr.append(apd); assertEquals("extension", appended.last().getMatchingProperty()); } public void testQuotedPath() throws Exception { final String INPUT = "/w~1out/til~0de/a~1b"; JsonPointer ptr = JsonPointer.compile(INPUT); assertFalse(ptr.matches()); assertEquals(-1, ptr.getMatchingIndex()); assertEquals("w/out", ptr.getMatchingProperty()); assertEquals("/w~1out/til~0de", ptr.head().toString()); assertEquals(INPUT, ptr.toString()); ptr = ptr.tail(); assertNotNull(ptr); assertFalse(ptr.matches()); assertEquals(-1, ptr.getMatchingIndex()); assertEquals("til~de", ptr.getMatchingProperty()); assertEquals("/til~0de", ptr.head().toString()); assertEquals("/til~0de/a~1b", ptr.toString()); ptr = ptr.tail(); assertNotNull(ptr); assertFalse(ptr.matches()); assertEquals(-1, ptr.getMatchingIndex()); assertEquals("a/b", ptr.getMatchingProperty()); assertEquals("/a~1b", ptr.toString()); assertEquals("", ptr.head().toString()); // done! ptr = ptr.tail(); assertTrue(ptr.matches()); assertNull(ptr.tail()); } // [core#133] public void testLongNumbers() throws Exception { final long LONG_ID = ((long) Integer.MAX_VALUE) + 1L; final String INPUT = "/User/"+LONG_ID; JsonPointer ptr = JsonPointer.compile(INPUT); assertEquals("User", ptr.getMatchingProperty()); assertEquals(INPUT, ptr.toString()); ptr = ptr.tail(); assertNotNull(ptr); assertFalse(ptr.matches()); /* 14-Mar-2014, tatu: We do not support array indexes beyond 32-bit * range; can still match textually of course. */ assertEquals(-1, ptr.getMatchingIndex()); assertEquals(String.valueOf(LONG_ID), ptr.getMatchingProperty()); // done! ptr = ptr.tail(); assertTrue(ptr.matches()); assertNull(ptr.tail()); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/TestLocation.java000066400000000000000000000077511356164247300316570ustar00rootroot00000000000000package com.fasterxml.jackson.core; import java.io.ByteArrayInputStream; import java.io.InputStream; public class TestLocation extends BaseTest { static class Foobar { } public void testBasics() { JsonLocation loc1 = new JsonLocation("src", 10L, 10L, 1, 2); JsonLocation loc2 = new JsonLocation(null, 10L, 10L, 3, 2); assertEquals(loc1, loc1); assertFalse(loc1.equals(null)); assertFalse(loc1.equals(loc2)); assertFalse(loc2.equals(loc1)); // don't care about what it is; should not compute to 0 with data above assertTrue(loc1.hashCode() != 0); assertTrue(loc2.hashCode() != 0); } public void testBasicToString() throws Exception { // no location: assertEquals("[Source: UNKNOWN; line: 3, column: 2]", new JsonLocation(null, 10L, 10L, 3, 2).toString()); // Short String assertEquals("[Source: (String)\"string-source\"; line: 1, column: 2]", new JsonLocation("string-source", 10L, 10L, 1, 2).toString()); // Short char[] assertEquals("[Source: (char[])\"chars-source\"; line: 1, column: 2]", new JsonLocation("chars-source".toCharArray(), 10L, 10L, 1, 2).toString()); // Short byte[] assertEquals("[Source: (byte[])\"bytes-source\"; line: 1, column: 2]", new JsonLocation("bytes-source".getBytes("UTF-8"), 10L, 10L, 1, 2).toString()); // InputStream assertEquals("[Source: (ByteArrayInputStream); line: 1, column: 2]", new JsonLocation(new ByteArrayInputStream(new byte[0]), 10L, 10L, 1, 2).toString()); // Class that specifies source type assertEquals("[Source: (InputStream); line: 1, column: 2]", new JsonLocation(InputStream.class, 10L, 10L, 1, 2).toString()); // misc other Foobar srcRef = new Foobar(); assertEquals("[Source: ("+srcRef.getClass().getName()+"); line: 1, column: 2]", new JsonLocation(srcRef, 10L, 10L, 1, 2).toString()); } public void testTruncatedSource() throws Exception { StringBuilder sb = new StringBuilder(); for (int i = 0; i < JsonLocation.MAX_CONTENT_SNIPPET; ++i) { sb.append("x"); } String main = sb.toString(); String json = main + "yyy"; JsonLocation loc = new JsonLocation(json, 0L, 0L, 1, 1); String desc = loc.sourceDescription(); assertEquals(String.format("(String)\"%s\"[truncated 3 chars]", main), desc); // and same with bytes loc = new JsonLocation(json.getBytes("UTF-8"), 0L, 0L, 1, 1); desc = loc.sourceDescription(); assertEquals(String.format("(byte[])\"%s\"[truncated 3 bytes]", main), desc); } // for [jackson-core#356] public void testDisableSourceInclusion() throws Exception { JsonFactory f = JsonFactory.builder() .disable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION) .build(); JsonParser p = f.createParser("[ foobar ]"); assertToken(JsonToken.START_ARRAY, p.nextToken()); try { p.nextToken(); fail("Shouldn't have passed"); } catch (JsonParseException e) { verifyException(e, "unrecognized token"); JsonLocation loc = e.getLocation(); assertNull(loc.getSourceRef()); assertEquals("UNKNOWN", loc.sourceDescription()); } p.close(); // and verify same works for byte-based too p = f.createParser("[ foobar ]".getBytes("UTF-8")); assertToken(JsonToken.START_ARRAY, p.nextToken()); try { p.nextToken(); fail("Shouldn't have passed"); } catch (JsonParseException e) { verifyException(e, "unrecognized token"); JsonLocation loc = e.getLocation(); assertNull(loc.getSourceRef()); assertEquals("UNKNOWN", loc.sourceDescription()); } p.close(); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/TestVersions.java000066400000000000000000000032271356164247300317110ustar00rootroot00000000000000package com.fasterxml.jackson.core; import com.fasterxml.jackson.core.json.*; import com.fasterxml.jackson.core.io.IOContext; import com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer; import com.fasterxml.jackson.core.util.BufferRecycler; /** * Tests to verify functioning of {@link Version} class. */ public class TestVersions extends com.fasterxml.jackson.core.BaseTest { public void testCoreVersions() throws Exception { assertVersion(new JsonFactory().version()); ReaderBasedJsonParser jp = new ReaderBasedJsonParser(getIOContext(), 0, null, null, CharsToNameCanonicalizer.createRoot()); assertVersion(jp.version()); jp.close(); JsonGenerator jgen = new WriterBasedJsonGenerator(getIOContext(), 0, null, null, '"'); assertVersion(jgen.version()); jgen.close(); } public void testMisc() { Version unk = Version.unknownVersion(); assertEquals("0.0.0", unk.toString()); assertEquals("//0.0.0", unk.toFullString()); assertTrue(unk.equals(unk)); Version other = new Version(2, 8, 4, "", "groupId", "artifactId"); assertEquals("2.8.4", other.toString()); assertEquals("groupId/artifactId/2.8.4", other.toFullString()); } /* /********************************************************** /* Helper methods /********************************************************** */ private void assertVersion(Version v) { assertEquals(PackageVersion.VERSION, v); } private IOContext getIOContext() { return new IOContext(new BufferRecycler(), null, false); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/VersionTest.java000066400000000000000000000026271356164247300315310ustar00rootroot00000000000000package com.fasterxml.jackson.core; import org.junit.Test; import static org.junit.Assert.*; /** * Unit tests for class {@link Version}. * **/ public class VersionTest{ @Test public void testCompareToOne() { Version version = Version.unknownVersion(); Version versionTwo = new Version(0, (-263), (-1820), "", "", ""); assertEquals(263, version.compareTo(versionTwo)); } @Test public void testCompareToReturningZero() { Version version = Version.unknownVersion(); Version versionTwo = new Version(0, 0, 0, "", "", ""); assertEquals(0, version.compareTo(versionTwo)); } @Test public void testCreatesVersionTaking6ArgumentsAndCallsCompareTo() { Version version = new Version(0, 0, 0, null, null, ""); Version versionTwo = new Version(0, 0, 0, "", "", "//0.0.0"); assertTrue(version.compareTo(versionTwo) < 0); } @Test public void testCompareToTwo() { Version version = Version.unknownVersion(); Version versionTwo = new Version((-1), 0, 0, "0.0.0", "", ""); assertTrue(version.compareTo(versionTwo) > 0); } @Test public void testCompareToAndCreatesVersionTaking6ArgumentsAndUnknownVersion() { Version version = Version.unknownVersion(); Version versionTwo = new Version(0, 0, 0, "//0.0.0", "//0.0.0", ""); assertTrue(version.compareTo(versionTwo) < 0); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/async/000077500000000000000000000000001356164247300275075ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/async/AsyncTestBase.java000066400000000000000000000025011356164247300330600ustar00rootroot00000000000000package com.fasterxml.jackson.core.async; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapperForByteArray; public abstract class AsyncTestBase extends BaseTest { final static String SPACES = " "; protected final static char UNICODE_2BYTES = (char) 167; // law symbol protected final static char UNICODE_3BYTES = (char) 0x4567; protected final static String UNICODE_SEGMENT = "["+UNICODE_2BYTES+"/"+UNICODE_3BYTES+"]"; protected AsyncReaderWrapper asyncForBytes(JsonFactory f, int bytesPerRead, byte[] bytes, int padding) throws IOException { return new AsyncReaderWrapperForByteArray(f.createNonBlockingByteArrayParser(), bytesPerRead, bytes, padding); } protected static String spaces(int count) { return SPACES.substring(0, Math.min(SPACES.length(), count)); } protected final JsonToken verifyStart(AsyncReaderWrapper reader) throws Exception { assertToken(JsonToken.NOT_AVAILABLE, reader.currentToken()); return reader.nextToken(); } protected final byte[] _jsonDoc(String doc) throws IOException { return doc.getBytes("UTF-8"); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/base64/000077500000000000000000000000001356164247300274565ustar00rootroot00000000000000Base64BinaryParsingTest.java000066400000000000000000000401741356164247300346250ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/base64package com.fasterxml.jackson.core.base64; import static org.junit.Assert.assertArrayEquals; import java.io.*; import org.junit.Assert; import com.fasterxml.jackson.core.*; public class Base64BinaryParsingTest extends com.fasterxml.jackson.core.BaseTest { public void testBase64UsingInputStream() throws Exception { _testBase64Text(MODE_INPUT_STREAM); _testBase64Text(MODE_INPUT_STREAM_THROTTLED); _testBase64Text(MODE_DATA_INPUT); } public void testBase64UsingReader() throws Exception { _testBase64Text(MODE_READER); } public void testStreaming() throws IOException { _testStreaming(MODE_INPUT_STREAM); _testStreaming(MODE_INPUT_STREAM_THROTTLED); _testStreaming(MODE_DATA_INPUT); _testStreaming(MODE_READER); } public void testSimple() throws IOException { for (int mode : ALL_MODES) { // [core#414]: Allow leading/trailign white-space, ensure it is accepted _testSimple(mode, false, false); _testSimple(mode, true, false); _testSimple(mode, false, true); _testSimple(mode, true, true); } } public void testInArray() throws IOException { for (int mode : ALL_MODES) { _testInArray(mode); } } public void testWithEscaped() throws IOException { for (int mode : ALL_MODES) { _testEscaped(mode); } } public void testWithEscapedPadding() throws IOException { for (int mode : ALL_MODES) { _testEscapedPadding(mode); } } public void testInvalidTokenForBase64() throws IOException { for (int mode : ALL_MODES) { // First: illegal padding JsonParser p = createParser(mode, "[ ]"); assertToken(JsonToken.START_ARRAY, p.nextToken()); try { p.getBinaryValue(); fail("Should not pass"); } catch (JsonParseException e) { verifyException(e, "current token"); verifyException(e, "can not access as binary"); } p.close(); } } public void testInvalidChar() throws IOException { for (int mode : ALL_MODES) { // First: illegal padding JsonParser p = createParser(mode, quote("a===")); assertToken(JsonToken.VALUE_STRING, p.nextToken()); try { p.getBinaryValue(Base64Variants.MIME); fail("Should not pass"); } catch (JsonParseException e) { verifyException(e, "padding only legal"); } p.close(); // second: invalid space within p = createParser(mode, quote("ab de")); assertToken(JsonToken.VALUE_STRING, p.nextToken()); try { p.getBinaryValue(Base64Variants.MIME); fail("Should not pass"); } catch (JsonParseException e) { verifyException(e, "illegal white space"); } p.close(); // third: something else p = createParser(mode, quote("ab#?")); assertToken(JsonToken.VALUE_STRING, p.nextToken()); try { p.getBinaryValue(Base64Variants.MIME); fail("Should not pass"); } catch (JsonParseException e) { verifyException(e, "illegal character '#'"); } p.close(); } } public void testOkMissingPadding() throws IOException { final byte[] DOC1 = new byte[] { (byte) 0xAD }; _testOkMissingPadding(DOC1, MODE_INPUT_STREAM); _testOkMissingPadding(DOC1, MODE_INPUT_STREAM_THROTTLED); _testOkMissingPadding(DOC1, MODE_READER); _testOkMissingPadding(DOC1, MODE_DATA_INPUT); final byte[] DOC2 = new byte[] { (byte) 0xAC, (byte) 0xDC }; _testOkMissingPadding(DOC2, MODE_INPUT_STREAM); _testOkMissingPadding(DOC2, MODE_INPUT_STREAM_THROTTLED); _testOkMissingPadding(DOC2, MODE_READER); _testOkMissingPadding(DOC2, MODE_DATA_INPUT); } private void _testOkMissingPadding(byte[] input, int mode) throws IOException { final Base64Variant b64 = Base64Variants.MODIFIED_FOR_URL; final String encoded = b64.encode(input, false); JsonParser p = createParser(mode, quote(encoded)); // 1 byte -> 2 encoded chars; 2 bytes -> 3 encoded chars assertEquals(input.length+1, encoded.length()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); byte[] actual = p.getBinaryValue(b64); Assert.assertArrayEquals(input, actual); p.close(); } public void testFailDueToMissingPadding() throws IOException { final String DOC1 = quote("fQ"); // 1 bytes, no padding _testFailDueToMissingPadding(DOC1, MODE_INPUT_STREAM); _testFailDueToMissingPadding(DOC1, MODE_INPUT_STREAM_THROTTLED); _testFailDueToMissingPadding(DOC1, MODE_READER); _testFailDueToMissingPadding(DOC1, MODE_DATA_INPUT); final String DOC2 = quote("A/A"); // 2 bytes, no padding _testFailDueToMissingPadding(DOC2, MODE_INPUT_STREAM); _testFailDueToMissingPadding(DOC2, MODE_INPUT_STREAM_THROTTLED); _testFailDueToMissingPadding(DOC2, MODE_READER); _testFailDueToMissingPadding(DOC2, MODE_DATA_INPUT); } private void _testFailDueToMissingPadding(String doc, int mode) throws IOException { final String EXP_EXCEPTION_MATCH = "Unexpected end of base64-encoded String: base64 variant 'MIME' expects padding"; // First, without getting text value first: JsonParser p = createParser(mode, doc); assertToken(JsonToken.VALUE_STRING, p.nextToken()); try { /*byte[] b =*/ p.getBinaryValue(Base64Variants.MIME); fail("Should not pass"); } catch (JsonParseException e) { verifyException(e, EXP_EXCEPTION_MATCH); } p.close(); // second, access String first p = createParser(mode, doc); assertToken(JsonToken.VALUE_STRING, p.nextToken()); /*String str =*/ p.getText(); try { /*byte[] b =*/ p.getBinaryValue(Base64Variants.MIME); fail("Should not pass"); } catch (JsonParseException e) { verifyException(e, EXP_EXCEPTION_MATCH); } p.close(); } /* /********************************************************** /* Test helper methods /********************************************************** */ @SuppressWarnings("resource") public void _testBase64Text(int mode) throws Exception { // let's actually iterate over sets of encoding modes, lengths final int[] LENS = { 1, 2, 3, 4, 7, 9, 32, 33, 34, 35 }; final Base64Variant[] VARIANTS = { Base64Variants.MIME, Base64Variants.MIME_NO_LINEFEEDS, Base64Variants.MODIFIED_FOR_URL, Base64Variants.PEM }; JsonFactory jsonFactory = sharedStreamFactory(); final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); StringWriter chars = null; for (int len : LENS) { byte[] input = new byte[len]; for (int i = 0; i < input.length; ++i) { input[i] = (byte) i; } for (Base64Variant variant : VARIANTS) { JsonGenerator g; if (mode == MODE_READER) { chars = new StringWriter(); g = jsonFactory.createGenerator(chars); } else { bytes.reset(); g = jsonFactory.createGenerator(bytes, JsonEncoding.UTF8); } 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 = p.getBinaryValue(variant); } catch (Exception e) { IOException ioException = new IOException("Failed (variant "+variant+", data length "+len+"): "+e.getMessage()); ioException.initCause(e); throw ioException; } assertNotNull(data); assertArrayEquals(data, input); if (mode != MODE_DATA_INPUT) { // no look-ahead for DataInput assertNull(p.nextToken()); } p.close(); } } } private byte[] _generateData(int size) { byte[] result = new byte[size]; for (int i = 0; i < size; ++i) { result[i] = (byte) (i % 255); } return result; } private void _testStreaming(int mode) throws IOException { final int[] SIZES = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 12, 100, 350, 1900, 6000, 19000, 65000, 139000 }; JsonFactory jsonFactory = sharedStreamFactory(); final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); StringWriter chars = null; for (int size : SIZES) { byte[] data = _generateData(size); JsonGenerator g; if (mode == MODE_READER) { chars = new StringWriter(); g = jsonFactory.createGenerator(chars); } else { bytes.reset(); g = jsonFactory.createGenerator(bytes, JsonEncoding.UTF8); } g.writeStartObject(); g.writeFieldName("b"); g.writeBinary(data); g.writeEndObject(); g.close(); // and verify JsonParser p; if (mode == MODE_READER) { p = jsonFactory.createParser(chars.toString()); } else { p = createParser(jsonFactory, mode, bytes.toByteArray()); } assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("b", p.getCurrentName()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); ByteArrayOutputStream result = new ByteArrayOutputStream(size); int gotten = p.readBinaryValue(result); assertEquals(size, gotten); assertArrayEquals(data, result.toByteArray()); assertToken(JsonToken.END_OBJECT, p.nextToken()); if (mode != MODE_DATA_INPUT) { // no look-ahead for DataInput assertNull(p.nextToken()); } p.close(); } } private void _testSimple(int mode, boolean leadingWS, boolean trailingWS) 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... String INPUT_STR = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz" +"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg" +"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu" +"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo" +"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=" ; if (leadingWS) { INPUT_STR = " "+INPUT_STR; } if (leadingWS) { INPUT_STR = INPUT_STR+" "; } 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 = sharedStreamFactory(); 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(); } private void _testEscapedPadding(int mode) throws IOException { // Input: "Test!" -> "VGVzdCE=" final String DOC = quote("VGVzdCE\\u003d"); // 06-Sep-2018, tatu: actually one more, test escaping of padding JsonParser p = createParser(mode, DOC); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("Test!", new String(p.getBinaryValue(), "US-ASCII")); if (mode != MODE_DATA_INPUT) { assertNull(p.nextToken()); } p.close(); // also, try out alternate access method p = createParser(mode, DOC); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("Test!", new String(_readBinary(p), "US-ASCII")); if (mode != MODE_DATA_INPUT) { assertNull(p.nextToken()); } p.close(); // and then different padding; "X" -> "WA==" final String DOC2 = quote("WA\\u003D\\u003D"); p = createParser(mode, DOC2); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("X", new String(p.getBinaryValue(), "US-ASCII")); if (mode != MODE_DATA_INPUT) { assertNull(p.nextToken()); } p.close(); p = createParser(mode, DOC2); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("X", new String(_readBinary(p), "US-ASCII")); if (mode != MODE_DATA_INPUT) { assertNull(p.nextToken()); } p.close(); } private byte[] _readBinary(JsonParser p) throws IOException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); p.readBinaryValue(bytes); return bytes.toByteArray(); } } Base64CodecTest.java000066400000000000000000000122671356164247300330740ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/base64package com.fasterxml.jackson.core.base64; import java.util.Arrays; import org.junit.Assert; import com.fasterxml.jackson.core.*; public class Base64CodecTest extends com.fasterxml.jackson.core.BaseTest { public void testVariantAccess() { for (Base64Variant var : new Base64Variant[] { Base64Variants.MIME, Base64Variants.MIME_NO_LINEFEEDS, Base64Variants.MODIFIED_FOR_URL, Base64Variants.PEM }) { assertSame(var, Base64Variants.valueOf(var.getName())); } try { Base64Variants.valueOf("foobar"); fail("Should not pass"); } catch (IllegalArgumentException e) { verifyException(e, "No Base64Variant with name 'foobar'"); } } public void testProps() { Base64Variant std = Base64Variants.MIME; // let's verify basic props of std cocec assertEquals("MIME", std.getName()); assertEquals("MIME", std.toString()); assertTrue(std.usesPadding()); assertFalse(std.usesPaddingChar('X')); assertEquals('=', std.getPaddingChar()); assertTrue(std.usesPaddingChar('=')); assertEquals((byte) '=', std.getPaddingByte()); assertEquals(76, std.getMaxLineLength()); } public void testCharEncoding() throws Exception { Base64Variant std = Base64Variants.MIME; assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Char('?')); assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Char((int) '?')); assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Char((char) 0xA0)); assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Char(0xA0)); assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Byte((byte) '?')); assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Byte((byte) 0xA0)); assertEquals(0, std.decodeBase64Char('A')); assertEquals(1, std.decodeBase64Char((int) 'B')); assertEquals(2, std.decodeBase64Char((byte)'C')); assertEquals(0, std.decodeBase64Byte((byte) 'A')); assertEquals(1, std.decodeBase64Byte((byte) 'B')); assertEquals(2, std.decodeBase64Byte((byte)'C')); assertEquals('/', std.encodeBase64BitsAsChar(63)); assertEquals((byte) 'b', std.encodeBase64BitsAsByte(27)); String EXP_STR = "HwdJ"; int TRIPLET = 0x1F0749; StringBuilder sb = new StringBuilder(); std.encodeBase64Chunk(sb, TRIPLET); assertEquals(EXP_STR, sb.toString()); byte[] exp = EXP_STR.getBytes("UTF-8"); byte[] act = new byte[exp.length]; std.encodeBase64Chunk(TRIPLET, act, 0); Assert.assertArrayEquals(exp, act); } public void testConvenienceMethods() throws Exception { final Base64Variant std = Base64Variants.MIME; byte[] input = new byte[] { 1, 2, 34, 127, -1 }; String encoded = std.encode(input, false); byte[] decoded = std.decode(encoded); Assert.assertArrayEquals(input, decoded); assertEquals(quote(encoded), std.encode(input, true)); // [core#414]: check white-space allow too decoded = std.decode("\n"+encoded); Assert.assertArrayEquals(input, decoded); decoded = std.decode(" "+encoded); Assert.assertArrayEquals(input, decoded); decoded = std.decode(encoded + " "); Assert.assertArrayEquals(input, decoded); decoded = std.decode(encoded + "\n"); Assert.assertArrayEquals(input, decoded); } public void testConvenienceMethodWithLFs() throws Exception { final Base64Variant std = Base64Variants.MIME; final int length = 100; final byte[] data = new byte[length]; Arrays.fill(data, (byte) 1); final StringBuilder sb = new StringBuilder(140); for (int i = 0; i < 100/3; ++i) { sb.append("AQEB"); if (sb.length() == 76) { sb.append("##"); } } sb.append("AQ=="); final String exp = sb.toString(); // first, JSON standard assertEquals(exp.replace("##", "\\n"), std.encode(data, false)); // then with custom linefeed assertEquals(exp.replace("##", "<%>"), std.encode(data, false, "<%>")); } @SuppressWarnings("unused") public void testErrors() throws Exception { try { Base64Variant b = new Base64Variant("foobar", "xyz", false, '!', 24); fail("Should not pass"); } catch (IllegalArgumentException iae) { verifyException(iae, "length must be exactly"); } try { Base64Variants.MIME.decode("!@##@%$#%&*^(&)(*"); } catch (IllegalArgumentException iae) { verifyException(iae, "Illegal character"); } // also, for [jackson-core#335] final String BASE64_HELLO = "aGVsbG8=!"; try { Base64Variants.MIME.decode(BASE64_HELLO); fail("Should not pass"); } catch (IllegalArgumentException iae) { verifyException(iae, "Illegal character"); } } } Base64GenerationTest.java000066400000000000000000000172411356164247300341470ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/base64package com.fasterxml.jackson.core.base64; import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.testsupport.ThrottledInputStream; public class Base64GenerationTest extends com.fasterxml.jackson.core.BaseTest { /* The usual sample input string, from Thomas Hobbes's "Leviathan" * (via Wikipedia) */ private final static String WIKIPEDIA_BASE64_TEXT = "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."; private final static byte[] WIKIPEDIA_BASE64_AS_BYTES; static { try { WIKIPEDIA_BASE64_AS_BYTES = WIKIPEDIA_BASE64_TEXT.getBytes("US-ASCII"); } catch (Exception e) { throw new RuntimeException(e); } } private final String WIKIPEDIA_BASE64_ENCODED = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz" +"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg" +"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu" +"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo" +"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=" ; private final static Base64Variant[] VARIANTS = { Base64Variants.MIME, Base64Variants.MIME_NO_LINEFEEDS, Base64Variants.MODIFIED_FOR_URL, Base64Variants.PEM }; /* /********************************************************** /* Test methods /********************************************************** */ private final JsonFactory JSON_F = new JsonFactory(); public void testStreamingBinaryWrites() throws Exception { _testStreamingWrites(JSON_F, true); _testStreamingWrites(JSON_F, false); } // For [core#55] public void testIssue55() throws Exception { final JsonFactory f = new JsonFactory(); // First, byte-backed: ByteArrayOutputStream bytes = new ByteArrayOutputStream(); JsonGenerator gen = f.createGenerator(bytes); ByteArrayInputStream data = new ByteArrayInputStream(new byte[2000]); gen.writeBinary(data, 1999); gen.close(); final int EXP_LEN = 2670; assertEquals(EXP_LEN, bytes.size()); // Then char-backed StringWriter sw = new StringWriter(); gen = f.createGenerator(sw); data = new ByteArrayInputStream(new byte[2000]); gen.writeBinary(data, 1999); gen.close(); assertEquals(EXP_LEN, sw.toString().length()); } /** * This is really inadequate test, all in all, but should serve * as some kind of sanity check. Reader-side should more thoroughly * test things, as it does need writers to construct the data first. */ public void testSimpleBinaryWrite() throws Exception { _testSimpleBinaryWrite(false); _testSimpleBinaryWrite(true); } // for [core#318] public void testBinaryAsEmbeddedObject() throws Exception { JsonGenerator g; StringWriter sw = new StringWriter(); g = JSON_F.createGenerator(sw); g.writeEmbeddedObject(WIKIPEDIA_BASE64_AS_BYTES); g.close(); assertEquals(quote(WIKIPEDIA_BASE64_ENCODED), sw.toString()); ByteArrayOutputStream bytes = new ByteArrayOutputStream(100); g = JSON_F.createGenerator(bytes); g.writeEmbeddedObject(WIKIPEDIA_BASE64_AS_BYTES); g.close(); assertEquals(quote(WIKIPEDIA_BASE64_ENCODED), bytes.toString("UTF-8")); } /* /********************************************************** /* Helper methods /********************************************************** */ private void _testSimpleBinaryWrite(boolean useCharBased) throws Exception { /* Let's only test the standard base64 variant; but write * values in root, array and object contexts. */ Base64Variant b64v = Base64Variants.getDefaultVariant(); JsonFactory jf = new JsonFactory(); for (int i = 0; i < 3; ++i) { JsonGenerator gen; ByteArrayOutputStream bout = new ByteArrayOutputStream(200); if (useCharBased) { gen = jf.createGenerator(new OutputStreamWriter(bout, "UTF-8")); } else { gen = jf.createGenerator(bout, JsonEncoding.UTF8); } switch (i) { case 0: // root gen.writeBinary(b64v, WIKIPEDIA_BASE64_AS_BYTES, 0, WIKIPEDIA_BASE64_AS_BYTES.length); break; case 1: // array gen.writeStartArray(); gen.writeBinary(b64v, WIKIPEDIA_BASE64_AS_BYTES, 0, WIKIPEDIA_BASE64_AS_BYTES.length); gen.writeEndArray(); break; default: // object gen.writeStartObject(); gen.writeFieldName("field"); gen.writeBinary(b64v, WIKIPEDIA_BASE64_AS_BYTES, 0, WIKIPEDIA_BASE64_AS_BYTES.length); gen.writeEndObject(); break; } gen.close(); JsonParser jp = jf.createParser(new ByteArrayInputStream(bout.toByteArray())); // Need to skip other events before binary data: switch (i) { case 0: break; case 1: assertEquals(JsonToken.START_ARRAY, jp.nextToken()); break; default: assertEquals(JsonToken.START_OBJECT, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); break; } assertEquals(JsonToken.VALUE_STRING, jp.nextToken()); String actualValue = jp.getText(); jp.close(); assertEquals(WIKIPEDIA_BASE64_ENCODED, actualValue); } } private final static String TEXT = "Some content so that we can test encoding of base64 data; must" +" be long enough include a line wrap or two..."; private final static String TEXT4 = TEXT + TEXT + TEXT + TEXT; @SuppressWarnings("resource") private void _testStreamingWrites(JsonFactory jf, boolean useBytes) throws Exception { final byte[] INPUT = TEXT4.getBytes("UTF-8"); for (Base64Variant variant : VARIANTS) { final String EXP_OUTPUT = "[" + quote(variant.encode(INPUT))+"]"; for (boolean passLength : new boolean[] { true, false }) { for (int chunkSize : new int[] { 1, 2, 3, 4, 7, 11, 29, 5000 }) { //System.err.println(""+variant+", length "+passLength+", chunk "+chunkSize); JsonGenerator jgen; final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); if (useBytes) { jgen = jf.createGenerator(bytes); } else { jgen = jf.createGenerator(new OutputStreamWriter(bytes, "UTF-8")); } jgen.writeStartArray(); int length = passLength ? INPUT.length : -1; InputStream data = new ThrottledInputStream(INPUT, chunkSize); jgen.writeBinary(variant, data, length); jgen.writeEndArray(); jgen.close(); String JSON = bytes.toString("UTF-8"); assertEquals(EXP_OUTPUT, JSON); } } } } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/filter/000077500000000000000000000000001356164247300276575ustar00rootroot00000000000000BasicGeneratorFilteringTest.java000066400000000000000000000301271356164247300360420ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/filterpackage com.fasterxml.jackson.core.filter; import java.io.*; import java.math.BigDecimal; import java.math.BigInteger; import java.util.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.SerializedString; /** * Low-level tests for explicit, hand-written tests for generator-side * filtering. */ @SuppressWarnings("resource") public class BasicGeneratorFilteringTest extends BaseTest { static class NameMatchFilter extends TokenFilter { private final Set _names; public NameMatchFilter(String... names) { _names = new HashSet(Arrays.asList(names)); } @Override public TokenFilter includeElement(int index) { return this; } @Override public TokenFilter includeProperty(String name) { if (_names.contains(name)) { return TokenFilter.INCLUDE_ALL; } return this; } @Override protected boolean _includeScalar() { return false; } } static class IndexMatchFilter extends TokenFilter { private final BitSet _indices; public IndexMatchFilter(int... ixs) { _indices = new BitSet(); for (int ix : ixs) { _indices.set(ix); } } @Override public TokenFilter includeProperty(String name) { return this; } @Override public TokenFilter includeElement(int index) { if (_indices.get(index)) { return TokenFilter.INCLUDE_ALL; } return null; } @Override protected boolean _includeScalar() { return false; } } /* /********************************************************** /* Test methods /********************************************************** */ private final JsonFactory JSON_F = new JsonFactory(); public void testNonFiltering() throws Exception { // First, verify non-filtering StringWriter w = new StringWriter(); JsonGenerator gen = JSON_F.createGenerator(w); final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"; writeJsonDoc(JSON_F, JSON, gen); assertEquals(aposToQuotes( "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"), w.toString()); } public void testSingleMatchFilteringWithoutPath() throws Exception { StringWriter w = new StringWriter(); JsonGenerator gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), new NameMatchFilter("value"), false, // includePath false // multipleMatches ); final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"; writeJsonDoc(JSON_F, JSON, gen); // 21-Apr-2015, tatu: note that there were plans to actually // allow "immediate parent inclusion" for matches on property // names. This behavior was NOT included in release however, so: // assertEquals(aposToQuotes("{'value':3}"), w.toString()); assertEquals(aposToQuotes("3"), w.toString()); } public void testSingleMatchFilteringWithPath() throws Exception { StringWriter w = new StringWriter(); JsonGenerator origGen = JSON_F.createGenerator(w); NameMatchFilter filter = new NameMatchFilter("value"); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(origGen, filter, true, // includePath false // multipleMatches ); // Hmmh. Should we get access to eventual target? assertSame(w, gen.getOutputTarget()); assertNotNull(gen.getFilterContext()); assertSame(filter, gen.getFilter()); final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"; writeJsonDoc(JSON_F, JSON, gen); assertEquals(aposToQuotes("{'ob':{'value':3}}"), w.toString()); assertEquals(1, gen.getMatchCount()); } public void testSingleMatchFilteringWithPathSkippedArray() throws Exception { StringWriter w = new StringWriter(); JsonGenerator origGen = JSON_F.createGenerator(w); NameMatchFilter filter = new NameMatchFilter("value"); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(origGen, filter, true, // includePath false // multipleMatches ); // Hmmh. Should we get access to eventual target? assertSame(w, gen.getOutputTarget()); assertNotNull(gen.getFilterContext()); assertSame(filter, gen.getFilter()); final String JSON = "{'array':[1,[2,3]],'ob':[{'value':'bar'}],'b':{'foo':[1,'foo']}}"; writeJsonDoc(JSON_F, JSON, gen); assertEquals(aposToQuotes("{'ob':[{'value':'bar'}]}"), w.toString()); assertEquals(1, gen.getMatchCount()); } // Alternative take, using slightly different calls for FIELD_NAME, START_ARRAY public void testSingleMatchFilteringWithPathAlternate1() throws Exception { StringWriter w = new StringWriter(); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), new NameMatchFilter("value"), true, // includePath false // multipleMatches ); //final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':[3],'value2':'foo'},'b':true}"; gen.writeStartObject(); gen.writeFieldName(new SerializedString("a")); gen.writeNumber(123); gen.writeFieldName("array"); gen.writeStartArray(2); gen.writeNumber("1"); gen.writeNumber((short) 2); gen.writeEndArray(); gen.writeFieldName(new SerializedString("ob")); gen.writeStartObject(); gen.writeNumberField("value0", 2); gen.writeFieldName(new SerializedString("value")); gen.writeStartArray(1); gen.writeString(new SerializedString("x")); // just to vary generation method gen.writeEndArray(); gen.writeStringField("value2", "foo"); gen.writeEndObject(); gen.writeBooleanField("b", true); gen.writeEndObject(); gen.close(); assertEquals(aposToQuotes("{'ob':{'value':['x']}}"), w.toString()); assertEquals(1, gen.getMatchCount()); } public void testSingleMatchFilteringWithPathRawBinary() throws Exception { StringWriter w = new StringWriter(); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), new NameMatchFilter("array"), true, // includePath false // multipleMatches ); //final String JSON = "{'header':['ENCODED',raw],'array':['base64stuff',1,2,3,4,5,6.25,7.5],'extra':[1,2,3,4,5,6.25,7.5]}"; gen.writeStartObject(); gen.writeFieldName("header"); gen.writeStartArray(); gen.writeBinary(new byte[] { 1 }); gen.writeRawValue(new SerializedString("1")); gen.writeRawValue("2"); gen.writeEndArray(); gen.writeFieldName("array"); gen.writeStartArray(); gen.writeBinary(new byte[] { 1 }); gen.writeNumber((short) 1); gen.writeNumber((int) 2); gen.writeNumber((long) 3); gen.writeNumber(BigInteger.valueOf(4)); gen.writeRaw(" "); gen.writeNumber(new BigDecimal("5.0")); gen.writeRaw(new SerializedString(" /*x*/")); gen.writeNumber(6.25f); gen.writeNumber(7.5); gen.writeEndArray(); gen.writeArrayFieldStart("extra"); gen.writeNumber((short) 1); gen.writeNumber((int) 2); gen.writeNumber((long) 3); gen.writeNumber(BigInteger.valueOf(4)); gen.writeNumber(new BigDecimal("5.0")); gen.writeNumber(6.25f); gen.writeNumber(7.5); gen.writeEndArray(); gen.writeEndObject(); gen.close(); assertEquals(aposToQuotes("{'array':['AQ==',1,2,3,4 ,5.0 /*x*/,6.25,7.5]}"), w.toString()); assertEquals(1, gen.getMatchCount()); } public void testMultipleMatchFilteringWithPath1() throws Exception { StringWriter w = new StringWriter(); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), new NameMatchFilter("value0", "value2"), true, /* includePath */ true /* multipleMatches */ ); final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"; writeJsonDoc(JSON_F, JSON, gen); assertEquals(aposToQuotes("{'ob':{'value0':2,'value2':4}}"), w.toString()); assertEquals(2, gen.getMatchCount()); } public void testMultipleMatchFilteringWithPath2() throws Exception { StringWriter w = new StringWriter(); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), new NameMatchFilter("array", "b", "value"), true, true); final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"; writeJsonDoc(JSON_F, JSON, gen); assertEquals(aposToQuotes("{'array':[1,2],'ob':{'value':3},'b':true}"), w.toString()); assertEquals(3, gen.getMatchCount()); } public void testMultipleMatchFilteringWithPath3() throws Exception { StringWriter w = new StringWriter(); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), new NameMatchFilter("value"), true, true); final String JSON = "{'root':{'a0':true,'a':{'value':3},'b':{'value':4}},'b0':false}"; writeJsonDoc(JSON_F, JSON, gen); assertEquals(aposToQuotes("{'root':{'a':{'value':3},'b':{'value':4}}}"), w.toString()); assertEquals(2, gen.getMatchCount()); } public void testIndexMatchWithPath1() throws Exception { StringWriter w = new StringWriter(); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), new IndexMatchFilter(1), true, true); final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"; writeJsonDoc(JSON_F, JSON, gen); assertEquals(aposToQuotes("{'array':[2]}"), w.toString()); w = new StringWriter(); gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), new IndexMatchFilter(0), true, true); writeJsonDoc(JSON_F, JSON, gen); assertEquals(aposToQuotes("{'array':[1]}"), w.toString()); assertEquals(1, gen.getMatchCount()); } public void testIndexMatchWithPath2() throws Exception { StringWriter w = new StringWriter(); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), new IndexMatchFilter(0,1), true, true); final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"; writeJsonDoc(JSON_F, JSON, gen); assertEquals(aposToQuotes("{'array':[1,2]}"), w.toString()); assertEquals(2, gen.getMatchCount()); } public void testWriteStartObjectWithObject() throws Exception { StringWriter w = new StringWriter(); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), TokenFilter.INCLUDE_ALL, true, true); String value = "val"; gen.writeStartObject(new Object()); gen.writeFieldName("field1"); { gen.writeStartObject(value); gen.writeEndObject(); } gen.writeFieldName("field2"); gen.writeString("val2"); gen.writeEndObject(); gen.close(); assertEquals(aposToQuotes("{'field1':{},'field2':'val2'}"), w.toString()); } } BasicParserFilteringTest.java000066400000000000000000000401561356164247300353530ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/filterpackage com.fasterxml.jackson.core.filter; import java.util.*; import com.fasterxml.jackson.core.*; @SuppressWarnings("resource") public class BasicParserFilteringTest extends BaseTest { static class NameMatchFilter extends TokenFilter { private final Set _names; public NameMatchFilter(String... names) { _names = new HashSet(Arrays.asList(names)); } @Override public TokenFilter includeElement(int index) { return this; } @Override public TokenFilter includeProperty(String name) { if (_names.contains(name)) { return TokenFilter.INCLUDE_ALL; } return this; } @Override protected boolean _includeScalar() { return false; } } static class IndexMatchFilter extends TokenFilter { private final BitSet _indices; public IndexMatchFilter(int... ixs) { _indices = new BitSet(); for (int ix : ixs) { _indices.set(ix); } } @Override public TokenFilter includeProperty(String name) { return this; } @Override public TokenFilter includeElement(int index) { if (_indices.get(index)) { return TokenFilter.INCLUDE_ALL; } return null; } @Override protected boolean _includeScalar() { return false; } } /* /********************************************************** /* Test methods /********************************************************** */ private final JsonFactory JSON_F = new JsonFactory(); private final String SIMPLE = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"); public void testNonFiltering() throws Exception { JsonParser p = JSON_F.createParser(SIMPLE); String result = readAndWrite(JSON_F, p); assertEquals(SIMPLE, result); } public void testSingleMatchFilteringWithoutPath() throws Exception { JsonParser p0 = JSON_F.createParser(SIMPLE); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), false, // includePath false // multipleMatches ); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("3"), result); assertEquals(1, p.getMatchCount()); } public void testSingleMatchFilteringWithPath1() throws Exception { String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"); JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("a"), true, // includePath false // multipleMatches ); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{'a':123}"), result); assertEquals(1, p.getMatchCount()); } public void testSingleMatchFilteringWithPath2() throws Exception { String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"); JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), true, // includePath false // multipleMatches ); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{\"ob\":{\"value\":3}}"), result); assertEquals(1, p.getMatchCount()); } public void testSingleMatchFilteringWithPath3() throws Exception { String jsonString = aposToQuotes("{'a':123,'ob':{'value0':2,'value':3,'value2':4},'array':[1,2],'b':true}"); JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("ob"), true, // includePath false // multipleMatches ); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{'ob':{'value0':2,'value':3,'value2':4}}"), result); assertEquals(1, p.getMatchCount()); } public void testNotAllowMultipleMatchesWithoutPath1() throws Exception { String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4,'value':{'value0':2}},'b':true}"); JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), false, // includePath false // multipleMatches -false ); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("3"), result); assertEquals(1, p.getMatchCount()); } public void testNotAllowMultipleMatchesWithoutPath2() throws Exception { String jsonString = aposToQuotes("{'a':123,'array':[1,2],'array':[3,4],'ob':{'value0':2,'value':3,'value2':4,'value':{'value0':2}},'value':\"val\",'b':true}"); JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new IndexMatchFilter(1), false, // includePath false // multipleMatches -false ); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("2"), result); assertEquals(1, p.getMatchCount()); } public void testNotAllowMultipleMatchesWithPath1() throws Exception { String jsonString = aposToQuotes("{'a':123,'array':[1,2],'array':[3,4],'ob':{'value':3,'array':[5,6],'value':{'value0':2}},'value':\"val\",'b':true}"); JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new IndexMatchFilter(1), true, // includePath false // multipleMatches -false ); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{\"array\":[2]}"), result); assertEquals(1, p.getMatchCount()); } public void testNotAllowMultipleMatchesWithPath2() throws Exception { String jsonString = aposToQuotes("{'a':123,'ob':{'value':3,'array':[1,2],'value':{'value0':2}},'array':[3,4]}"); JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new IndexMatchFilter(1), true, // includePath false // multipleMatches -false ); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{\"ob\":{\"array\":[2]}}"), result); assertEquals(1, p.getMatchCount()); } public void testNotAllowMultipleMatchesWithPath3() throws Exception { String jsonString = aposToQuotes("{'ob':{'value':3,'ob':{'value':2}},'value':\"val\"}"); JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), true, // includePath false // multipleMatches -false ); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{'ob':{'value':3}}"), result); assertEquals(1, p.getMatchCount()); } public void testNotAllowMultipleMatchesWithPath4() throws Exception { String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value1':1},'ob2':{'ob':{'value2':2}},'value':\"val\",'b':true}"); JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("ob"), true, // includePath false // multipleMatches -false ); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{'ob':{'value1':1}}"), result); assertEquals(1, p.getMatchCount()); } public void testAllowMultipleMatchesWithoutPath() throws Exception { String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4,'value':{'value0':2}},'value':\"val\",'b':true}"); JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), false, // includePath true // multipleMatches - true ); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("3 {\"value0\":2} \"val\""), result); assertEquals(3, p.getMatchCount()); } public void testAllowMultipleMatchesWithPath1() throws Exception { String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4,'value':{'value0':2}},'value':\"val\",'b':true}"); JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), true, // includePath true // multipleMatches - true ); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{\"ob\":{\"value\":3,\"value\":{\"value0\":2}},\"value\":\"val\"}"), result); assertEquals(3, p.getMatchCount()); } public void testAllowMultipleMatchesWithPath2() throws Exception { String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'array':[3,4],'value':{'value0':2}},'value':\"val\",'b':true}"); JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new IndexMatchFilter(1), true, // includePath true // multipleMatches - true ); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{\"array\":[2],\"ob\":{\"array\":[4]}}"), result); assertEquals(2, p.getMatchCount()); } public void testMultipleMatchFilteringWithPath1() throws Exception { JsonParser p0 = JSON_F.createParser(SIMPLE); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value0", "value2"), true, /* includePath */ true /* multipleMatches */ ); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{'ob':{'value0':2,'value2':4}}"), result); assertEquals(2, p.getMatchCount()); } public void testMultipleMatchFilteringWithPath2() throws Exception { String INPUT = aposToQuotes("{'a':123,'ob':{'value0':2,'value':3,'value2':4},'b':true}"); JsonParser p0 = JSON_F.createParser(INPUT); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("b", "value"), true, true); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{'ob':{'value':3},'b':true}"), result); assertEquals(2, p.getMatchCount()); } public void testMultipleMatchFilteringWithPath3() throws Exception { final String JSON = aposToQuotes("{'root':{'a0':true,'a':{'value':3},'b':{'value':\"foo\"}},'b0':false}"); JsonParser p0 = JSON_F.createParser(JSON); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), true, true); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{'root':{'a':{'value':3},'b':{'value':\"foo\"}}}"), result); assertEquals(2, p.getMatchCount()); } public void testIndexMatchWithPath1() throws Exception { FilteringParserDelegate p = new FilteringParserDelegate(JSON_F.createParser(SIMPLE), new IndexMatchFilter(1), true, true); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{'array':[2]}"), result); assertEquals(1, p.getMatchCount()); p = new FilteringParserDelegate(JSON_F.createParser(SIMPLE), new IndexMatchFilter(0), true, true); result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{'array':[1]}"), result); assertEquals(1, p.getMatchCount()); } public void testIndexMatchWithPath2() throws Exception { FilteringParserDelegate p = new FilteringParserDelegate(JSON_F.createParser(SIMPLE), new IndexMatchFilter(0, 1), true, true); assertEquals(aposToQuotes("{'array':[1,2]}"), readAndWrite(JSON_F, p)); assertEquals(2, p.getMatchCount()); String JSON = aposToQuotes("{'a':123,'array':[1,2,3,4,5],'b':[1,2,3]}"); p = new FilteringParserDelegate(JSON_F.createParser(JSON), new IndexMatchFilter(1, 3), true, true); assertEquals(aposToQuotes("{'array':[2,4],'b':[2]}"), readAndWrite(JSON_F, p)); assertEquals(3, p.getMatchCount()); } public void testBasicSingleMatchFilteringWithPath() throws Exception { JsonParser p0 = JSON_F.createParser(SIMPLE); JsonParser p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), true, // includePath false // multipleMatches ); // {'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true} String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{'ob':{'value':3}}"), result); } public void testTokensSingleMatchWithPath() throws Exception { JsonParser p0 = JSON_F.createParser(SIMPLE); JsonParser p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), true, // includePath false // multipleMatches ); assertFalse(p.hasCurrentToken()); assertNull(p.getCurrentToken()); assertEquals(JsonTokenId.ID_NO_TOKEN, p.getCurrentTokenId()); assertFalse(p.isExpectedStartObjectToken()); assertFalse(p.isExpectedStartArrayToken()); // {'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true} // String result = readAndWrite(JSON_F, p); // assertEquals(aposToQuotes("{'ob':{'value':3}}"), result); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertEquals(JsonToken.START_OBJECT, p.getCurrentToken()); assertTrue(p.isExpectedStartObjectToken()); assertFalse(p.isExpectedStartArrayToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals(JsonToken.FIELD_NAME, p.getCurrentToken()); assertEquals("ob", p.getCurrentName()); // assertEquals("ob", p.getText()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertEquals("ob", p.getCurrentName()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("value", p.getCurrentName()); assertEquals("value", p.getText()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(JsonToken.VALUE_NUMBER_INT, p.getCurrentToken()); assertEquals(JsonParser.NumberType.INT, p.getNumberType()); assertEquals(3, p.getIntValue()); assertEquals("value", p.getCurrentName()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertEquals(JsonToken.END_OBJECT, p.getCurrentToken()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertEquals(JsonToken.END_OBJECT, p.getCurrentToken()); p.clearCurrentToken(); assertNull(p.getCurrentToken()); p.close(); } public void testSkippingForSingleWithPath() throws Exception { JsonParser p0 = JSON_F.createParser(SIMPLE); JsonParser p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), true, // includePath false // multipleMatches ); assertToken(JsonToken.START_OBJECT, p.nextToken()); p.skipChildren(); assertEquals(JsonToken.END_OBJECT, p.getCurrentToken()); assertNull(p.nextToken()); } } JsonPointerGeneratorFilteringTest.java000066400000000000000000000076721356164247300373040ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/filterpackage com.fasterxml.jackson.core.filter; import java.io.*; import com.fasterxml.jackson.core.*; @SuppressWarnings("resource") public class JsonPointerGeneratorFilteringTest extends com.fasterxml.jackson.core.BaseTest { private final JsonFactory JSON_F = new JsonFactory(); final String SIMPLE_INPUT = aposToQuotes("{'a':1,'b':[1,2,3],'c':{'d':{'a':true}},'d':null}"); public void testSimplePropertyWithPath() throws Exception { _assert(SIMPLE_INPUT, "/c", true, "{'c':{'d':{'a':true}}}"); _assert(SIMPLE_INPUT, "/c/d", true, "{'c':{'d':{'a':true}}}"); _assert(SIMPLE_INPUT, "/c/d/a", true, "{'c':{'d':{'a':true}}}"); _assert(SIMPLE_INPUT, "/c/d/a", true, "{'c':{'d':{'a':true}}}"); _assert(SIMPLE_INPUT, "/a", true, "{'a':1}"); _assert(SIMPLE_INPUT, "/d", true, "{'d':null}"); // and then non-match _assert(SIMPLE_INPUT, "/x", true, ""); } public void testSimplePropertyWithoutPath() throws Exception { _assert(SIMPLE_INPUT, "/c", false, "{'d':{'a':true}}"); _assert(SIMPLE_INPUT, "/c/d", false, "{'a':true}"); _assert(SIMPLE_INPUT, "/c/d/a", false, "true"); _assert(SIMPLE_INPUT, "/a", false, "1"); _assert(SIMPLE_INPUT, "/d", false, "null"); // and then non-match _assert(SIMPLE_INPUT, "/x", false, ""); } public void testArrayElementWithPath() throws Exception { _assert(SIMPLE_INPUT, "/b", true, "{'b':[1,2,3]}"); _assert(SIMPLE_INPUT, "/b/1", true, "{'b':[2]}"); _assert(SIMPLE_INPUT, "/b/2", true, "{'b':[3]}"); // and then non-match _assert(SIMPLE_INPUT, "/b/8", true, ""); } public void testArrayNestedWithPath() throws Exception { _assert("{'a':[true,{'b':3,'d':2},false]}", "/a/1/b", true, "{'a':[{'b':3}]}"); _assert("[true,[1]]", "/0", true, "[true]"); _assert("[true,[1]]", "/1", true, "[[1]]"); _assert("[true,[1,2,[true],3],0]", "/0", true, "[true]"); _assert("[true,[1,2,[true],3],0]", "/1", true, "[[1,2,[true],3]]"); _assert("[true,[1,2,[true],3],0]", "/1/2", true, "[[[true]]]"); _assert("[true,[1,2,[true],3],0]", "/1/2/0", true, "[[[true]]]"); _assert("[true,[1,2,[true],3],0]", "/1/3/0", true, ""); } public void testArrayNestedWithoutPath() throws Exception { _assert("{'a':[true,{'b':3,'d':2},false]}", "/a/1/b", false, "3"); _assert("[true,[1,2,[true],3],0]", "/0", false, "true"); _assert("[true,[1,2,[true],3],0]", "/1", false, "[1,2,[true],3]"); _assert("[true,[1,2,[true],3],0]", "/1/2", false, "[true]"); _assert("[true,[1,2,[true],3],0]", "/1/2/0", false, "true"); _assert("[true,[1,2,[true],3],0]", "/1/3/0", false, ""); } // final String SIMPLE_INPUT = aposToQuotes("{'a':1,'b':[1,2,3],'c':{'d':{'a':true}},'d':null}"); public void testArrayElementWithoutPath() throws Exception { _assert(SIMPLE_INPUT, "/b", false, "[1,2,3]"); _assert(SIMPLE_INPUT, "/b/1", false, "2"); _assert(SIMPLE_INPUT, "/b/2", false, "3"); _assert(SIMPLE_INPUT, "/b/8", false, ""); // and then non-match _assert(SIMPLE_INPUT, "/x", false, ""); } private void _assert(String input, String pathExpr, boolean includeParent, String exp) throws Exception { StringWriter w = new StringWriter(); JsonGenerator g0 = JSON_F.createGenerator(w); FilteringGeneratorDelegate g = new FilteringGeneratorDelegate(g0, new JsonPointerBasedFilter(pathExpr), includeParent, false); try { writeJsonDoc(JSON_F, input, g); } catch (Exception e) { g0.flush(); System.err.println("With input '"+input+"', output at point of failure: <"+w+">"); throw e; } assertEquals(aposToQuotes(exp), w.toString()); } } JsonPointerParserFilteringTest.java000066400000000000000000000056631356164247300366100ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/filterpackage com.fasterxml.jackson.core.filter; import java.io.StringWriter; import com.fasterxml.jackson.core.*; public class JsonPointerParserFilteringTest extends com.fasterxml.jackson.core.BaseTest { private final JsonFactory JSON_F = new JsonFactory(); final String SIMPLEST_INPUT = aposToQuotes("{'a':1,'b':2,'c':3}"); final String SIMPLE_INPUT = aposToQuotes("{'a':1,'b':[1,2,3],'c':{'d':{'a':true}},'d':null}"); public void testSimplestWithPath() throws Exception { _assert(SIMPLEST_INPUT, "/a", true, "{'a':1}"); _assert(SIMPLEST_INPUT, "/b", true, "{'b':2}"); _assert(SIMPLEST_INPUT, "/c", true, "{'c':3}"); _assert(SIMPLEST_INPUT, "/c/0", true, ""); _assert(SIMPLEST_INPUT, "/d", true, ""); } public void testSimplestNoPath() throws Exception { _assert(SIMPLEST_INPUT, "/a", false, "1"); _assert(SIMPLEST_INPUT, "/b", false, "2"); _assert(SIMPLEST_INPUT, "/b/2", false, ""); _assert(SIMPLEST_INPUT, "/c", false, "3"); _assert(SIMPLEST_INPUT, "/d", false, ""); } public void testSimpleWithPath() throws Exception { _assert(SIMPLE_INPUT, "/c", true, "{'c':{'d':{'a':true}}}"); _assert(SIMPLE_INPUT, "/c/d", true, "{'c':{'d':{'a':true}}}"); _assert(SIMPLE_INPUT, "/a", true, "{'a':1}"); _assert(SIMPLE_INPUT, "/b", true, "{'b':[1,2,3]}"); _assert(SIMPLE_INPUT, "/b/0", true, "{'b':[1]}"); _assert(SIMPLE_INPUT, "/b/1", true, "{'b':[2]}"); _assert(SIMPLE_INPUT, "/b/2", true, "{'b':[3]}"); _assert(SIMPLE_INPUT, "/b/3", true, ""); } public void testSimpleNoPath() throws Exception { _assert(SIMPLE_INPUT, "/c", false, "{'d':{'a':true}}"); _assert(SIMPLE_INPUT, "/c/d", false, "{'a':true}"); _assert(SIMPLE_INPUT, "/a", false, "1"); _assert(SIMPLE_INPUT, "/b", false, "[1,2,3]"); _assert(SIMPLE_INPUT, "/b/0", false, "1"); _assert(SIMPLE_INPUT, "/b/1", false, "2"); _assert(SIMPLE_INPUT, "/b/2", false, "3"); _assert(SIMPLE_INPUT, "/b/3", false, ""); } @SuppressWarnings("resource") void _assert(String input, String pathExpr, boolean includeParent, String exp) throws Exception { JsonParser p0 = JSON_F.createParser(input); FilteringParserDelegate p = new FilteringParserDelegate(p0, new JsonPointerBasedFilter(pathExpr), includeParent, false); StringWriter w = new StringWriter(); JsonGenerator g = JSON_F.createGenerator(w); try { while (p.nextToken() != null) { g.copyCurrentEvent(p); } p.close(); g.close(); } catch (Exception e) { g.flush(); System.err.println("With input '"+input+"', output at point of failure: <"+w+">"); throw e; } assertEquals(aposToQuotes(exp), w.toString()); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/format/000077500000000000000000000000001356164247300276625ustar00rootroot00000000000000DataFormatMatcherTest.java000066400000000000000000000022401356164247300346320ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/formatpackage com.fasterxml.jackson.core.format; import java.io.IOException; import java.io.InputStream; import com.fasterxml.jackson.core.JsonFactory; /** * Unit tests for class {@link DataFormatMatcher}. */ public class DataFormatMatcherTest extends com.fasterxml.jackson.core.BaseTest { public void testGetDataStream() throws IOException { byte[] byteArray = new byte[2]; MatchStrength matchStrength = MatchStrength.WEAK_MATCH; DataFormatMatcher dataFormatMatcher = new DataFormatMatcher(null, byteArray, 1, 0, null, matchStrength); InputStream inputStream = dataFormatMatcher.getDataStream(); assertEquals(0, inputStream.available()); inputStream.close(); } public void testCreatesDataFormatMatcherTwo() throws IOException { JsonFactory jsonFactory = new JsonFactory(); try { @SuppressWarnings("unused") DataFormatMatcher dataFormatMatcher = new DataFormatMatcher(null, new byte[0], 2, 1, jsonFactory, MatchStrength.NO_MATCH); } catch (IllegalArgumentException e) { verifyException(e, "Illegal start/length"); } } } TestJsonFormatDetection.java000066400000000000000000000075761356164247300352460ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/formatpackage com.fasterxml.jackson.core.format; import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.format.DataFormatDetector; import com.fasterxml.jackson.core.format.DataFormatMatcher; import com.fasterxml.jackson.core.format.MatchStrength; public class TestJsonFormatDetection extends com.fasterxml.jackson.core.BaseTest { public void testSimpleValidArray() throws Exception { JsonFactory jsonF = new JsonFactory(); DataFormatDetector detector = new DataFormatDetector(jsonF); final String ARRAY_JSON = "[ 1, 2 ]"; DataFormatMatcher matcher = detector.findFormat(new ByteArrayInputStream(ARRAY_JSON.getBytes("UTF-8"))); // should have match assertTrue(matcher.hasMatch()); assertEquals("JSON", matcher.getMatchedFormatName()); assertSame(jsonF, matcher.getMatch()); // no "certain" match with JSON, but solid: assertEquals(MatchStrength.SOLID_MATCH, matcher.getMatchStrength()); // and thus: JsonParser jp = matcher.createParserWithMatch(); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertNull(jp.nextToken()); jp.close(); } public void testSimpleValidObject() throws Exception { JsonFactory jsonF = new JsonFactory(); DataFormatDetector detector = new DataFormatDetector(jsonF); final String JSON = "{ \"field\" : true }"; DataFormatMatcher matcher = detector.findFormat(new ByteArrayInputStream(JSON.getBytes("UTF-8"))); // should have match assertTrue(matcher.hasMatch()); assertEquals("JSON", matcher.getMatchedFormatName()); assertSame(jsonF, matcher.getMatch()); // no "certain" match with JSON, but solid: assertEquals(MatchStrength.SOLID_MATCH, matcher.getMatchStrength()); // and thus: JsonParser jp = matcher.createParserWithMatch(); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("field", jp.getCurrentName()); assertToken(JsonToken.VALUE_TRUE, jp.nextToken()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); assertNull(jp.nextToken()); jp.close(); } /** * While JSON String is not a strong match alone, it should * be detected unless some better match is available */ public void testSimpleValidString() throws Exception { JsonFactory jsonF = new JsonFactory(); DataFormatDetector detector = new DataFormatDetector(jsonF); final String JSON = "\"JSON!\""; DataFormatMatcher matcher = detector.findFormat(new ByteArrayInputStream(JSON.getBytes("UTF-8"))); // should have match assertTrue(matcher.hasMatch()); assertEquals("JSON", matcher.getMatchedFormatName()); assertSame(jsonF, matcher.getMatch()); assertEquals(MatchStrength.WEAK_MATCH, matcher.getMatchStrength()); JsonParser jp = matcher.createParserWithMatch(); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("JSON!", jp.getText()); assertNull(jp.nextToken()); jp.close(); } public void testSimpleInvalid() throws Exception { DataFormatDetector detector = new DataFormatDetector(new JsonFactory()); final String NON_JSON = ""; DataFormatMatcher matcher = detector.findFormat(new ByteArrayInputStream(NON_JSON.getBytes("UTF-8"))); // should not have match assertFalse(matcher.hasMatch()); // and thus: assertEquals(MatchStrength.INCONCLUSIVE, matcher.getMatchStrength()); // also: assertNull(matcher.createParserWithMatch()); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/io/000077500000000000000000000000001356164247300270015ustar00rootroot00000000000000SegmentedStringWriterTest.java000066400000000000000000000025501356164247300347260ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/iopackage com.fasterxml.jackson.core.io; import com.fasterxml.jackson.core.io.SegmentedStringWriter; import com.fasterxml.jackson.core.util.BufferRecycler; public class SegmentedStringWriterTest extends com.fasterxml.jackson.core.BaseTest { public void testSimple() throws Exception { BufferRecycler br = new BufferRecycler(); SegmentedStringWriter w = new SegmentedStringWriter(br); StringBuilder exp = new StringBuilder(); for (int i = 0; exp.length() < 100; ++i) { String nr = String.valueOf(i); exp.append(' ').append(nr); w.append(' '); switch (i % 4) { case 0: w.append(nr); break; case 1: { String str = " "+nr; w.append(str, 2, str.length()); } break; case 2: w.write(nr.toCharArray()); break; default: { char[] ch = (" "+nr+" ").toCharArray(); w.write(ch, 1, nr.length()); } break; } } // flush, close are nops but trigger just for fun w.flush(); w.close(); String act = w.getAndClear(); assertEquals(exp.toString(), act); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/io/TestCharTypes.java000066400000000000000000000017561356164247300324170ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; public class TestCharTypes extends com.fasterxml.jackson.core.BaseTest { public void testAppendQuoted0_31 () { final String[] inputs = { "\u0000", "\u001F", "abcd", "\u0001ABCD\u0002", "WX\u000F\u0010YZ" }; final String[] expecteds = { "\\u0000", "\\u001F", "abcd", "\\u0001ABCD\\u0002", "WX\\u000F\\u0010YZ" }; assert inputs.length == expecteds.length; for (int i = 0; i < inputs.length; i++) { final String input = inputs[i]; final String expected = expecteds[i]; final StringBuilder sb = new StringBuilder(); CharTypes.appendQuoted(sb, input); final String actual = sb.toString(); assertEquals(expected, actual); } } public void testHexOutOfRange() { final int[] inputs = {0, -1, 1, 129, -129}; for (int input : inputs) { assertEquals(-1, CharTypes.charToHex(input)); } } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/io/TestIOContext.java000066400000000000000000000054351356164247300323670ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import com.fasterxml.jackson.core.io.IOContext; import com.fasterxml.jackson.core.util.BufferRecycler; public class TestIOContext extends com.fasterxml.jackson.core.BaseTest { public void testAllocations() throws Exception { IOContext ctxt = new IOContext(new BufferRecycler(), "N/A", true); /* I/O Read buffer */ // First succeeds: assertNotNull(ctxt.allocReadIOBuffer()); // second fails try { ctxt.allocReadIOBuffer(); } catch (IllegalStateException e) { verifyException(e, "second time"); } // Also: can't succeed with different buffer try { ctxt.releaseReadIOBuffer(new byte[1]); } catch (IllegalArgumentException e) { verifyException(e, "smaller than original"); } // but call with null is a NOP for convenience ctxt.releaseReadIOBuffer(null); /* I/O Write buffer */ assertNotNull(ctxt.allocWriteEncodingBuffer()); try { ctxt.allocWriteEncodingBuffer(); } catch (IllegalStateException e) { verifyException(e, "second time"); } try { ctxt.releaseWriteEncodingBuffer(new byte[1]); } catch (IllegalArgumentException e) { verifyException(e, "smaller than original"); } ctxt.releaseWriteEncodingBuffer(null); /* Token (read) buffer */ assertNotNull(ctxt.allocTokenBuffer()); try { ctxt.allocTokenBuffer(); } catch (IllegalStateException e) { verifyException(e, "second time"); } try { ctxt.releaseTokenBuffer(new char[1]); } catch (IllegalArgumentException e) { verifyException(e, "smaller than original"); } ctxt.releaseTokenBuffer(null); /* Concat (write?) buffer */ assertNotNull(ctxt.allocConcatBuffer()); try { ctxt.allocConcatBuffer(); } catch (IllegalStateException e) { verifyException(e, "second time"); } try { ctxt.releaseConcatBuffer(new char[1]); } catch (IllegalArgumentException e) { verifyException(e, "smaller than original"); } ctxt.releaseConcatBuffer(null); /* NameCopy (write?) buffer */ assertNotNull(ctxt.allocNameCopyBuffer(100)); try { ctxt.allocNameCopyBuffer(100); } catch (IllegalStateException e) { verifyException(e, "second time"); } try { ctxt.releaseNameCopyBuffer(new char[1]); } catch (IllegalArgumentException e) { verifyException(e, "smaller than original"); } ctxt.releaseNameCopyBuffer(null); } } TestJsonStringEncoder.java000066400000000000000000000116551356164247300340350ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/iopackage com.fasterxml.jackson.core.io; import java.io.StringWriter; import java.util.Random; import static org.junit.Assert.*; import com.fasterxml.jackson.core.*; public class TestJsonStringEncoder extends com.fasterxml.jackson.core.BaseTest { public void testQuoteAsString() throws Exception { JsonStringEncoder encoder = new JsonStringEncoder(); char[] result = encoder.quoteAsString("foobar"); assertArrayEquals("foobar".toCharArray(), result); result = encoder.quoteAsString("\"x\""); assertArrayEquals("\\\"x\\\"".toCharArray(), result); } public void testQuoteCharSequenceAsString() throws Exception { StringBuilder output = new StringBuilder(); StringBuilder builder = new StringBuilder(); builder.append("foobar"); JsonStringEncoder.getInstance().quoteAsString(builder, output); assertEquals("foobar", output.toString()); builder.setLength(0); output.setLength(0); builder.append("\"x\""); JsonStringEncoder.getInstance().quoteAsString(builder, output); assertEquals("\\\"x\\\"", output.toString()); } // For [JACKSON-853] public void testQuoteLongAsString() throws Exception { JsonStringEncoder encoder = new JsonStringEncoder(); StringBuilder sb = new StringBuilder(); StringBuilder sb2 = new StringBuilder(); for (int i = 0; i < 1111; ++i) { sb.append('"'); sb2.append("\\\""); } String input = sb.toString(); String exp = sb2.toString(); char[] result = encoder.quoteAsString(input); assertEquals(2*input.length(), result.length); assertEquals(exp, new String(result)); } public void testQuoteLongCharSequenceAsString() throws Exception { 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(); JsonStringEncoder.getInstance().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 JsonFactory f = new JsonFactory(); JsonStringEncoder encoder = new JsonStringEncoder(); int[] lengths = new int[] { 5, 19, 200, 7000, 21000, 37000 }; for (int length : lengths) { String str = generateRandom(length); StringWriter sw = new StringWriter(length*2); JsonGenerator jgen = f.createGenerator(sw); jgen.writeString(str); jgen.close(); String encoded = sw.toString(); // ok, except need to remove surrounding quotes encoded = encoded.substring(1, encoded.length() - 1); byte[] expected = encoded.getBytes("UTF-8"); byte[] actual = encoder.quoteAsUTF8(str); assertArrayEquals(expected, actual); } } public void testEncodeAsUTF8() throws Exception { JsonStringEncoder encoder = new JsonStringEncoder(); String[] strings = new String[] { "a", "foobar", "p\u00f6ll\u00f6", "\"foo\"", generateRandom(200), generateRandom(5000), generateRandom(39000) }; for (String str : strings) { assertArrayEquals(str.getBytes("UTF-8"), encoder.encodeAsUTF8(str)); } } public void testCtrlChars() throws Exception { char[] input = new char[] { 0, 1, 2, 3, 4 }; 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 /********************************************************** */ private String generateRandom(int length) { StringBuilder sb = new StringBuilder(length); Random rnd = new Random(length); for (int i = 0; i < length; ++i) { // let's limit it not to include surrogate pairs: char ch = (char) rnd.nextInt(0xCFFF); sb.append(ch); } return sb.toString(); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/io/TestMergedStream.java000066400000000000000000000037121356164247300330660ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.io.*; import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.io.IOContext; import com.fasterxml.jackson.core.io.MergedStream; import com.fasterxml.jackson.core.util.BufferRecycler; public class TestMergedStream extends com.fasterxml.jackson.core.BaseTest { public void testSimple() throws Exception { BufferRecycler rec = new BufferRecycler(); IOContext ctxt = new IOContext(rec, null, false); // bit complicated; must use recyclable buffer... byte[] first = ctxt.allocReadIOBuffer(); System.arraycopy("ABCDE".getBytes("UTF-8"), 0, first, 99, 5); byte[] second = "FGHIJ".getBytes("UTF-8"); assertNull(ctxt.getSourceReference()); assertFalse(ctxt.isResourceManaged()); ctxt.setEncoding(JsonEncoding.UTF8); MergedStream ms = new MergedStream(ctxt, new ByteArrayInputStream(second), first, 99, 99+5); // Ok, first, should have 5 bytes from first buffer: assertEquals(5, ms.available()); // not supported when there's buffered stuff... assertFalse(ms.markSupported()); // so this won't work, but shouldn't throw exception ms.mark(1); assertEquals((byte) 'A', ms.read()); assertEquals(3, ms.skip(3)); byte[] buffer = new byte[5]; /* Ok, now, code is allowed to return anywhere between 1 and 3, * but we now it will return 1... */ assertEquals(1, ms.read(buffer, 1, 3)); assertEquals((byte) 'E', buffer[1]); // So let's read bit more assertEquals(3, ms.read(buffer, 0, 3)); assertEquals((byte) 'F', buffer[0]); assertEquals((byte) 'G', buffer[1]); assertEquals((byte) 'H', buffer[2]); assertEquals(2, ms.available()); // And then skip the reset assertEquals(2, ms.skip(200)); ms.close(); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/io/UTF8WriterTest.java000066400000000000000000000115431356164247300324330ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.io.*; import org.junit.Assert; import com.fasterxml.jackson.core.io.IOContext; import com.fasterxml.jackson.core.io.UTF8Writer; import com.fasterxml.jackson.core.util.BufferRecycler; public class UTF8WriterTest extends com.fasterxml.jackson.core.BaseTest { public void testSimple() throws Exception { BufferRecycler rec = new BufferRecycler(); IOContext ctxt = new IOContext(rec, null, false); ByteArrayOutputStream out = new ByteArrayOutputStream(); UTF8Writer w = new UTF8Writer(ctxt, out); String str = "AB\u00A0\u1AE9\uFFFC"; char[] ch = str.toCharArray(); // Let's write 3 times, using different methods w.write(str); w.append(ch[0]); w.write(ch[1]); w.write(ch, 2, 3); w.flush(); w.write(str, 0, str.length()); w.close(); // and thus should have 3 times contents byte[] data = out.toByteArray(); assertEquals(3*10, data.length); String act = out.toString("UTF-8"); assertEquals(15, act.length()); assertEquals(3 * str.length(), act.length()); assertEquals(str+str+str, act); } public void testSimpleAscii() throws Exception { BufferRecycler rec = new BufferRecycler(); IOContext ctxt = new IOContext(rec, null, false); ByteArrayOutputStream out = new ByteArrayOutputStream(); UTF8Writer w = new UTF8Writer(ctxt, out); String str = "abcdefghijklmnopqrst\u00A0"; char[] ch = str.toCharArray(); w.write(ch, 0, ch.length); w.flush(); // trigger different code path for close w.close(); byte[] data = out.toByteArray(); // one 2-byte encoded char assertEquals(ch.length+1, data.length); String act = out.toString("UTF-8"); assertEquals(str, act); } public void testFlushAfterClose() throws Exception { BufferRecycler rec = new BufferRecycler(); IOContext ctxt = new IOContext(rec, null, false); ByteArrayOutputStream out = new ByteArrayOutputStream(); UTF8Writer w = new UTF8Writer(ctxt, out); w.write('X'); char[] ch = { 'Y' }; w.write(ch); w.close(); assertEquals(2, out.size()); // and this ought to be fine... w.flush(); // as well as some more... w.close(); w.flush(); } public void testSurrogatesOk() throws Exception { BufferRecycler rec = new BufferRecycler(); IOContext ctxt = new IOContext(rec, null, false); ByteArrayOutputStream out = new ByteArrayOutputStream(); UTF8Writer w = new UTF8Writer(ctxt, out); // First, valid case, char by char w.write(0xD83D); w.write(0xDE03); w.close(); assertEquals(4, out.size()); final byte[] EXP_SURROGATES = new byte[] { (byte) 0xF0, (byte) 0x9F, (byte) 0x98, (byte) 0x83 }; Assert.assertArrayEquals(EXP_SURROGATES, out.toByteArray()); // and then as String ctxt = new IOContext(rec, null, false); out = new ByteArrayOutputStream(); w = new UTF8Writer(ctxt, out); w.write("\uD83D\uDE03"); w.close(); assertEquals(4, out.size()); Assert.assertArrayEquals(EXP_SURROGATES, out.toByteArray()); } @SuppressWarnings("resource") public void testSurrogatesFail() throws Exception { BufferRecycler rec = new BufferRecycler(); IOContext ctxt; ByteArrayOutputStream out; UTF8Writer w; ctxt = new IOContext(rec, null, false); out = new ByteArrayOutputStream(); w = new UTF8Writer(ctxt, out); try { w.write(0xDE03); fail("should not pass"); } catch (IOException e) { verifyException(e, "Unmatched second part"); } ctxt = new IOContext(rec, null, false); out = new ByteArrayOutputStream(); w = new UTF8Writer(ctxt, out); w.write(0xD83D); try { w.write('a'); fail("should not pass"); } catch (IOException e) { verifyException(e, "Broken surrogate pair"); } ctxt = new IOContext(rec, null, false); out = new ByteArrayOutputStream(); w = new UTF8Writer(ctxt, out); try { w.write("\uDE03"); fail("should not pass"); } catch (IOException e) { verifyException(e, "Unmatched second part"); } ctxt = new IOContext(rec, null, false); out = new ByteArrayOutputStream(); w = new UTF8Writer(ctxt, out); try { w.write("\uD83Da"); fail("should not pass"); } catch (IOException e) { verifyException(e, "Broken surrogate pair"); } } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/000077500000000000000000000000001356164247300273435ustar00rootroot00000000000000ArrayGenerationTest.java000066400000000000000000000153661356164247300340740ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.ByteArrayOutputStream; import java.io.StringWriter; import com.fasterxml.jackson.core.*; /** * Basic testing for scalar-array write methods added in 2.8. */ public class ArrayGenerationTest extends BaseTest { private final JsonFactory FACTORY = new JsonFactory(); public void testIntArray() throws Exception { _testIntArray(false); _testIntArray(true); } public void testLongArray() throws Exception { _testLongArray(false); _testLongArray(true); } public void testDoubleArray() throws Exception { _testDoubleArray(false); _testDoubleArray(true); } private void _testIntArray(boolean useBytes) throws Exception { // first special cases of 0, 1 values _testIntArray(useBytes, 0, 0, 0); _testIntArray(useBytes, 0, 1, 1); _testIntArray(useBytes, 1, 0, 0); _testIntArray(useBytes, 1, 1, 1); // and then some bigger data _testIntArray(useBytes, 15, 0, 0); _testIntArray(useBytes, 15, 2, 3); _testIntArray(useBytes, 39, 0, 0); _testIntArray(useBytes, 39, 4, 0); _testIntArray(useBytes, 271, 0, 0); _testIntArray(useBytes, 271, 0, 4); _testIntArray(useBytes, 5009, 0, 0); _testIntArray(useBytes, 5009, 0, 1); } private void _testLongArray(boolean useBytes) throws Exception { // first special cases of 0, 1 values _testLongArray(useBytes, 0, 0, 0); _testLongArray(useBytes, 0, 1, 1); _testLongArray(useBytes, 1, 0, 0); _testLongArray(useBytes, 1, 1, 1); // and then some bigger data _testLongArray(useBytes, 15, 0, 0); _testLongArray(useBytes, 15, 2, 3); _testLongArray(useBytes, 39, 0, 0); _testLongArray(useBytes, 39, 4, 0); _testLongArray(useBytes, 271, 0, 0); _testLongArray(useBytes, 271, 0, 4); _testLongArray(useBytes, 5009, 0, 0); _testLongArray(useBytes, 5009, 0, 1); } private void _testDoubleArray(boolean useBytes) throws Exception { // first special cases of 0, 1 values _testDoubleArray(useBytes, 0, 0, 0); _testDoubleArray(useBytes, 0, 1, 1); _testDoubleArray(useBytes, 1, 0, 0); _testDoubleArray(useBytes, 1, 1, 1); // and then some bigger data _testDoubleArray(useBytes, 15, 0, 0); _testDoubleArray(useBytes, 15, 2, 3); _testDoubleArray(useBytes, 39, 0, 0); _testDoubleArray(useBytes, 39, 4, 0); _testDoubleArray(useBytes, 271, 0, 0); _testDoubleArray(useBytes, 271, 0, 4); _testDoubleArray(useBytes, 5009, 0, 0); _testDoubleArray(useBytes, 5009, 0, 1); } private void _testIntArray(boolean useBytes, int elements, int pre, int post) throws Exception { int[] values = new int[elements+pre+post]; for (int i = pre, end = pre+elements; i < end; ++i) { values[i] = i-pre; } StringWriter sw = new StringWriter(); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); JsonGenerator gen = useBytes ? FACTORY.createGenerator(bytes) : FACTORY.createGenerator(sw); gen.writeArray(values, pre, elements); gen.close(); String json; if (useBytes) { json = bytes.toString("UTF-8"); } else { json = sw.toString(); } JsonParser p = useBytes ? FACTORY.createParser(bytes.toByteArray()) : FACTORY.createParser(json); assertToken(JsonToken.START_ARRAY, p.nextToken()); for (int i = 0; i < elements; ++i) { if ((i & 1) == 0) { // alternate JsonToken t = p.nextToken(); if (t != JsonToken.VALUE_NUMBER_INT) { fail("Expected number, got "+t+", element #"+i); } assertEquals(i, p.getIntValue()); } else { assertEquals(i, p.nextIntValue(-1)); } } assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); } private void _testLongArray(boolean useBytes, int elements, int pre, int post) throws Exception { long[] values = new long[elements+pre+post]; for (int i = pre, end = pre+elements; i < end; ++i) { values[i] = i-pre; } StringWriter sw = new StringWriter(); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); JsonGenerator gen = useBytes ? FACTORY.createGenerator(bytes) : FACTORY.createGenerator(sw); gen.writeArray(values, pre, elements); gen.close(); String json; if (useBytes) { json = bytes.toString("UTF-8"); } else { json = sw.toString(); } JsonParser p = useBytes ? FACTORY.createParser(bytes.toByteArray()) : FACTORY.createParser(json); assertToken(JsonToken.START_ARRAY, p.nextToken()); for (int i = 0; i < elements; ++i) { if ((i & 1) == 0) { // alternate JsonToken t = p.nextToken(); if (t != JsonToken.VALUE_NUMBER_INT) { fail("Expected number, got "+t+", element #"+i); } assertEquals(i, p.getLongValue()); } else { assertEquals(i, p.nextLongValue(-1)); } } assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); } private void _testDoubleArray(boolean useBytes, int elements, int pre, int post) throws Exception { double[] values = new double[elements+pre+post]; for (int i = pre, end = pre+elements; i < end; ++i) { values[i] = i-pre; } StringWriter sw = new StringWriter(); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); JsonGenerator gen = useBytes ? FACTORY.createGenerator(bytes) : FACTORY.createGenerator(sw); gen.writeArray(values, pre, elements); gen.close(); String json; if (useBytes) { json = bytes.toString("UTF-8"); } else { json = sw.toString(); } JsonParser p = useBytes ? FACTORY.createParser(bytes.toByteArray()) : FACTORY.createParser(json); assertToken(JsonToken.START_ARRAY, p.nextToken()); for (int i = 0; i < elements; ++i) { JsonToken t = p.nextToken(); if (t != JsonToken.VALUE_NUMBER_FLOAT) { fail("Expected floating-point number, got "+t+", element #"+i); } assertEquals((double) i, p.getDoubleValue()); } assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); } } CustomQuoteCharTest.java000066400000000000000000000070251356164247300340610ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.*; import com.fasterxml.jackson.core.*; // For [core#549], ability to use alternate quote characters public class CustomQuoteCharTest extends com.fasterxml.jackson.core.BaseTest { final JsonFactory JSON_F = streamFactoryBuilder() .quoteChar('\'') .build(); // Only ASCII range supported as of 2.10 public void testInvalidQuote() throws Exception { try { streamFactoryBuilder() .quoteChar('\u00A0'); fail("Should not allow quote character outside ASCII range"); } catch (IllegalArgumentException e) { verifyException(e, "Can only use Unicode characters up to 0x7F"); } } public void testBasicAposWithCharBased() throws Exception { StringWriter w; JsonGenerator g; // with Object w = new StringWriter(); g = createGenerator(JSON_F, w); _writeObject(g, "question", "answer"); g.close(); assertEquals("{'question':'answer'}", w.toString()); // with Array w = new StringWriter(); g = createGenerator(JSON_F, w); _writeArray(g, "hello world"); g.close(); assertEquals("['hello world']", w.toString()); } public void testBasicAposWithByteBased() throws Exception { ByteArrayOutputStream out; JsonGenerator g; // with Object out = new ByteArrayOutputStream(); g = createGenerator(JSON_F, out); _writeObject(g, "question", "answer"); g.close(); assertEquals("{'question':'answer'}", out.toString("UTF-8")); // with Array out = new ByteArrayOutputStream(); g = createGenerator(JSON_F, out); _writeArray(g, "hello world"); g.close(); assertEquals("['hello world']", out.toString("UTF-8")); } public void testAposQuotingWithCharBased() throws Exception { StringWriter w; JsonGenerator g; // with Object w = new StringWriter(); g = createGenerator(JSON_F, w); _writeObject(g, "key", "It's \"fun\""); g.close(); // should escape apostrophes but not quotes? assertEquals("{'key':'It\\u0027s \\\"fun\\\"'}", w.toString()); // with Array w = new StringWriter(); g = createGenerator(JSON_F, w); _writeArray(g, "It's a sin"); g.close(); assertEquals("['It\\u0027s a sin']", w.toString()); } public void testAposQuotingWithByteBased() throws Exception { ByteArrayOutputStream out; JsonGenerator g; // with Object out = new ByteArrayOutputStream(); g = createGenerator(JSON_F, out); _writeObject(g, "key", "It's \"fun\""); g.close(); // should escape apostrophes but not quotes? assertEquals("{'key':'It\\u0027s \\\"fun\\\"'}", out.toString("UTF-8")); // with Array out = new ByteArrayOutputStream(); g = createGenerator(JSON_F, out); _writeArray(g, "It's a sin"); g.close(); assertEquals("['It\\u0027s a sin']", out.toString("UTF-8")); } private void _writeObject(JsonGenerator g, String key, String value) throws Exception { g.writeStartObject(); g.writeStringField(key, value); g.writeEndObject(); } private void _writeArray(JsonGenerator g, String value) throws Exception { g.writeStartArray(); g.writeString(value); g.writeEndArray(); } } GeneratorBasicTest.java000066400000000000000000000332021356164247300336570ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import com.fasterxml.jackson.core.*; import java.io.*; /** * Set of basic unit tests for verifying that the basic generator * functionality works as expected. */ public class GeneratorBasicTest extends com.fasterxml.jackson.core.BaseTest { private final JsonFactory JSON_F = new JsonFactory(); // // // First, tests for primitive (non-structured) values public void testStringWrite() throws Exception { String[] inputStrings = new String[] { "", "X", "1234567890" }; for (int useReader = 0; useReader < 2; ++useReader) { for (int writeString = 0; writeString < 2; ++writeString) { for (int strIx = 0; strIx < inputStrings.length; ++strIx) { String input = inputStrings[strIx]; JsonGenerator gen; ByteArrayOutputStream bout = new ByteArrayOutputStream(); if (useReader != 0) { gen = JSON_F.createGenerator(new OutputStreamWriter(bout, "UTF-8")); } else { gen = JSON_F.createGenerator(bout, JsonEncoding.UTF8); } if (writeString > 0) { gen.writeString(input); } else { int len = input.length(); char[] buffer = new char[len + 20]; // Let's use non-zero base offset too... input.getChars(0, len, buffer, strIx); gen.writeString(buffer, strIx, len); } gen.flush(); gen.close(); JsonParser jp = JSON_F.createParser(new ByteArrayInputStream(bout.toByteArray())); JsonToken t = jp.nextToken(); assertNotNull("Document \""+bout.toString("UTF-8")+"\" yielded no tokens", t); assertEquals(JsonToken.VALUE_STRING, t); assertEquals(input, jp.getText()); assertEquals(null, jp.nextToken()); jp.close(); } } } } public void testIntValueWrite() throws Exception { // char[] doTestIntValueWrite(false, false); doTestIntValueWrite(true, false); // byte[] doTestIntValueWrite(false, true); doTestIntValueWrite(true, true); } public void testLongValueWrite() throws Exception { // char[] doTestLongValueWrite(false, false); doTestLongValueWrite(true, false); // byte[] doTestLongValueWrite(false, true); doTestLongValueWrite(true, true); } public void testBooleanWrite() throws Exception { for (int i = 0; i < 4; ++i) { boolean state = (i & 1) == 0; boolean pad = (i & 2) == 0; StringWriter sw = new StringWriter(); JsonGenerator gen = JSON_F.createGenerator(sw); gen.writeBoolean(state); if (pad) { gen.writeRaw(" "); } gen.close(); String docStr = sw.toString(); JsonParser jp = createParserUsingReader(docStr); JsonToken t = jp.nextToken(); String exp = Boolean.valueOf(state).toString(); if (!exp.equals(jp.getText())) { fail("Expected '"+exp+"', got '"+jp.getText()); } assertEquals(state ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE, t); assertEquals(null, jp.nextToken()); jp.close(); } } public void testNullWrite() throws Exception { for (int i = 0; i < 2; ++i) { boolean pad = (i & 1) == 0; StringWriter sw = new StringWriter(); JsonGenerator gen = JSON_F.createGenerator(sw); gen.writeNull(); if (pad) { gen.writeRaw(" "); } gen.close(); String docStr = sw.toString(); JsonParser jp = createParserUsingReader(docStr); JsonToken t = jp.nextToken(); String exp = "null"; if (!exp.equals(jp.getText())) { fail("Expected '"+exp+"', got '"+jp.getText()); } assertEquals(JsonToken.VALUE_NULL, t); assertEquals(null, jp.nextToken()); jp.close(); } } // // Then root-level output testing public void testRootIntsWrite() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = JSON_F.createGenerator(sw); gen.writeNumber(1); gen.writeNumber(2); gen.writeNumber(-13); gen.close(); String docStr = sw.toString(); 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 public void testFieldValueWrites() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = JSON_F.createGenerator(sw); gen.writeStartObject(); gen.writeNumberField("long", 3L); gen.writeNumberField("double", 0.25); gen.writeNumberField("float", -0.25f); gen.writeEndObject(); gen.close(); assertEquals("{\"long\":3,\"double\":0.25,\"float\":-0.25}", sw.toString().trim()); } /** * Test to verify that output context actually contains useful information */ public void testOutputContext() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = JSON_F.createGenerator(sw); JsonStreamContext ctxt = gen.getOutputContext(); assertTrue(ctxt.inRoot()); gen.writeStartObject(); assertTrue(gen.getOutputContext().inObject()); gen.writeFieldName("a"); assertEquals("a", gen.getOutputContext().getCurrentName()); gen.writeStartArray(); assertTrue(gen.getOutputContext().inArray()); gen.writeStartObject(); assertTrue(gen.getOutputContext().inObject()); gen.writeFieldName("b"); ctxt = gen.getOutputContext(); assertEquals("b", ctxt.getCurrentName()); gen.writeNumber(123); assertEquals("b", ctxt.getCurrentName()); gen.writeFieldName("c"); assertEquals("c", gen.getOutputContext().getCurrentName()); gen.writeNumber(5); // assertEquals("c", gen.getOutputContext().getCurrentName()); gen.writeFieldName("d"); assertEquals("d", gen.getOutputContext().getCurrentName()); gen.writeStartArray(); ctxt = gen.getOutputContext(); assertTrue(ctxt.inArray()); assertEquals(0, ctxt.getCurrentIndex()); assertEquals(0, ctxt.getEntryCount()); gen.writeBoolean(true); ctxt = gen.getOutputContext(); assertTrue(ctxt.inArray()); // NOTE: index still refers to currently output entry assertEquals(0, ctxt.getCurrentIndex()); assertEquals(1, ctxt.getEntryCount()); gen.writeNumber(3); ctxt = gen.getOutputContext(); assertTrue(ctxt.inArray()); assertEquals(1, ctxt.getCurrentIndex()); assertEquals(2, ctxt.getEntryCount()); gen.writeEndArray(); assertTrue(gen.getOutputContext().inObject()); gen.writeEndObject(); assertTrue(gen.getOutputContext().inArray()); gen.writeEndArray(); assertTrue(gen.getOutputContext().inObject()); gen.writeEndObject(); assertTrue(gen.getOutputContext().inRoot()); gen.close(); } public void testGetOutputTarget() throws Exception { OutputStream out = new ByteArrayOutputStream(); JsonGenerator gen = JSON_F.createGenerator(out); assertSame(out, gen.getOutputTarget()); gen.close(); StringWriter sw = new StringWriter(); gen = JSON_F.createGenerator(sw); assertSame(sw, gen.getOutputTarget()); gen.close(); } // for [core#195] public void testGetOutputBufferd() throws Exception { OutputStream out = new ByteArrayOutputStream(); JsonGenerator gen = JSON_F.createGenerator(out); _testOutputBuffered(gen); gen.close(); StringWriter sw = new StringWriter(); gen = JSON_F.createGenerator(sw); _testOutputBuffered(gen); gen.close(); } private void _testOutputBuffered(JsonGenerator gen) throws IOException { gen.writeStartArray(); // 1 byte gen.writeNumber(1234); // 4 bytes assertEquals(5, gen.getOutputBuffered()); gen.flush(); assertEquals(0, gen.getOutputBuffered()); gen.writeEndArray(); assertEquals(1, gen.getOutputBuffered()); gen.close(); assertEquals(0, gen.getOutputBuffered()); } /* /********************************************************** /* Internal methods /********************************************************** */ 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, 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]; 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); } 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(p.getText())) { fail("Expected '"+exp+"', got '"+p.getText()); } assertEquals(JsonToken.VALUE_NUMBER_INT, t); assertEquals(VALUE, p.getIntValue()); assertEquals(null, p.nextToken()); p.close(); } } private void doTestLongValueWrite(boolean pad, boolean useBytes) throws Exception { long[] VALUES = new long[] { 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]; 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); } 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(p.getText())) { fail("Expected '"+exp+"', got '"+p.getText()); } assertEquals(JsonToken.VALUE_NUMBER_INT, t); assertEquals(VALUE, p.getLongValue()); assertEquals(null, p.nextToken()); p.close(); } } } GeneratorFailFromReaderTest.java000066400000000000000000000102551356164247300354630ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import java.io.ByteArrayOutputStream; import java.io.OutputStreamWriter; import java.io.StringReader; public class GeneratorFailFromReaderTest extends com.fasterxml.jackson.core.BaseTest { private final JsonFactory F = new JsonFactory(); // [core#177] // Also: should not try writing JSON String if field name expected // (in future maybe take one as alias... but not yet) public void testFailOnWritingStringNotFieldNameBytes() throws Exception { _testFailOnWritingStringNotFieldName(F, false); } // [core#177] public void testFailOnWritingStringNotFieldNameChars() throws Exception { _testFailOnWritingStringNotFieldName(F, true); } public void testFailOnWritingStringFromReaderWithTooFewCharacters() throws Exception { _testFailOnWritingStringFromReaderWithTooFewCharacters(F, true); _testFailOnWritingStringFromReaderWithTooFewCharacters(F, false); } public void testFailOnWritingStringFromNullReader() throws Exception { _testFailOnWritingStringFromNullReader(F, true); _testFailOnWritingStringFromNullReader(F, false); } /* /********************************************************** /* Internal methods /********************************************************** */ private void _testFailOnWritingStringNotFieldName(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); } gen.writeStartObject(); try { StringReader reader = new StringReader("a"); gen.writeString(reader, -1); gen.flush(); String json = bout.toString("UTF-8"); fail("Should not have let "+gen.getClass().getName()+".writeString() be used in place of 'writeFieldName()': output = "+json); } catch (JsonProcessingException e) { verifyException(e, "can not write a String"); } gen.close(); } private void _testFailOnWritingStringFromReaderWithTooFewCharacters(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); } gen.writeStartObject(); try { String testStr = "aaaaaaaaa"; StringReader reader = new StringReader(testStr); gen.writeFieldName("a"); gen.writeString(reader, testStr.length() + 1); gen.flush(); String json = bout.toString("UTF-8"); fail("Should not have let "+gen.getClass().getName()+".writeString() ': output = "+json); } catch (JsonProcessingException e) { verifyException(e, "Didn't read enough from reader"); } gen.close(); } private void _testFailOnWritingStringFromNullReader(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); } gen.writeStartObject(); try { gen.writeFieldName("a"); gen.writeString(null, -1); gen.flush(); String json = bout.toString("UTF-8"); fail("Should not have let "+gen.getClass().getName()+".writeString() ': output = "+json); } catch (JsonProcessingException e) { verifyException(e, "null reader"); } gen.close(); } } GeneratorFailTest.java000066400000000000000000000076431356164247300335230ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.ByteArrayOutputStream; import java.io.OutputStreamWriter; import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; public class GeneratorFailTest extends com.fasterxml.jackson.core.BaseTest { private final JsonFactory F = new JsonFactory(); // [core#167]: no error for writing field name twice public void testDupFieldNameWrites() throws Exception { _testDupFieldNameWrites(F, false); _testDupFieldNameWrites(F, true); } // [core#177] // Also: should not try writing JSON String if field name expected // (in future maybe take one as alias... but not yet) public void testFailOnWritingStringNotFieldNameBytes() throws Exception { _testFailOnWritingStringNotFieldName(F, false); } // [core#177] public void testFailOnWritingStringNotFieldNameChars() throws Exception { _testFailOnWritingStringNotFieldName(F, true); } // for [core#282] public void testFailOnWritingFieldNameInRoot() throws Exception { _testFailOnWritingFieldNameInRoot(F, false); _testFailOnWritingFieldNameInRoot(F, true); } /* /********************************************************** /* Internal methods /********************************************************** */ private void _testDupFieldNameWrites(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); } gen.writeStartObject(); gen.writeFieldName("a"); try { gen.writeFieldName("b"); gen.flush(); String json = bout.toString("UTF-8"); fail("Should not have let two consecutive field name writes succeed: output = "+json); } catch (JsonProcessingException e) { verifyException(e, "can not write a field name, expecting a value"); } gen.close(); } private void _testFailOnWritingStringNotFieldName(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); } gen.writeStartObject(); try { gen.writeString("a"); gen.flush(); String json = bout.toString("UTF-8"); fail("Should not have let "+gen.getClass().getName()+".writeString() be used in place of 'writeFieldName()': output = "+json); } catch (JsonProcessingException e) { verifyException(e, "can not write a String"); } 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(); } } GeneratorFeaturesTest.java000066400000000000000000000273631356164247300344270ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage 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(); @SuppressWarnings("deprecation") 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)); assertFalse(g.isEnabled(StreamWriteFeature.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)); assertTrue(g.isEnabled(StreamWriteFeature.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)); assertFalse(g.isEnabled(StreamWriteFeature.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 = JsonFactory.builder() .disable(JsonWriteFeature.QUOTE_FIELD_NAMES) .build(); _testFieldNameQuoting(f, false); // and (re)enable: f = JsonFactory.builder() .enable(JsonWriteFeature.QUOTE_FIELD_NAMES) .build(); _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 = JsonFactory.builder() .disable(JsonWriteFeature.WRITE_NAN_AS_STRINGS) .build(); _testNonNumericQuoting(f, false); // and (re)enable: f = JsonFactory.builder() .enable(JsonWriteFeature.WRITE_NAN_AS_STRINGS) .build(); _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 = JsonFactory.builder() .enable(JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS) .build(); 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 { BigDecimal ENG = new BigDecimal("1E+2"); JsonFactory f = JsonFactory.builder() .enable(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN) .enable(JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS) .build(); 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] @SuppressWarnings("deprecation") 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 = JsonFactory.builder() .disable(JsonWriteFeature.QUOTE_FIELD_NAMES) .build(); _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}"); } @SuppressWarnings("deprecation") 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); } @SuppressWarnings("deprecation") 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); } } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/JsonFactoryTest.java000066400000000000000000000201541356164247300333110ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import java.io.*; import java.util.Iterator; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.type.ResolvedType; import com.fasterxml.jackson.core.type.TypeReference; public class JsonFactoryTest extends com.fasterxml.jackson.core.BaseTest { private static class BogusCodec extends ObjectCodec { @Override public Version version() { return null; } @Override public T readValue(JsonParser p, Class valueType) throws IOException { return null; } @Override public T readValue(JsonParser p, TypeReference valueTypeRef) throws IOException { return null; } @Override public T readValue(JsonParser p, ResolvedType valueType) throws IOException { return null; } @Override public Iterator readValues(JsonParser p, Class valueType) throws IOException { return null; } @Override public Iterator readValues(JsonParser p, TypeReference valueTypeRef) throws IOException { return null; } @Override public Iterator readValues(JsonParser p, ResolvedType valueType) throws IOException { return null; } @Override public void writeValue(JsonGenerator gen, Object value) throws IOException { } @Override public T readTree(JsonParser p) throws IOException { return null; } @Override public void writeTree(JsonGenerator gen, TreeNode tree) throws IOException { } @Override public TreeNode createObjectNode() { return null; } @Override public TreeNode createArrayNode() { return null; } @Override public JsonParser treeAsTokens(TreeNode n) { return null; } @Override public T treeToValue(TreeNode n, Class valueType) throws JsonProcessingException { return null; } @Override public TreeNode missingNode() { return null; } @Override public TreeNode nullNode() { return null; } } // for testing [core#460] @SuppressWarnings("serial") static class CustomFactory extends JsonFactory { public CustomFactory(JsonFactory f, ObjectCodec codec) { super(f, codec); } } /* /********************************************************************** /* Test methods /********************************************************************** */ @SuppressWarnings("deprecation") public void testGeneratorFeatures() throws Exception { JsonFactory f = new JsonFactory(); assertNull(f.getCodec()); f = JsonFactory.builder() .configure(JsonWriteFeature.QUOTE_FIELD_NAMES, true) .build(); // 24-Oct-2018, tatu: Until 3.x, we'll only have backwards compatible assertTrue(f.isEnabled(JsonGenerator.Feature.QUOTE_FIELD_NAMES)); f = JsonFactory.builder() .configure(JsonWriteFeature.QUOTE_FIELD_NAMES, false) .build(); assertFalse(f.isEnabled(JsonGenerator.Feature.QUOTE_FIELD_NAMES)); } public void testFactoryFeatures() throws Exception { JsonFactory f = JsonFactory.builder() .configure(JsonFactory.Feature.INTERN_FIELD_NAMES, false) .build(); assertFalse(f.isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)); // by default, should be enabled assertTrue(f.isEnabled(JsonFactory.Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING)); } // for [core#189]: verify that it's ok to disable recycling // Basically simply exercises basic functionality, to ensure // there are no obvious problems; needed since testing never // disables this handling otherwise public void testDisablingBufferRecycling() throws Exception { JsonFactory f = JsonFactory.builder() .disable(JsonFactory.Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING) .build(); assertFalse(f.isEnabled(JsonFactory.Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING)); // First, generation for (int i = 0; i < 3; ++i) { StringWriter w = new StringWriter(); JsonGenerator gen = f.createGenerator(w); gen.writeStartObject(); gen.writeEndObject(); gen.close(); assertEquals("{}", w.toString()); } for (int i = 0; i < 3; ++i) { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); JsonGenerator gen = f.createGenerator(bytes); gen.writeStartArray(); gen.writeEndArray(); gen.close(); assertEquals("[]", bytes.toString("UTF-8")); } // Then parsing: for (int i = 0; i < 3; ++i) { JsonParser p = f.createParser("{}"); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertNull(p.nextToken()); p.close(); p = f.createParser("{}".getBytes("UTF-8")); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertNull(p.nextToken()); p.close(); } } public void testJsonWithFiles() throws Exception { File file = File.createTempFile("jackson-test", null); file.deleteOnExit(); JsonFactory f = new JsonFactory(); // First: create file via generator.. and use an odd encoding JsonGenerator jg = f.createGenerator(file, JsonEncoding.UTF16_LE); jg.writeStartObject(); jg.writeRaw(" "); jg.writeEndObject(); jg.close(); // Ok: first read file directly JsonParser jp = f.createParser(file); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); assertNull(jp.nextToken()); jp.close(); // Then via URL: jp = f.createParser(file.toURI().toURL()); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); assertNull(jp.nextToken()); jp.close(); // ok, delete once we are done file.delete(); } // #72 @SuppressWarnings("deprecation") public void testCopy() throws Exception { JsonFactory jf = new JsonFactory(); // first, verify defaults assertNull(jf.getCodec()); assertTrue(jf.isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)); assertFalse(jf.isEnabled(JsonParser.Feature.ALLOW_COMMENTS)); assertFalse(jf.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII)); // then change, verify that changes "stick" jf = JsonFactory.builder() .disable(JsonFactory.Feature.INTERN_FIELD_NAMES) .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) .enable(JsonWriteFeature.ESCAPE_NON_ASCII) .build(); ObjectCodec codec = new BogusCodec(); jf.setCodec(codec); assertFalse(jf.isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)); assertTrue(jf.isEnabled(JsonParser.Feature.ALLOW_COMMENTS)); assertTrue(jf.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII)); assertSame(codec, jf.getCodec()); JsonFactory jf2 = jf.copy(); assertFalse(jf2.isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)); assertTrue(jf2.isEnabled(JsonParser.Feature.ALLOW_COMMENTS)); assertTrue(jf2.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII)); // 16-May-2018, tatu: But! Note that despited [core#460], this should NOT copy it back assertNull(jf2.getCodec()); // However: real copy constructor SHOULD copy it JsonFactory jf3 = new CustomFactory(jf, codec); assertSame(codec, jf3.getCodec()); } } JsonParserClosedCaseTest.java000066400000000000000000000051431356164247300350060ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.*; import java.util.ArrayList; import java.util.Collection; import java.util.List; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.io.SerializedString; import com.fasterxml.jackson.core.testsupport.MockDataInput; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; /** * Tests asserts that using closed `JsonParser` doesn't cause ArrayIndexOutOfBoundsException * with `nextXxx()` methods but returns `null` as expected. */ @RunWith(Parameterized.class) public class JsonParserClosedCaseTest { private static final JsonFactory JSON_F = new JsonFactory(); private JsonParser parser; /** * Creates a list of parsers to tests. * * @return List of Object[2]. Object[0] is is the name of the class, Object[1] is instance itself. * @throws IOException when closing stream fails. */ @Parameters(name = "{0}") public static Collection parsers() throws IOException { ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[] { '{', '}' }); return closeParsers( (ReaderBasedJsonParser) JSON_F.createParser(new InputStreamReader(inputStream)), (UTF8StreamJsonParser) JSON_F.createParser(inputStream), (UTF8DataInputJsonParser) JSON_F.createParser(new MockDataInput("{}")) ); } public JsonParserClosedCaseTest(String parserName, JsonParser parser) { this.parser = parser; } @Test public void testNullReturnedOnClosedParserOnNextFieldName() throws Exception { Assert.assertNull(parser.nextFieldName()); } @Test public void testFalseReturnedOnClosedParserOnNextFieldNameSerializedString() throws Exception { Assert.assertFalse(parser.nextFieldName(new SerializedString(""))); } @Test public void testNullReturnedOnClosedParserOnNextToken() throws Exception { Assert.assertNull(parser.nextToken()); } @Test public void testNullReturnedOnClosedParserOnNextValue() throws Exception { Assert.assertNull(parser.nextValue()); } private static Collection closeParsers(JsonParser... parsersToClose) throws IOException { List list = new ArrayList(); for (JsonParser p : parsersToClose) { p.close(); list.add(new Object[] { p.getClass().getSimpleName(), p }); } return list; } } JsonReadContextTest.java000066400000000000000000000040701356164247300340420ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.Test; import static org.junit.Assert.*; /** * Unit tests for class {@link JsonReadContext}. * * @date 2017-08-01 * @see JsonReadContext * **/ public class JsonReadContextTest{ @Test(expected = JsonParseException.class) public void testSetCurrentNameTwiceWithSameNameRaisesJsonParseException() throws JsonProcessingException { DupDetector dupDetector = DupDetector.rootDetector((JsonGenerator) null); JsonReadContext jsonReadContext = new JsonReadContext((JsonReadContext) null, dupDetector, 2441, 2441, 2441); jsonReadContext.setCurrentName("4'Du>icate field'"); jsonReadContext.setCurrentName("4'Du>icate field'"); } @Test public void testSetCurrentName() throws JsonProcessingException { JsonReadContext jsonReadContext = JsonReadContext.createRootContext(0, 0, (DupDetector) null); jsonReadContext.setCurrentName("asd / \" € < - _"); assertEquals("asd / \" € < - _", jsonReadContext.getCurrentName()); jsonReadContext.setCurrentName(null); assertNull(jsonReadContext.getCurrentName()); } @Test public void testReset() { DupDetector dupDetector = DupDetector.rootDetector((JsonGenerator) null); JsonReadContext jsonReadContext = JsonReadContext.createRootContext(dupDetector); assertTrue(jsonReadContext.inRoot()); assertEquals("root", jsonReadContext.typeDesc()); assertEquals(1, jsonReadContext.getStartLocation(jsonReadContext).getLineNr()); assertEquals(0, jsonReadContext.getStartLocation(jsonReadContext).getColumnNr()); jsonReadContext.reset(200, 500, 200); assertFalse(jsonReadContext.inRoot()); assertEquals("?", jsonReadContext.typeDesc()); assertEquals(500, jsonReadContext.getStartLocation(jsonReadContext).getLineNr()); assertEquals(200, jsonReadContext.getStartLocation(jsonReadContext).getColumnNr()); } }ParserSequenceTest.java000066400000000000000000000111071356164247300337140ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.util.JsonParserSequence; @SuppressWarnings("resource") public class ParserSequenceTest extends com.fasterxml.jackson.core.BaseTest { public void testSimple() throws Exception { JsonParser p1 = JSON_FACTORY.createParser("[ 1 ]"); JsonParser p2 = JSON_FACTORY.createParser("[ 2 ]"); JsonParserSequence seq = JsonParserSequence.createFlattened(false, p1, p2); assertEquals(2, seq.containedParsersCount()); assertFalse(p1.isClosed()); assertFalse(p2.isClosed()); assertFalse(seq.isClosed()); assertToken(JsonToken.START_ARRAY, seq.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken()); assertEquals(1, seq.getIntValue()); assertToken(JsonToken.END_ARRAY, seq.nextToken()); assertFalse(p1.isClosed()); assertFalse(p2.isClosed()); assertFalse(seq.isClosed()); assertToken(JsonToken.START_ARRAY, seq.nextToken()); // first parser ought to be closed now assertTrue(p1.isClosed()); assertFalse(p2.isClosed()); assertFalse(seq.isClosed()); assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken()); assertEquals(2, seq.getIntValue()); assertToken(JsonToken.END_ARRAY, seq.nextToken()); assertTrue(p1.isClosed()); assertFalse(p2.isClosed()); assertFalse(seq.isClosed()); assertNull(seq.nextToken()); assertTrue(p1.isClosed()); assertTrue(p2.isClosed()); assertTrue(seq.isClosed()); seq.close(); } public void testMultiLevel() throws Exception { JsonParser p1 = JSON_FACTORY.createParser("[ 1 ] "); JsonParser p2 = JSON_FACTORY.createParser(" 5"); JsonParser p3 = JSON_FACTORY.createParser(" { } "); JsonParserSequence seq1 = JsonParserSequence.createFlattened(true, p1, p2); JsonParserSequence seq = JsonParserSequence.createFlattened(false, seq1, p3); assertEquals(3, seq.containedParsersCount()); assertToken(JsonToken.START_ARRAY, seq.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken()); assertToken(JsonToken.END_ARRAY, seq.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, seq.nextToken()); assertToken(JsonToken.START_OBJECT, seq.nextToken()); assertToken(JsonToken.END_OBJECT, seq.nextToken()); assertNull(seq.nextToken()); assertTrue(p1.isClosed()); assertTrue(p2.isClosed()); assertTrue(p3.isClosed()); assertTrue(seq.isClosed()); } // for [jackson-core#296] public void testInitializationDisabled() throws Exception { // // 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(); } } RawValueWithSurrogatesTest.java000066400000000000000000000237741356164247300354450ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.ByteArrayOutputStream; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; public class RawValueWithSurrogatesTest extends com.fasterxml.jackson.core.BaseTest { final String SURROGATES_307; { // This one fails: String msg ="{'xxxxxxx':{'xxxx':'xxxxxxxxx','xx':'xxxxxxxxxxxxxxxxxxx','xxxxxxxxx':'xxxx://xxxxxxx.xxx'," +"'xxxxxx':{'xxxx':'xxxxxxxxxxx','xxxxxxxx':{'xxxxxxxxxxx':'xx','xxxxxxxxxx':'xx-xx'}}," +"'xxxxx':[{'xxxx':'xxxx','xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍'}]}," +"'xxxxxxxxxxx':[{'xxxxxxx':'xxxxxx','xxxxxxxx':[{'xxxxxx':x,'xxxxxx':xxx," +"'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍'," +"'xxxxxx':{'xxxxxx':xx,'xxxxxx':x,'xxxx':'xxxxx'}}," +"{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍'" +",'xxxxxx':{'xxxxxx':xxx,'xxxxxx':x,'xxxx':'xxxxx'}}]},{'xxxxxxxxxxx':'xxxxxx','xxxxxxxx':[{'xxxxxx':x,'xxxxxx':xxx," +"'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍'," +"'xxxxxx':{'xxxxxx':xx,'xxxxxx':x,'xxxx':'xxxxx'}}," +"{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍'," +"'xxxxxx':{'xxxxxx':xxx,'xxxxxx':x,'xxxx':'xxxxx'}}]},{'xxxxxxxxxxx':'xxxxxx','xxxxxxxx':[{'xxxxxx':x,'xxxxxx':xxx," +"'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍'," +"'xxxxxx':{'xxxxxx':xx,'xxxxxx':x,'xxxx':'xxxxx'}},{'xxxxxx':x,'xxxxxx':xxx," +"'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍'," +"'xxxxxx':{'xxxxxx':xxx,'xxxxxx':x,'xxxx':'xxxxx'}}]},{'xxxxxxxxxxx':'xxxxxx','xxxxxxxx':[{'xxxxxx':x,'xxxxxx':xxx," +"'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍'," +"'xxxxxx':{'xxxxxx':xx,'xxxxxx':x,'xxxx':'xxxxx'}},{'xxxxxx':x,'xxxxxx':xxx," +"'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍'," +"'xxxxxx':{'xxxxxx':xxx,'xxxxxx':x,'xxxx':'xxxxx'}}]},{'xxxxxxxxxxx':'xxxxxx','xxxxxxxx':" +"[{'xxxxxx':3,'xxxxxx':123,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍'," +"'xxxxxx':{'xxxxxx':24,'xxxxxx':4,'xxxx':'xxxxx'}},{'xxxxxx':0,'xxxxxx':123," +"'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍'," +"'xxxxxx':{'xxxxxx':123,'xxxxxx':1,'xxxx':'xxxxx'}}]},{'xxxxxxxxxxx':'xxxxxx','xxxxxxxx':[{'xxxxxx':1,'xxxxxx':123," +"'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍','xxxxxx':{'xxxxxx':xx,'xxxxxx':x,'xxxx':'xxxxx'}}," +"{'xxxxxx':x,'xxxxxx':123,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍'," +"'xxxxxx':{'xxxxxx':xxx,'xxxxxx':x,'xxxx':'xxxxx'}}]},{'xxxxxxxxxxx':'xxxxx','xxxxxxxx':[{'xxxxxx':x,'xxxxxx':xxx," +"'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍'," +"'xxxxxx':{'xxxxxx':xx,'xxxxxx':x,'xxxx':'xxxxx'}},{'xxxxxx':x,'xxxxxx':xxx," +"'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍'," +"'xxxxxx':{'xxxxxx':xxx,'xxxxxx':x,'xxxx':'xxxxx'}}]}]}"; // This one works: // String msg ="{'xxx':{'xxxx':'xxxxxxxxx','xx':'xxxxxxxxxxxxxxxxxxx','xxxxxxxxx':'xxxx://xxxxxxx.xxx','xxxxxx':{'xxxx':'xxxxxxxxxxx','xxxxxxxx':{'xxxxxxxxxxx':'xx','xxxxxxxxxx':'xx-xx'}},'xxxxx':[{'xxxx':'xxxx','xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍'}]},'xxxxxxxxxxx':[{'xxxxxxx':'xxxxxx','xxxxxxxx':[{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍','xxxxxx':{'xxxxxx':xx,'xxxxxx':x,'xxxx':'xxxxx'}},{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍','xxxxxx':{'xxxxxx':xxx,'xxxxxx':x,'xxxx':'xxxxx'}}]},{'xxxxxxxxxxx':'xxxxxx','xxxxxxxx':[{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍','xxxxxx':{'xxxxxx':xx,'xxxxxx':x,'xxxx':'xxxxx'}},{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍','xxxxxx':{'xxxxxx':xxx,'xxxxxx':x,'xxxx':'xxxxx'}}]},{'xxxxxxxxxxx':'xxxxxx','xxxxxxxx':[{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍','xxxxxx':{'xxxxxx':xx,'xxxxxx':x,'xxxx':'xxxxx'}},{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍','xxxxxx':{'xxxxxx':xxx,'xxxxxx':x,'xxxx':'xxxxx'}}]},{'xxxxxxxxxxx':'xxxxxx','xxxxxxxx':[{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍','xxxxxx':{'xxxxxx':xx,'xxxxxx':x,'xxxx':'xxxxx'}},{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍','xxxxxx':{'xxxxxx':xxx,'xxxxxx':x,'xxxx':'xxxxx'}}]},{'xxxxxxxxxxx':'xxxxxx','xxxxxxxx':[{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍','xxxxxx':{'xxxxxx':xx,'xxxxxx':x,'xxxx':'xxxxx'}},{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍','xxxxxx':{'xxxxxx':xxx,'xxxxxx':x,'xxxx':'xxxxx'}}]},{'xxxxxxxxxxx':'xxxxxx','xxxxxxxx':[{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍','xxxxxx':{'xxxxxx':xx,'xxxxxx':x,'xxxx':'xxxxx'}},{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍','xxxxxx':{'xxxxxx':xxx,'xxxxxx':x,'xxxx':'xxxxx'}}]},{'xxxxxxxxxxx':'xxxxx','xxxxxxxx':[{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍','xxxxxx':{'xxxxxx':xx,'xxxxxx':x,'xxxx':'xxxxx'}},{'xxxxxx':x,'xxxxxx':xxx,'xxxx':'xx xxxxxxxxxxx: xxxxxxx xxxxxx xxxxxxxxxxxxx xxxxx xxxxxx. xxxxx xxxxxx xxxxx xxxxx. xx xxx xx xxxx xxx xxxx. xxxx xxxxx xxx xxxxxxxx xxxxx xxxxxx xxxxxxx😆👍','xxxxxx':{'xxxxxx':xxx,'xxxxxx':x,'xxxx':'xxxxx'}}]}]}"; SURROGATES_307 = aposToQuotes(msg); } private final JsonFactory JSON_F = new JsonFactory(); // for [jackson-core#307] public void testRawWithSurrogatesString() throws Exception { _testRawWithSurrogatesString(false); } // for [jackson-core#307] public void testRawWithSurrogatesCharArray() throws Exception { _testRawWithSurrogatesString(true); } private void _testRawWithSurrogatesString(boolean useCharArray) throws Exception { // boundaries are not exact, may vary, so use this: final int OFFSET = 3; final int COUNT = 100; for (int i = OFFSET; i < COUNT; ++i) { StringBuilder sb = new StringBuilder(1000); for (int j = 0; j < i; ++j) { sb.append(' '); } sb.append(SURROGATES_307); final String text = sb.toString(); ByteArrayOutputStream out = new ByteArrayOutputStream(1000); JsonGenerator g = JSON_F.createGenerator(out); if (useCharArray) { char[] ch = text.toCharArray(); g.writeRawValue(ch, OFFSET, ch.length - OFFSET); } else { g.writeRawValue(text, OFFSET, text.length() - OFFSET); } g.close(); byte[] b = out.toByteArray(); assertNotNull(b); } } } RequestPayloadOnExceptionTest.java000066400000000000000000000143171356164247300361130ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage 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(); } } StringGenerationFromReaderTest.java000066400000000000000000000244511356164247300362260ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import com.fasterxml.jackson.core.*; import java.io.*; import java.util.Random; /** * Set of basic unit tests for verifying that the string * generation, including character escaping, works as expected. */ public class StringGenerationFromReaderTest extends BaseTest { final static String[] SAMPLES = new String[] { "\"test\"", "\n", "\\n", "\r\n", "a\\b", "tab:\nok?", "a\tb\tc\n\fdef\t \tg\"\"\"h\"\\ijklmn\b", "\"\"\"", "\\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 { doTestBasicEscaping(); } // for [core#194] public void testMediumStringsBytes() throws Exception { 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 { for (int mode : ALL_TEXT_MODES) { for (int size : new int[] { 1100, 2300, 3800, 7500, 19000 }) { _testMediumStrings(mode, size); } } } public void testLongerRandomSingleChunk() throws Exception { /* Let's first generate 100k of pseudo-random characters, favoring * 7-bit ascii range */ for (int mode : ALL_TEXT_MODES) { for (int round = 0; round < 80; ++round) { String content = generateRandom(75000+round); _testLongerRandom(mode, content); } } } public void testLongerRandomMultiChunk() throws Exception { /* Let's first generate 100k of pseudo-random characters, favoring * 7-bit ascii range */ for (int mode : ALL_TEXT_MODES) { for (int round = 0; round < 70; ++round) { String content = generateRandom(73000+round); _testLongerRandomMulti(mode, content, round); } } } /* /********************************************************** /* Internal methods /********************************************************** */ private String _generareMediumText(int minLen) { StringBuilder sb = new StringBuilder(minLen + 1000); Random rnd = new Random(minLen); do { switch (rnd.nextInt() % 4) { case 0: sb.append(" foo"); break; case 1: sb.append(" bar"); break; case 2: sb.append(String.valueOf(sb.length())); break; default: sb.append(" \"stuff\""); break; } } while (sb.length() < minLen); return sb.toString(); } private String generateRandom(int len) { StringBuilder sb = new StringBuilder(len+1000); // pad for surrogates Random r = new Random(len); for (int i = 0; i < len; ++i) { if (r.nextBoolean()) { // non-ascii int value = r.nextInt() & 0xFFFF; // Otherwise easy, except that need to ensure that // surrogates are properly paired: and, also // their values do not exceed 0x10FFFF if (value >= 0xD800 && value <= 0xDFFF) { // Let's discard first value, then, and produce valid pair int fullValue = (r.nextInt() & 0xFFFFF); sb.append((char) (0xD800 + (fullValue >> 10))); value = 0xDC00 + (fullValue & 0x3FF); } sb.append((char) value); } else { // ascii sb.append((char) (r.nextInt() & 0x7F)); } } return sb.toString(); } private void _testMediumStrings(int readMode, int length) throws Exception { String text = _generareMediumText(length); StringWriter sw = new StringWriter(); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); JsonGenerator gen = (readMode != MODE_READER) ? FACTORY.createGenerator(bytes) : FACTORY.createGenerator(sw); gen.writeStartArray(); StringReader reader = new StringReader(text); gen.writeString(reader, -1); gen.writeEndArray(); gen.close(); JsonParser p; if (readMode == MODE_READER) { p = FACTORY.createParser(sw.toString()); } else { p = createParser(FACTORY, readMode, bytes.toByteArray()); } assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals(text, p.getText()); assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); } private void doTestBasicEscaping() throws Exception { for (int i = 0; i < SAMPLES.length; ++i) { String VALUE = SAMPLES[i]; StringWriter sw = new StringWriter(); JsonGenerator gen = FACTORY.createGenerator(sw); gen.writeStartArray(); StringReader reader = new StringReader(VALUE); gen.writeString(reader, -1); gen.writeEndArray(); gen.close(); String docStr = sw.toString(); JsonParser p = createParserUsingReader(docStr); assertEquals(JsonToken.START_ARRAY, p.nextToken()); JsonToken t = p.nextToken(); assertEquals(JsonToken.VALUE_STRING, t); assertEquals(VALUE, p.getText()); assertEquals(JsonToken.END_ARRAY, p.nextToken()); assertEquals(null, p.nextToken()); p.close(); } } private void _testLongerRandom(int readMode, String text) throws Exception { ByteArrayOutputStream bow = new ByteArrayOutputStream(text.length()); JsonGenerator gen = FACTORY.createGenerator(bow, JsonEncoding.UTF8); gen.writeStartArray(); StringReader reader = new StringReader(text); gen.writeString(reader, -1); gen.writeEndArray(); gen.close(); byte[] docData = bow.toByteArray(); JsonParser p = createParser(FACTORY, readMode, docData); assertEquals(JsonToken.START_ARRAY, p.nextToken()); JsonToken t = p.nextToken(); assertEquals(JsonToken.VALUE_STRING, t); String act = p.getText(); if (!text.equals(act)) { if (text.length() != act.length()) { fail("Expected string length "+text.length()+", actual "+act.length()); } int i = 0; for (int len = text.length(); i < len; ++i) { if (text.charAt(i) != act.charAt(i)) { break; } } 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, p.nextToken()); p.close(); } private void _testLongerRandomMulti(int readMode, String text, int round) throws Exception { ByteArrayOutputStream bow = new ByteArrayOutputStream(text.length()); JsonGenerator gen = FACTORY.createGenerator(bow, JsonEncoding.UTF8); gen.writeStartArray(); StringReader reader = new StringReader(text); gen.writeString(reader, -1); gen.writeEndArray(); gen.close(); gen = FACTORY.createGenerator(bow, JsonEncoding.UTF8); gen.writeStartArray(); gen.writeStartArray(); Random rnd = new Random(text.length()); int offset = 0; while (offset < text.length()) { int shift = 1 + ((rnd.nextInt() & 0xFFFFF) % 12); // 1 - 12 int len = (1 << shift) + shift; // up to 4k if ((offset + len) >= text.length()) { len = text.length() - offset; } else { // Need to avoid splitting surrogates though char c = text.charAt(offset+len-1); if (c >= 0xD800 && c < 0xDC00) { ++len; } } reader = new StringReader(text.substring(offset, offset+len)); gen.writeString(reader, -1); offset += len; } gen.writeEndArray(); gen.close(); byte[] docData = bow.toByteArray(); JsonParser p = createParser(FACTORY, readMode, docData); assertEquals(JsonToken.START_ARRAY, p.nextToken()); offset = 0; while (p.nextToken() == JsonToken.VALUE_STRING) { // Let's verify, piece by piece 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); } if (!act.equals(exp)) { int i = 0; while (act.charAt(i) == exp.charAt(i)) { ++i; } fail("String segment ["+offset+" - "+(offset+act.length())+"[ different at offset #"+i +"; exp char 0x"+Integer.toHexString(exp.charAt(i)) +", actual 0x"+Integer.toHexString(act.charAt(i))); } offset += act.length(); } assertEquals(JsonToken.END_ARRAY, p.currentToken()); p.close(); } // [jackson-core#556] public void testIssue556() throws Exception { StringBuilder sb = new StringBuilder(8000); sb.append('"'); for (int i = 0; i < 7988; i++) { sb.append("a"); } sb.append('"'); JsonGenerator g = FACTORY.createGenerator(new ByteArrayOutputStream()); g.writeStartArray(); _write556(g, sb.toString()); _write556(g, "b"); _write556(g, "c"); g.writeEndArray(); g.close(); } private void _write556(JsonGenerator g, String value) throws Exception { g.writeString(new StringReader(value), -1); } } StringGenerationTest.java000066400000000000000000000243641356164247300342620ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.*; import com.fasterxml.jackson.core.*; import java.util.Random; /** * Set of basic unit tests for verifying that the string * generation, including character escaping, works as expected. */ public class StringGenerationTest extends BaseTest { final static String[] SAMPLES = new String[] { "\"test\"", "\n", "\\n", "\r\n", "a\\b", "tab:\nok?", "a\tb\tc\n\fdef\t \tg\"\"\"h\"\\ijklmn\b", "\"\"\"", "\\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 { doTestBasicEscaping(false); doTestBasicEscaping(true); } // for [core#194] public void testMediumStringsBytes() throws Exception { 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 { for (int mode : ALL_TEXT_MODES) { for (int size : new int[] { 1100, 2300, 3800, 7500, 19000 }) { _testMediumStrings(mode, size); } } } public void testLongerRandomSingleChunk() throws Exception { /* Let's first generate 100k of pseudo-random characters, favoring * 7-bit ascii range */ 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); } } } public void testLongerRandomMultiChunk() throws Exception { /* Let's first generate 100k of pseudo-random characters, favoring * 7-bit ascii range */ 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); } } } /* /********************************************************** /* Internal methods /********************************************************** */ private String _generareMediumText(int minLen) { StringBuilder sb = new StringBuilder(minLen + 1000); Random rnd = new Random(minLen); do { switch (rnd.nextInt() % 4) { case 0: sb.append(" foo"); break; case 1: sb.append(" bar"); break; case 2: sb.append(String.valueOf(sb.length())); break; default: sb.append(" \"stuff\""); break; } } while (sb.length() < minLen); return sb.toString(); } private String generateRandom(int len) { StringBuilder sb = new StringBuilder(len+1000); // pad for surrogates Random r = new Random(len); for (int i = 0; i < len; ++i) { if (r.nextBoolean()) { // non-ascii int value = r.nextInt() & 0xFFFF; // Otherwise easy, except that need to ensure that // surrogates are properly paired: and, also // their values do not exceed 0x10FFFF if (value >= 0xD800 && value <= 0xDFFF) { // Let's discard first value, then, and produce valid pair int fullValue = (r.nextInt() & 0xFFFFF); sb.append((char) (0xD800 + (fullValue >> 10))); value = 0xDC00 + (fullValue & 0x3FF); } sb.append((char) value); } else { // ascii sb.append((char) (r.nextInt() & 0x7F)); } } return sb.toString(); } private void _testMediumStrings(int readMode, int length) throws Exception { String text = _generareMediumText(length); StringWriter sw = new StringWriter(); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); JsonGenerator gen = (readMode != MODE_READER) ? FACTORY.createGenerator(bytes) : FACTORY.createGenerator(sw); gen.writeStartArray(); gen.writeString(text); gen.writeEndArray(); gen.close(); JsonParser p; if (readMode == MODE_READER) { p = FACTORY.createParser(sw.toString()); } else { p = createParser(FACTORY, readMode, bytes.toByteArray()); } assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals(text, p.getText()); assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); } private void doTestBasicEscaping(boolean charArray) throws Exception { for (int i = 0; i < SAMPLES.length; ++i) { String VALUE = SAMPLES[i]; StringWriter sw = new StringWriter(); JsonGenerator gen = FACTORY.createGenerator(sw); gen.writeStartArray(); if (charArray) { char[] buf = new char[VALUE.length() + i]; VALUE.getChars(0, VALUE.length(), buf, i); gen.writeString(buf, i, VALUE.length()); } else { gen.writeString(VALUE); } gen.writeEndArray(); gen.close(); String docStr = sw.toString(); JsonParser p = createParserUsingReader(docStr); assertEquals(JsonToken.START_ARRAY, p.nextToken()); JsonToken t = p.nextToken(); assertEquals(JsonToken.VALUE_STRING, t); assertEquals(VALUE, p.getText()); assertEquals(JsonToken.END_ARRAY, p.nextToken()); assertEquals(null, p.nextToken()); p.close(); } } private void _testLongerRandom(int readMode, String text, boolean charArray) throws Exception { ByteArrayOutputStream bow = new ByteArrayOutputStream(text.length()); JsonGenerator gen = FACTORY.createGenerator(bow, JsonEncoding.UTF8); gen.writeStartArray(); if (charArray) { char[] buf = new char[text.length()]; text.getChars(0, text.length(), buf, 0); gen.writeString(buf, 0, text.length()); } else { gen.writeString(text); } gen.writeEndArray(); gen.close(); byte[] docData = bow.toByteArray(); JsonParser p = createParser(FACTORY, readMode, docData); assertEquals(JsonToken.START_ARRAY, p.nextToken()); JsonToken t = p.nextToken(); assertEquals(JsonToken.VALUE_STRING, t); String act = p.getText(); if (!text.equals(act)) { if (text.length() != act.length()) { fail("Expected string length "+text.length()+", actual "+act.length()); } int i = 0; for (int len = text.length(); i < len; ++i) { if (text.charAt(i) != act.charAt(i)) { break; } } 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, p.nextToken()); p.close(); } private void _testLongerRandomMulti(int readMode, String text, boolean charArray, int round) throws Exception { ByteArrayOutputStream bow = new ByteArrayOutputStream(text.length()); JsonGenerator gen = FACTORY.createGenerator(bow, JsonEncoding.UTF8); gen.writeStartArray(); gen.writeString(text); gen.writeEndArray(); gen.close(); gen = FACTORY.createGenerator(bow, JsonEncoding.UTF8); gen.writeStartArray(); gen.writeStartArray(); Random rnd = new Random(text.length()); int offset = 0; while (offset < text.length()) { int shift = 1 + ((rnd.nextInt() & 0xFFFFF) % 12); // 1 - 12 int len = (1 << shift) + shift; // up to 4k if ((offset + len) >= text.length()) { len = text.length() - offset; } else { // Need to avoid splitting surrogates though char c = text.charAt(offset+len-1); if (c >= 0xD800 && c < 0xDC00) { ++len; } } if (charArray) { char[] buf = new char[len]; text.getChars(offset, offset+len, buf, 0); gen.writeString(buf, 0, len); } else { gen.writeString(text.substring(offset, offset+len)); } offset += len; } gen.writeEndArray(); gen.close(); byte[] docData = bow.toByteArray(); JsonParser p = createParser(FACTORY, readMode, docData); assertEquals(JsonToken.START_ARRAY, p.nextToken()); offset = 0; while (p.nextToken() == JsonToken.VALUE_STRING) { // Let's verify, piece by piece 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); } if (!act.equals(exp)) { int i = 0; while (act.charAt(i) == exp.charAt(i)) { ++i; } fail("String segment ["+offset+" - "+(offset+act.length())+"[ different at offset #"+i +"; exp char 0x"+Integer.toHexString(exp.charAt(i)) +", actual 0x"+Integer.toHexString(act.charAt(i))); } offset += act.length(); } assertEquals(JsonToken.END_ARRAY, p.currentToken()); p.close(); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/TestCharEscaping.java000066400000000000000000000170141356164247300334000ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.CharacterEscapes; /** * Set of basic unit tests for verifying that the basic parser * functionality works as expected. */ public class TestCharEscaping extends com.fasterxml.jackson.core.BaseTest { @SuppressWarnings("serial") private final static CharacterEscapes ESC_627 = new CharacterEscapes() { final int[] ascii = CharacterEscapes.standardAsciiEscapesForJSON(); { ascii['<'] = CharacterEscapes.ESCAPE_STANDARD; ascii['>'] = CharacterEscapes.ESCAPE_STANDARD; } @Override public int[] getEscapeCodesForAscii() { return ascii; } @Override public SerializableString getEscapeSequence(int ch) { throw new UnsupportedOperationException("Not implemented for test"); } }; /* /********************************************************** /* Unit tests /********************************************************** */ private final static JsonFactory JSON_F = new JsonFactory(); public void testMissingEscaping() throws Exception { // Invalid: control chars, including lf, must be escaped final String DOC = "[" +"\"Linefeed: \n.\"" +"]"; JsonParser jp = createParserUsingReader(DOC); assertToken(JsonToken.START_ARRAY, jp.nextToken()); try { // This may or may not trigger exception JsonToken t = jp.nextToken(); assertToken(JsonToken.VALUE_STRING, t); // and if not, should get it here: jp.getText(); fail("Expected an exception for un-escaped linefeed in string value"); } catch (JsonParseException jex) { verifyException(jex, "has to be escaped"); } jp.close(); } public void testSimpleEscaping() throws Exception { String DOC = "[" +"\"LF=\\n\"" +"]"; JsonParser jp = createParserUsingReader(DOC); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("LF=\n", jp.getText()); jp.close(); /* Note: must split Strings, so that javac won't try to handle * escape and inline null char */ DOC = "[\"NULL:\\u0000!\"]"; jp = createParserUsingReader(DOC); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("NULL:\0!", jp.getText()); jp.close(); // Then just a single char escaping jp = createParserUsingReader("[\"\\u0123\"]"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("\u0123", jp.getText()); jp.close(); // And then double sequence jp = createParserUsingReader("[\"\\u0041\\u0043\"]"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("AC", jp.getText()); jp.close(); } public void testInvalid() throws Exception { // 2-char sequences not allowed: String DOC = "[\"\\u41=A\"]"; JsonParser jp = createParserUsingReader(DOC); assertToken(JsonToken.START_ARRAY, jp.nextToken()); try { jp.nextToken(); jp.getText(); fail("Expected an exception for unclosed ARRAY"); } catch (JsonParseException jpe) { verifyException(jpe, "for character escape"); } jp.close(); } /** * Test to verify that decoder does not allow 8-digit escapes * (non-BMP characters must be escaped using two 4-digit sequences) */ public void test8DigitSequence() throws Exception { String DOC = "[\"\\u00411234\"]"; JsonParser jp = createParserUsingReader(DOC); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("A1234", jp.getText()); jp.close(); } public void testWriteLongCustomEscapes() throws Exception { JsonFactory jf = new JsonFactory(); jf.setCharacterEscapes(ESC_627); // must set to trigger bug StringBuilder longString = new StringBuilder(); while (longString.length() < 2000) { longString.append("\u65e5\u672c\u8a9e"); } StringWriter writer = new StringWriter(); // must call #createGenerator(Writer), #createGenerator(OutputStream) doesn't trigger bug JsonGenerator jgen = jf.createGenerator(writer); jgen.setHighestNonEscapedChar(127); // must set to trigger bug jgen.writeString(longString.toString()); jgen.close(); } // [jackson-core#116] public void testEscapesForCharArrays() throws Exception { StringWriter writer = new StringWriter(); JsonGenerator jgen = JSON_F.createGenerator(writer); // must call #writeString(char[],int,int) and not #writeString(String) jgen.writeString(new char[] { '\0' }, 0, 1); jgen.close(); assertEquals("\"\\u0000\"", writer.toString()); } // [jackson-core#540] public void testInvalidEscape() throws Exception { JsonParser p = JSON_F.createParser(quote("\\u\u0080...").getBytes("UTF-8")); assertToken(JsonToken.VALUE_STRING, p.nextToken()); // this is where we should get proper exception try { p.getText(); fail("Should not pass"); } catch (JsonParseException e) { verifyException(e, "Unexpected character"); } p.close(); } // [jackson-core#116] public void testEscapeNonLatin1Chars() throws Exception { _testEscapeNonLatin1ViaChars(false); } // [jackson-core#116] public void testEscapeNonLatin1Bytes() throws Exception { _testEscapeNonLatin1ViaChars(true); } private void _testEscapeNonLatin1ViaChars(boolean useBytes) throws Exception { // NOTE! First one is outside latin-1, so escape; second one within, do NOT escape: final String VALUE_IN = "Line\u2028feed, \u00D6l!"; final String VALUE_ESCAPED = "Line\\u2028feed, \u00D6l!"; final JsonFactory DEFAULT_F = new JsonFactory(); // First: with default settings, no auto-escaping _testEscapeNonLatin1(DEFAULT_F, VALUE_IN, VALUE_IN, useBytes); // char // Second: with escaping beyond Latin-1 range final JsonFactory latinF = ((JsonFactoryBuilder)JsonFactory.builder()) .highestNonEscapedChar(255) .build(); _testEscapeNonLatin1(latinF, VALUE_IN, VALUE_ESCAPED, useBytes); } private void _testEscapeNonLatin1(JsonFactory f, String valueIn, String expEncoded, boolean useBytes) throws Exception { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); StringWriter sw = new StringWriter(); final JsonGenerator g = useBytes ? f.createGenerator(bytes, JsonEncoding.UTF8) : f.createGenerator(sw); g.writeStartArray(); g.writeString(valueIn); g.writeEndArray(); g.close(); // Don't parse, as we want to verify actual escaping aspects final String doc = useBytes ? bytes.toString("UTF-8") : sw.toString(); assertEquals("[\""+expEncoded+"\"]", doc); } } TestCustomEscaping.java000066400000000000000000000154711356164247300337230ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.CharacterEscapes; import com.fasterxml.jackson.core.io.SerializedString; public class TestCustomEscaping extends com.fasterxml.jackson.core.BaseTest { final static int TWO_BYTE_ESCAPED = 0x111; final static int THREE_BYTE_ESCAPED = 0x1111; final static SerializedString TWO_BYTE_ESCAPED_STRING = new SerializedString("&111;"); final static SerializedString THREE_BYTE_ESCAPED_STRING = new SerializedString("&1111;"); /* /******************************************************** /* Helper types /******************************************************** */ /** * Trivial simple custom escape definition set. */ @SuppressWarnings("serial") static class MyEscapes extends CharacterEscapes { private final int[] _asciiEscapes; public MyEscapes() { _asciiEscapes = standardAsciiEscapesForJSON(); _asciiEscapes['a'] = 'A'; // to basically give us "\A" _asciiEscapes['b'] = CharacterEscapes.ESCAPE_STANDARD; // too force "\u0062" _asciiEscapes['d'] = CharacterEscapes.ESCAPE_CUSTOM; } @Override public int[] getEscapeCodesForAscii() { return _asciiEscapes; } @Override public SerializableString getEscapeSequence(int ch) { if (ch == 'd') { return new SerializedString("[D]"); } if (ch == TWO_BYTE_ESCAPED) { return TWO_BYTE_ESCAPED_STRING; } if (ch == THREE_BYTE_ESCAPED) { return THREE_BYTE_ESCAPED_STRING; } return null; } } /* /******************************************************** /* Unit tests /******************************************************** */ /** * Test to ensure that it is possible to force escaping * of non-ASCII characters. * Related to [JACKSON-102] */ public void testAboveAsciiEscapeWithReader() throws Exception { _testEscapeAboveAscii(false); // reader } public void testAboveAsciiEscapeWithUTF8Stream() throws Exception { _testEscapeAboveAscii(true); // stream (utf-8) } // // // Tests for [JACKSON-106] public void testEscapeCustomWithReader() throws Exception { _testEscapeCustom(false); // reader } public void testEscapeCustomWithUTF8Stream() throws Exception { _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 /******************************************************** */ @SuppressWarnings({ "resource", "deprecation" }) private void _testEscapeAboveAscii(boolean useStream) throws Exception { JsonFactory f = new JsonFactory(); final String VALUE = "chars: [\u00A0]/[\u1234]"; final String KEY = "fun:\u0088:\u3456"; 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")); } g.writeStartArray(); g.writeString(VALUE); g.writeEndArray(); g.close(); String json = bytes.toString("UTF-8"); assertEquals("["+quote(VALUE)+"]", json); // And then with forced ASCII; first, values bytes = new ByteArrayOutputStream(); if (useStream) { g = f.createGenerator(bytes, JsonEncoding.UTF8); } else { g = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8")); } 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) { g = f.createGenerator(bytes, JsonEncoding.UTF8); } else { g = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8")); } 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); } @SuppressWarnings("resource") private void _testEscapeCustom(boolean useStream) throws Exception { JsonFactory f = new JsonFactory().setCharacterEscapes(new MyEscapes()); 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 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")); } 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); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/TestDecorators.java000066400000000000000000000067101356164247300331570ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.IOContext; import com.fasterxml.jackson.core.io.InputDecorator; import com.fasterxml.jackson.core.io.OutputDecorator; /** * Unit tests to verify that input and output decorators work as * expected * * @since 1.8 */ @SuppressWarnings("serial") public class TestDecorators extends com.fasterxml.jackson.core.BaseTest { /* /********************************************************** /* Helper classes /********************************************************** */ static class SimpleInputDecorator extends InputDecorator { @Override public InputStream decorate(IOContext ctxt, InputStream in) throws IOException { return new ByteArrayInputStream("123".getBytes("UTF-8")); } @Override public InputStream decorate(IOContext ctxt, byte[] src, int offset, int length) throws IOException { return new ByteArrayInputStream("456".getBytes("UTF-8")); } @Override public Reader decorate(IOContext ctxt, Reader src) { return new StringReader("789"); } } static class SimpleOutputDecorator extends OutputDecorator { @Override public OutputStream decorate(IOContext ctxt, OutputStream out) throws IOException { out.write("123".getBytes("UTF-8")); out.flush(); return new ByteArrayOutputStream(); } @Override public Writer decorate(IOContext ctxt, Writer w) throws IOException { w.write("567"); w.flush(); return new StringWriter(); } } /* /********************************************************** /* Unit tests /********************************************************** */ public void testInputDecoration() throws IOException { JsonFactory f = JsonFactory.builder() .inputDecorator(new SimpleInputDecorator()) .build(); JsonParser p; // first test with Reader p = f.createParser(new StringReader("{ }")); // should be overridden; assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(789, p.getIntValue()); p.close(); // similarly with InputStream p = f.createParser(new ByteArrayInputStream("[ ]".getBytes("UTF-8"))); // should be overridden; assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(123, p.getIntValue()); p.close(); // and with raw bytes p = f.createParser("[ ]".getBytes("UTF-8")); // should be overridden; assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(456, p.getIntValue()); p.close(); } public void testOutputDecoration() throws IOException { JsonFactory f = JsonFactory.builder() .outputDecorator(new SimpleOutputDecorator()) .build(); JsonGenerator g; StringWriter sw = new StringWriter(); g = f.createGenerator(sw); g.close(); assertEquals("567", sw.toString()); ByteArrayOutputStream out = new ByteArrayOutputStream(); g = f.createGenerator(out, JsonEncoding.UTF8); g.close(); assertEquals("123", out.toString("UTF-8")); } } TestGeneratorDupHandling.java000066400000000000000000000063611356164247300350410ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.*; import com.fasterxml.jackson.core.*; public class TestGeneratorDupHandling extends com.fasterxml.jackson.core.BaseTest { public void testSimpleDupsEagerlyBytes() throws Exception { _testSimpleDups(true, false, new JsonFactory()); } public void testSimpleDupsEagerlyChars() throws Exception { _testSimpleDups(false, false, new JsonFactory()); } // Testing ability to enable checking after construction of // generator, not just via JsonFactory public void testSimpleDupsLazilyBytes() throws Exception { final JsonFactory f = new JsonFactory(); assertFalse(f.isEnabled(JsonGenerator.Feature.STRICT_DUPLICATE_DETECTION)); _testSimpleDups(true, true, f); } public void testSimpleDupsLazilyChars() throws Exception { final JsonFactory f = new JsonFactory(); _testSimpleDups(false, true, f); } @SuppressWarnings("resource") protected void _testSimpleDups(boolean useStream, boolean lazySetting, JsonFactory f) throws Exception { // First: fine, when not checking if (!lazySetting) { _writeSimple0(_generator(f, useStream), "a"); _writeSimple1(_generator(f, useStream), "b"); } // but not when checking JsonGenerator g1; if (lazySetting) { g1 = _generator(f, useStream); g1.enable(JsonGenerator.Feature.STRICT_DUPLICATE_DETECTION); } else { f.enable(JsonGenerator.Feature.STRICT_DUPLICATE_DETECTION); g1 = _generator(f, useStream); } try { _writeSimple0(g1, "a"); fail("Should have gotten exception"); } catch (JsonGenerationException e) { verifyException(e, "duplicate field 'a'"); } JsonGenerator g2; if (lazySetting) { g2 = _generator(f, useStream); g2.enable(JsonGenerator.Feature.STRICT_DUPLICATE_DETECTION); } else { g2 = _generator(f, useStream); } try { _writeSimple1(g2, "x"); fail("Should have gotten exception"); } catch (JsonGenerationException e) { verifyException(e, "duplicate field 'x'"); } } protected JsonGenerator _generator(JsonFactory f, boolean useStream) throws IOException { return useStream ? f.createGenerator(new ByteArrayOutputStream()) : f.createGenerator(new StringWriter()); } protected void _writeSimple0(JsonGenerator g, String name) throws IOException { g.writeStartObject(); g.writeNumberField(name, 1); g.writeNumberField(name, 2); g.writeEndObject(); g.close(); } protected void _writeSimple1(JsonGenerator g, String name) throws IOException { g.writeStartArray(); g.writeNumber(3); g.writeStartObject(); g.writeNumberField("foo", 1); g.writeNumberField("bar", 1); g.writeNumberField(name, 1); g.writeNumberField("bar2", 1); g.writeNumberField(name, 2); g.writeEndObject(); g.writeEndArray(); g.close(); } } TestGeneratorWithSerializedString.java000066400000000000000000000151421356164247300367570ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.*; import java.util.Random; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.SerializedString; public class TestGeneratorWithSerializedString extends com.fasterxml.jackson.core.BaseTest { final static String NAME_WITH_QUOTES = "\"name\""; final static String NAME_WITH_LATIN1 = "P\u00f6ll\u00f6"; final static String VALUE_WITH_QUOTES = "\"Value\""; final static String VALUE2 = _generateLongName(9000); private final JsonFactory JSON_F = new JsonFactory(); private final SerializedString quotedName = new SerializedString(NAME_WITH_QUOTES); private final SerializedString latin1Name = new SerializedString(NAME_WITH_LATIN1); public void testSimpleFieldNames() throws Exception { // First using char-backed generator StringWriter sw = new StringWriter(); JsonGenerator gen = JSON_F.createGenerator(sw); _writeSimple(gen); gen.close(); String json = sw.toString(); _verifySimple(JSON_F.createParser(json)); // then using UTF-8 ByteArrayOutputStream out = new ByteArrayOutputStream(); gen = JSON_F.createGenerator(out, JsonEncoding.UTF8); _writeSimple(gen); gen.close(); byte[] jsonB = out.toByteArray(); _verifySimple(JSON_F.createParser(jsonB)); } public void testSimpleValues() throws Exception { // First using char-backed generator StringWriter sw = new StringWriter(); JsonGenerator gen = JSON_F.createGenerator(sw); _writeSimpleValues(gen); gen.close(); _verifySimpleValues(JSON_F.createParser(new StringReader(sw.toString()))); // then using UTF-8 ByteArrayOutputStream out = new ByteArrayOutputStream(); gen = JSON_F.createGenerator(out, JsonEncoding.UTF8); _writeSimpleValues(gen); gen.close(); _verifySimpleValues(JSON_F.createParser(new ByteArrayInputStream(out.toByteArray()))); } /* /********************************************************** /* Helper methods /********************************************************** */ private void _writeSimple(JsonGenerator gen) throws Exception { // Let's just write array of 2 objects gen.writeStartArray(); gen.writeStartObject(); gen.writeFieldName(quotedName); gen.writeString("a"); gen.writeFieldName(latin1Name); gen.writeString("b"); gen.writeEndObject(); gen.writeStartObject(); gen.writeFieldName(latin1Name); gen.writeString("c"); gen.writeFieldName(quotedName); gen.writeString("d"); gen.writeEndObject(); gen.writeEndArray(); } private void _writeSimpleValues(JsonGenerator gen) throws Exception { // Let's just write an array of 2 objects gen.writeStartArray(); gen.writeStartObject(); gen.writeFieldName(NAME_WITH_QUOTES); gen.writeString(new SerializedString(VALUE_WITH_QUOTES)); gen.writeFieldName(NAME_WITH_LATIN1); gen.writeString(VALUE2); gen.writeEndObject(); gen.writeStartObject(); gen.writeFieldName(NAME_WITH_LATIN1); gen.writeString(VALUE_WITH_QUOTES); gen.writeFieldName(NAME_WITH_QUOTES); gen.writeString(new SerializedString(VALUE2)); gen.writeEndObject(); gen.writeEndArray(); } private void _verifySimple(JsonParser p) throws Exception { assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals(NAME_WITH_QUOTES, p.getText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("a", p.getText()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals(NAME_WITH_LATIN1, p.getText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("b", p.getText()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals(NAME_WITH_LATIN1, p.getText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("c", p.getText()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals(NAME_WITH_QUOTES, p.getText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("d", p.getText()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertNull(p.nextToken()); } private void _verifySimpleValues(JsonParser p) throws Exception { assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals(NAME_WITH_QUOTES, p.getText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals(VALUE_WITH_QUOTES, p.getText()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals(NAME_WITH_LATIN1, p.getText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals(VALUE2, p.getText()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals(NAME_WITH_LATIN1, p.getText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals(VALUE_WITH_QUOTES, p.getText()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals(NAME_WITH_QUOTES, p.getText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals(VALUE2, p.getText()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertNull(p.nextToken()); } private static String _generateLongName(int minLen) { StringBuilder sb = new StringBuilder(); Random rnd = new Random(123); while (sb.length() < minLen) { int ch = rnd.nextInt(96); if (ch < 32) { // ascii (single byte) sb.append((char) (48 + ch)); } else if (ch < 64) { // 2 byte sb.append((char) (128 + ch)); } else { // 3 byte sb.append((char) (4000 + ch)); } } return sb.toString(); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/TestMaxErrorSize.java000066400000000000000000000057051356164247300334470ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import com.fasterxml.jackson.core.*; /** * Test size of parser error messages */ public class TestMaxErrorSize extends com.fasterxml.jackson.core.BaseTest { private final static int EXPECTED_MAX_TOKEN_LEN = 256; // ParserBase.MAX_ERROR_TOKEN_LENGTH private final JsonFactory JSON_F = new JsonFactory(); public void testLongErrorMessage() throws Exception { _testLongErrorMessage(MODE_INPUT_STREAM); _testLongErrorMessage(MODE_INPUT_STREAM_THROTTLED); } public void testLongErrorMessageReader() throws Exception { _testLongErrorMessage(MODE_READER); } private void _testLongErrorMessage(int mode) throws Exception { final String DOC = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; assertTrue(DOC.length() > 256); JsonParser jp = createParser(JSON_F, mode, DOC); try { jp.nextToken(); fail("Expected an exception for unrecognized token"); } catch (JsonParseException jpe) { String msg = jpe.getMessage(); final String expectedPrefix = "Unrecognized token '"; final String expectedSuffix = "...': was expecting"; verifyException(jpe, expectedPrefix); verifyException(jpe, expectedSuffix); assertTrue(msg.contains(expectedSuffix)); int tokenLen = msg.indexOf (expectedSuffix) - expectedPrefix.length(); assertEquals(EXPECTED_MAX_TOKEN_LEN, tokenLen); } jp.close(); } public void testShortErrorMessage() throws Exception { _testShortErrorMessage(MODE_INPUT_STREAM); _testShortErrorMessage(MODE_INPUT_STREAM_THROTTLED); _testShortErrorMessage(MODE_READER); } public void _testShortErrorMessage(int mode) throws Exception { final String DOC = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; assertTrue(DOC.length() < 256); JsonParser jp = createParser(JSON_F, mode, DOC); try { jp.nextToken(); fail("Expected an exception for unrecognized token"); } catch (JsonParseException jpe) { String msg = jpe.getMessage(); final String expectedPrefix = "Unrecognized token '"; final String expectedSuffix = "': was expecting"; verifyException(jpe, expectedPrefix); verifyException(jpe, expectedSuffix); int tokenLen = msg.indexOf(expectedSuffix) - expectedPrefix.length(); assertEquals(DOC.length(), tokenLen); } jp.close(); } } TestParserOverrides.java000066400000000000000000000067741356164247300341240ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.ByteArrayInputStream; import java.io.StringReader; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; public class TestParserOverrides extends com.fasterxml.jackson.core.BaseTest { /* /********************************************************** /* Wrappers, to test stream and reader-based parsers /********************************************************** */ public void testTokenAccess() throws Exception { JsonFactory jf = new JsonFactory(); _testTokenAccess(jf, false); _testTokenAccess(jf, true); } public void testCurrentName() throws Exception { JsonFactory jf = new JsonFactory(); _testCurrentName(jf, false); _testCurrentName(jf, true); } /* /********************************************************** /* Actual test methods /********************************************************** */ public void _testTokenAccess(JsonFactory jf, boolean useStream) throws Exception { final String DOC = "[ ]"; JsonParser jp = useStream ? jf.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8"))) : jf.createParser(DOC); assertNull(jp.currentToken()); jp.clearCurrentToken(); assertNull(jp.currentToken()); assertNull(jp.getEmbeddedObject()); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.START_ARRAY, jp.currentToken()); jp.clearCurrentToken(); assertNull(jp.currentToken()); // Also: no codec defined by default try { jp.readValueAsTree(); fail("Should get exception without codec"); } catch (IllegalStateException e) { verifyException(e, "No ObjectCodec defined"); } jp.close(); } private void _testCurrentName(JsonFactory jf, boolean useStream) throws Exception { final String DOC = "{\"first\":{\"second\":3, \"third\":false}}"; JsonParser jp = useStream ? jf.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8"))) : jf.createParser(new StringReader(DOC)); assertNull(jp.currentToken()); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("first", jp.getCurrentName()); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertEquals("first", jp.getCurrentName()); // still the same... jp.overrideCurrentName("foobar"); assertEquals("foobar", jp.getCurrentName()); // but not any more! assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("second", jp.getCurrentName()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals("second", jp.getCurrentName()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("third", jp.getCurrentName()); assertToken(JsonToken.VALUE_FALSE, jp.nextToken()); assertEquals("third", jp.getCurrentName()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); // should retain overrides, too assertEquals("foobar", jp.getCurrentName()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); jp.clearCurrentToken(); assertNull(jp.currentToken()); jp.close(); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/TestRootValues.java000066400000000000000000000140621356164247300331540ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import java.io.*; import com.fasterxml.jackson.core.*; public class TestRootValues extends com.fasterxml.jackson.core.BaseTest { private final JsonFactory JSON_F = new JsonFactory(); public void testSimpleNumbers() throws Exception { _testSimpleNumbers(false); _testSimpleNumbers(true); } private void _testSimpleNumbers(boolean useStream) throws Exception { final String DOC = "1 2\t3\r4\n5\r\n6\r\n 7"; JsonParser jp = useStream ? createParserUsingStream(JSON_F, DOC, "UTF-8") : createParserUsingReader(JSON_F, DOC); for (int i = 1; i <= 7; ++i) { assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(i, jp.getIntValue()); } assertNull(jp.nextToken()); jp.close(); } public void testBrokenNumber() throws Exception { _testBrokenNumber(false); _testBrokenNumber(true); } private void _testBrokenNumber(boolean useStream) throws Exception { JsonFactory f = new JsonFactory(); final String DOC = "14:89:FD:D3:E7:8C"; JsonParser p = useStream ? createParserUsingStream(f, DOC, "UTF-8") : createParserUsingReader(f, DOC); // Should fail, right away try { p.nextToken(); fail("Ought to fail! Instead, got token: "+p.currentToken()); } catch (JsonParseException e) { verifyException(e, "unexpected character"); } p.close(); } public void testSimpleBooleans() throws Exception { _testSimpleBooleans(false); _testSimpleBooleans(true); } private void _testSimpleBooleans(boolean useStream) throws Exception { final String DOC = "true false\ttrue\rfalse\ntrue\r\nfalse\r\n true"; JsonParser jp = useStream ? createParserUsingStream(JSON_F, DOC, "UTF-8") : createParserUsingReader(JSON_F, DOC); boolean exp = true; for (int i = 1; i <= 7; ++i) { assertToken(exp ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE, jp.nextToken()); exp = !exp; } assertNull(jp.nextToken()); jp.close(); } public void testSimpleWrites() throws Exception { _testSimpleWrites(false); _testSimpleWrites(true); } private void _testSimpleWrites(boolean useStream) throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); StringWriter w = new StringWriter(); JsonGenerator gen; if (useStream) { gen = JSON_F.createGenerator(out, JsonEncoding.UTF8); } else { gen = JSON_F.createGenerator(w); } gen.writeNumber(123); gen.writeString("abc"); gen.writeBoolean(true); gen.close(); out.close(); w.close(); // and verify String json = useStream ? out.toString("UTF-8") : w.toString(); assertEquals("123 \"abc\" true", json); } // [core#516]: Off-by-one read problem public void testRootOffsetIssue516Bytes() throws Exception { // InputStream that forces _parseNumber2 to be invoked. final InputStream in = new Issue516InputStream(new byte[][] { "1234".getBytes("UTF-8"), "5 true".getBytes("UTF-8") }); JsonParser parser = JSON_F.createParser(in); assertEquals(12345, parser.nextIntValue(0)); // Fails with com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'rue': was expecting ('true', 'false' or 'null') assertTrue(parser.nextBooleanValue()); parser.close(); in.close(); } // [core#516]: Off-by-one read problem public void testRootOffsetIssue516Chars() throws Exception { // InputStream that forces _parseNumber2 to be invoked. final Reader in = new Issue516Reader(new char[][] { "1234".toCharArray(), "5 true".toCharArray() }); JsonParser parser = JSON_F.createParser(in); assertEquals(12345, parser.nextIntValue(0)); // Fails with com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'rue': was expecting ('true', 'false' or 'null') assertTrue(parser.nextBooleanValue()); parser.close(); in.close(); } static class Issue516InputStream extends InputStream { private final byte[][] reads; private int currentRead; public Issue516InputStream(byte[][] reads) { this.reads = reads; this.currentRead = 0; } @Override public int read() throws IOException { throw new UnsupportedOperationException(); } @Override public int read(byte[] b, int off, int len) throws IOException { if (currentRead >= reads.length) { return -1; } byte[] bytes = reads[currentRead++]; if (len < bytes.length) { throw new IllegalArgumentException(); } System.arraycopy(bytes, 0, b, off, bytes.length); return bytes.length; } } static class Issue516Reader extends Reader { private final char[][] reads; private int currentRead; public Issue516Reader(char[][] reads) { this.reads = reads; this.currentRead = 0; } @Override public void close() { } @Override public int read() throws IOException { throw new UnsupportedOperationException(); } @Override public int read(char[] b, int off, int len) throws IOException { if (currentRead >= reads.length) { return -1; } char[] bytes = reads[currentRead++]; if (len < bytes.length) { throw new IllegalArgumentException(); } System.arraycopy(bytes, 0, b, off, bytes.length); return bytes.length; } } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/TestUnicode.java000066400000000000000000000022451356164247300324370ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import java.io.IOException; import com.fasterxml.jackson.core.*; public class TestUnicode extends com.fasterxml.jackson.core.BaseTest { public void testSurrogates() throws Exception { JsonFactory f = new JsonFactory(); _testSurrogates(f, true); _testSurrogates(f, false); } /* /********************************************************** /* Helper methods /********************************************************** */ private void _testSurrogates(JsonFactory f, boolean checkText) throws IOException { byte[] json = "{\"text\":\"\uD83D\uDE03\"}".getBytes("UTF-8"); // first JsonParser jp = f.createParser(json); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); if (checkText) { assertEquals("text", jp.getText()); } assertToken(JsonToken.VALUE_STRING, jp.nextToken()); if (checkText) { assertEquals("\uD83D\uDE03", jp.getText()); } assertToken(JsonToken.END_OBJECT, jp.nextToken()); jp.close(); } } TestUtf8Generator.java000066400000000000000000000074531356164247300334750ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import com.fasterxml.jackson.core.*; 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(); public void testUtf8Issue462() throws Exception { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); IOContext ioc = new IOContext(new BufferRecycler(), bytes, true); JsonGenerator gen = new UTF8JsonGenerator(ioc, 0, null, bytes, '"'); String str = "Natuurlijk is alles gelukt en weer een tevreden klant\uD83D\uDE04"; int length = 4000 - 38; for (int i = 1; i <= length; ++i) { gen.writeNumber(1); } gen.writeString(str); gen.flush(); gen.close(); // Also verify it's parsable? JsonParser p = JSON_F.createParser(bytes.toByteArray()); for (int i = 1; i <= length; ++i) { assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(1, p.getIntValue()); } assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals(str, p.getText()); assertNull(p.nextToken()); p.close(); } // for [core#115] public void testSurrogatesWithRaw() throws Exception { final String VALUE = quote("\ud83d\ude0c"); ByteArrayOutputStream out = new ByteArrayOutputStream(); JsonGenerator g = JSON_F.createGenerator(out); g.writeStartArray(); g.writeRaw(VALUE); g.writeEndArray(); g.close(); final byte[] JSON = out.toByteArray(); JsonParser jp = JSON_F.createParser(JSON); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); String str = jp.getText(); assertEquals(2, str.length()); assertEquals((char) 0xD83D, str.charAt(0)); assertEquals((char) 0xDE0C, str.charAt(1)); 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(); } } TestWithTonsaSymbols.java000066400000000000000000000044601356164247300342640ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.*; import com.fasterxml.jackson.core.*; /** * Some unit tests to try to exercise part of parser code that * deals with symbol (table) management. */ public class TestWithTonsaSymbols extends BaseTest { /** * How many fields to generate? Since maximum symbol table * size is defined as 6000 (above which table gets cleared, * assuming the name vocabulary is unbounded), let's do something * just slightly below it. */ final static int FIELD_COUNT = 5000; public void testStreamReaderParser() throws Exception { _testWith(true); } public void testReaderParser() throws Exception { _testWith(false); } /* /********************************************************** /* Helper methods /********************************************************** */ private void _testWith(boolean useStream) throws Exception { JsonFactory jf = new JsonFactory(); String doc = buildDoc(FIELD_COUNT); /* And let's do this multiple times: just so that symbol table * state is different between runs. */ for (int x = 0; x < 3; ++x) { JsonParser p = useStream ? jf.createParser(new ByteArrayInputStream(doc.getBytes("UTF-8"))) : jf.createParser(new StringReader(doc)); assertToken(JsonToken.START_OBJECT, p.nextToken()); for (int i = 0; i < FIELD_COUNT; ++i) { 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, p.nextToken()); p.close(); } } private String buildDoc(int len) { StringBuilder sb = new StringBuilder(len * 12); sb.append('{'); for (int i = 0; i < len; ++i) { if (i > 0) { sb.append(','); } sb.append('"'); fieldNameFor(sb, i); sb.append('"'); sb.append(':'); sb.append(i); } sb.append('}'); return sb.toString(); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/async/000077500000000000000000000000001356164247300304605ustar00rootroot00000000000000AsyncBinaryParseTest.java000066400000000000000000000144511356164247300353260ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.*; import static org.junit.Assert.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncBinaryParseTest extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); final static int[] SIZES = new int[] { 1, 2, 3, 4, 5, 7, 11, 90, 350, 1900, 6000, 19000, 65000, 139000 }; public void testRawAsRootValue() throws IOException { _testBinaryAsRoot(JSON_F); } public void testRawAsArray() throws IOException { _testBinaryAsArray(JSON_F); } public void testRawAsObject() throws IOException { _testBinaryAsObject(JSON_F); } /* /********************************************************** /* Helper methods /********************************************************** */ private void _testBinaryAsRoot(JsonFactory f) throws IOException { _testBinaryAsRoot2(f, 1, Integer.MAX_VALUE); _testBinaryAsRoot2(f, 0, 3); _testBinaryAsRoot2(f, 1, 1); } private void _testBinaryAsObject(JsonFactory f) throws IOException { _testBinaryAsObject2(f, 1, Integer.MAX_VALUE); _testBinaryAsObject2(f, 0, 3); _testBinaryAsObject2(f, 1, 1); } private void _testBinaryAsArray(JsonFactory f) throws IOException { _testBinaryAsArray2(f, 1, Integer.MAX_VALUE); _testBinaryAsArray2(f, 0, 3); _testBinaryAsArray2(f, 1, 1); } /* /********************************************************** /* Helper methods /********************************************************** */ private void _testBinaryAsRoot2(JsonFactory f, int offset, int readSize) throws IOException { for (int size : SIZES) { byte[] binary = _generateData(size); ByteArrayOutputStream bo = new ByteArrayOutputStream(size+10); JsonGenerator g = f.createGenerator(bo); g.writeBinary(binary); g.close(); byte[] smile = bo.toByteArray(); // and verify AsyncReaderWrapper p = asyncForBytes(f, readSize, smile, offset); // JSON has no native binary type so assertToken(JsonToken.VALUE_STRING, p.nextToken()); byte[] result = p.getBinaryValue(); assertArrayEquals(binary, result); assertNull(p.nextToken()); p.close(); // and second time around, skipping p = asyncForBytes(f, readSize, smile, offset); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertNull(p.nextToken()); p.close(); } } private void _testBinaryAsArray2(JsonFactory f, int offset, int readSize) throws IOException { for (int size : SIZES) { byte[] binary = _generateData(size); ByteArrayOutputStream bo = new ByteArrayOutputStream(size+10); JsonGenerator g = f.createGenerator(bo); g.writeStartArray(); g.writeBinary(binary); g.writeNumber(1); // just to verify there's no overrun g.writeEndArray(); g.close(); byte[] smile = bo.toByteArray(); // and verify AsyncReaderWrapper p = asyncForBytes(f, readSize, smile, offset); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); byte[] result = p.getBinaryValue(); assertArrayEquals(binary, result); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(1, p.getIntValue()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertNull(p.nextToken()); p.close(); // and second time around, skipping p = asyncForBytes(f, readSize, smile, offset); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertNull(p.nextToken()); p.close(); } } private void _testBinaryAsObject2(JsonFactory f, int offset, int readSize) throws IOException { for (int size : SIZES) { byte[] data = _generateData(size); ByteArrayOutputStream bo = new ByteArrayOutputStream(size+10); JsonGenerator g = f.createGenerator(bo); g.writeStartObject(); g.writeFieldName("binary"); g.writeBinary(data); g.writeEndObject(); g.close(); byte[] smile = bo.toByteArray(); AsyncReaderWrapper p = asyncForBytes(f, readSize, smile, offset); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("binary", p.currentName()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); byte[] result = p.getBinaryValue(); assertArrayEquals(data, result); // also, via different accessor ByteArrayOutputStream bytes = new ByteArrayOutputStream(result.length); assertEquals(result.length, p.parser().readBinaryValue(bytes)); assertArrayEquals(data, bytes.toByteArray()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertNull(p.nextToken()); p.close(); // and second time around, skipping p = asyncForBytes(f, readSize, smile, offset); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertNull(p.nextToken()); p.close(); } } private byte[] _generateData(int size) { byte[] result = new byte[size]; for (int i = 0; i < size; ++i) { result[i] = (byte) (i % 255); } return result; } } AsyncCharEscapingTest.java000066400000000000000000000074001356164247300354320ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; /** * Set of basic unit tests for verifying that the basic parser * functionality works as expected. */ public class AsyncCharEscapingTest extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); public void testMissingLinefeedEscaping() throws Exception { byte[] doc = _jsonDoc(aposToQuotes("['Linefeed: \n.']")); _testMissingLinefeedEscaping(doc, 0, 99); _testMissingLinefeedEscaping(doc, 0, 5); _testMissingLinefeedEscaping(doc, 0, 3); _testMissingLinefeedEscaping(doc, 0, 2); _testMissingLinefeedEscaping(doc, 0, 1); _testMissingLinefeedEscaping(doc, 1, 99); _testMissingLinefeedEscaping(doc, 1, 3); _testMissingLinefeedEscaping(doc, 1, 1); } private void _testMissingLinefeedEscaping(byte[] doc, int offset, int readSize) throws Exception { AsyncReaderWrapper r = asyncForBytes(JSON_F, readSize, doc, offset); assertToken(JsonToken.START_ARRAY, r.nextToken()); try { // This may or may not trigger exception JsonToken t = r.nextToken(); assertToken(JsonToken.VALUE_STRING, t); fail("Expected an exception for un-escaped linefeed in string value"); } catch (JsonParseException jex) { verifyException(jex, "has to be escaped"); } r.close(); } public void testSimpleEscaping() throws Exception { _testSimpleEscaping(0, 99); _testSimpleEscaping(0, 5); _testSimpleEscaping(0, 3); _testSimpleEscaping(0, 2); _testSimpleEscaping(0, 1); _testSimpleEscaping(1, 99); _testSimpleEscaping(1, 3); _testSimpleEscaping(1, 1); } private void _testSimpleEscaping(int offset, int readSize) throws Exception { byte[] doc = _jsonDoc(aposToQuotes("['LF=\\n']")); AsyncReaderWrapper r = asyncForBytes(JSON_F, readSize, doc, offset); assertToken(JsonToken.START_ARRAY, r.nextToken()); assertToken(JsonToken.VALUE_STRING, r.nextToken()); assertEquals("LF=\n", r.currentText()); r.close(); // Note: must split Strings, so that javac won't try to handle // escape and inline null char doc = _jsonDoc(aposToQuotes("['NULL:\\u0000!']")); r = asyncForBytes(JSON_F, readSize, doc, offset); assertToken(JsonToken.START_ARRAY, r.nextToken()); assertToken(JsonToken.VALUE_STRING, r.nextToken()); assertEquals("NULL:\0!", r.currentText()); r.close(); // Then just a single char escaping doc = _jsonDoc(aposToQuotes("['\\u0123']")); r = asyncForBytes(JSON_F, readSize, doc, offset); assertToken(JsonToken.START_ARRAY, r.nextToken()); assertToken(JsonToken.VALUE_STRING, r.nextToken()); assertEquals("\u0123", r.currentText()); r.close(); // And then double sequence doc = _jsonDoc(aposToQuotes("['\\u0041\\u0043']")); r = asyncForBytes(JSON_F, readSize, doc, offset); assertToken(JsonToken.START_ARRAY, r.nextToken()); assertToken(JsonToken.VALUE_STRING, r.nextToken()); assertEquals("AC", r.currentText()); r.close(); } public void test8DigitSequence() throws Exception { String DOC = "[\"\\u00411234\"]"; AsyncReaderWrapper r = asyncForBytes(JSON_F, 1, _jsonDoc(DOC), 1); assertToken(JsonToken.START_ARRAY, r.nextToken()); assertToken(JsonToken.VALUE_STRING, r.nextToken()); assertEquals("A1234", r.currentText()); r.close(); } } AsyncCommentParsingTest.java000066400000000000000000000217361356164247300360410ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; /** * Unit tests for verifying that support for (non-standard) comments * works as expected. */ public class AsyncCommentParsingTest extends AsyncTestBase { 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 /********************************************************** */ public void testCommentsDisabled() throws Exception { _testDisabled(DOC_WITH_SLASHSTAR_COMMENT); _testDisabled(DOC_WITH_SLASHSLASH_COMMENT); } public void testCommentsEnabled() throws Exception { _testEnabled(DOC_WITH_SLASHSTAR_COMMENT, 99); _testEnabled(DOC_WITH_SLASHSTAR_COMMENT, 3); _testEnabled(DOC_WITH_SLASHSTAR_COMMENT, 1); _testEnabled(DOC_WITH_SLASHSLASH_COMMENT, 99); _testEnabled(DOC_WITH_SLASHSLASH_COMMENT, 3); _testEnabled(DOC_WITH_SLASHSLASH_COMMENT, 1); } public void testCCommentsWithUTF8() throws Exception { final String JSON = "/* \u00a9 2099 Yoyodyne Inc. */\n [ \"bar? \u00a9\" ]\n"; _testWithUTF8Chars(JSON, 99); _testWithUTF8Chars(JSON, 5); _testWithUTF8Chars(JSON, 3); _testWithUTF8Chars(JSON, 2); _testWithUTF8Chars(JSON, 1); } public void testYAMLCommentsEnabled() throws Exception { final JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_YAML_COMMENTS) .build(); _testYAMLComments(f, 99); _testYAMLComments(f, 3); _testYAMLComments(f, 1); _testCommentsBeforePropValue(f, "# foo\n", 99); _testCommentsBeforePropValue(f, "# foo\n", 3); _testCommentsBeforePropValue(f, "# foo\n", 1); _testCommentsBetweenArrayValues(f, "# foo\n", 99); _testCommentsBetweenArrayValues(f, "# foo\n", 3); _testCommentsBetweenArrayValues(f, "# foo\n", 1); } public void testCCommentsEnabled() throws Exception { final JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) .build(); final String COMMENT = "/* foo */\n"; _testCommentsBeforePropValue(f, COMMENT, 99); _testCommentsBeforePropValue(f, COMMENT, 3); _testCommentsBeforePropValue(f, COMMENT, 1); } public void testCppCommentsEnabled() throws Exception { final JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) .build(); final String COMMENT = "// foo\n"; _testCommentsBeforePropValue(f, COMMENT, 99); _testCommentsBeforePropValue(f, COMMENT, 3); _testCommentsBeforePropValue(f, COMMENT, 1); } private void _testCommentsBeforePropValue(JsonFactory f, String comment, int bytesPerRead) 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 + "}"; AsyncReaderWrapper p = _createParser(f, DOC, bytesPerRead); 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(); } } private void _testCommentsBetweenArrayValues(JsonFactory f, String comment, int bytesPerRead) 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]"; AsyncReaderWrapper p = _createParser(f, DOC, bytesPerRead); 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 bytesPerRead) 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" ; AsyncReaderWrapper p = _createParser(f, DOC, bytesPerRead); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertEquals(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("a", p.currentName()); assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(1, p.getIntValue()); assertEquals(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("b", p.currentName()); 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()); assertNull(p.nextToken()); p.close(); } /* /********************************************************** /* Helper methods /********************************************************** */ private void _testWithUTF8Chars(String doc, int bytesPerRead) throws IOException { // should basically just stream through AsyncReaderWrapper p = _createParser(doc, true, bytesPerRead); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertNull(p.nextToken()); p.close(); } private void _testDisabled(String doc) throws IOException { AsyncReaderWrapper p = _createParser(doc, false, 3); assertToken(JsonToken.START_ARRAY, p.nextToken()); 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 bytesPerRead) throws IOException { AsyncReaderWrapper p = _createParser(doc, true, bytesPerRead); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(1, p.getIntValue()); assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); } private AsyncReaderWrapper _createParser(String doc, boolean enabled, int bytesPerRead) throws IOException { final JsonFactory f = JsonFactory.builder() .configure(JsonReadFeature.ALLOW_JAVA_COMMENTS, enabled) .build(); return asyncForBytes(f, bytesPerRead, _jsonDoc(doc), 0); } private AsyncReaderWrapper _createParser(JsonFactory f, String doc, int bytesPerRead) throws IOException { return asyncForBytes(f, bytesPerRead, _jsonDoc(doc), 0); } } AsyncConcurrencyTest.java000066400000000000000000000130741356164247300354010ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.IOException; import java.util.ArrayList; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncConcurrencyTest extends AsyncTestBase { private final static JsonFactory JSON_F = new JsonFactory(); static { // To make it pass, try: // JSON_F.disable(JsonFactory.Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING); } private final static String TEXT1 = "Short"; private final static String TEXT2 = "Some longer text"; private final static String TEXT3 = "and yet more"; private final static String TEXT4 = "... Longest yet although not superbly long still (see 'apos'?)"; private final static byte[] JSON_DOC = utf8Bytes(String.format( "[\"%s\", \"%s\",\n\"%s\",\"%s\" ]", TEXT1, TEXT2, TEXT3, TEXT4)); private class WorkUnit { private int stage = 0; private AsyncReaderWrapper parser; private boolean errored = false; public boolean process() throws Exception { // short-cut through if this instance has already failed if (errored) { return false; } try { switch (stage++) { case 0: parser = asyncForBytes(JSON_F, 100, JSON_DOC, 0); break; case 1: _assert(JsonToken.START_ARRAY); break; case 2: _assert(TEXT1); break; case 3: _assert(TEXT2); break; case 4: _assert(TEXT3); break; case 5: _assert(TEXT4); break; case 6: _assert(JsonToken.END_ARRAY); break; default: /* if (parser.nextToken() != null) { throw new IOException("Unexpected token at "+stage+"; expected `null`, got "+parser.currentToken()); } */ parser.close(); parser = null; stage = 0; return true; } } catch (Exception e) { errored = true; throw e; } return false; } private void _assert(String exp) throws IOException { _assert(JsonToken.VALUE_STRING); String str = parser.currentText(); if (!exp.equals(str)) { throw new IOException("Unexpected VALUE_STRING: expected '"+exp+"', got '"+str+"'"); } } private void _assert(JsonToken exp) throws IOException { JsonToken t = parser.nextToken(); if (t != exp) { throw new IOException("Unexpected token at "+stage+"; expected "+exp+", got "+t); } } } // [jackson-core#476] public void testConcurrentAsync() throws Exception { final int MAX_ROUNDS = 30; for (int i = 0; i < MAX_ROUNDS; ++i) { _testConcurrentAsyncOnce(i, MAX_ROUNDS); } } private void _testConcurrentAsyncOnce(final int round, final int maxRounds) throws Exception { final int numThreads = 3; final ExecutorService executor = Executors.newFixedThreadPool(numThreads); final AtomicInteger errorCount = new AtomicInteger(0); final AtomicInteger completedCount = new AtomicInteger(0); final AtomicReference errorRef = new AtomicReference(); // First, add a few shared work units final ArrayBlockingQueue q = new ArrayBlockingQueue(20); for (int i = 0; i < 7; ++i) { q.add(new WorkUnit()); } // then invoke swarm of workers on it... final int REP_COUNT = 99000; ArrayList> futures = new ArrayList>(); for (int i = 0; i < REP_COUNT; i++) { Callable c = new Callable() { @Override public Void call() throws Exception { WorkUnit w = q.take(); try { if (w.process()) { completedCount.incrementAndGet(); } } catch (Throwable t) { if (errorCount.getAndIncrement() == 0) { errorRef.set(t.toString()); } } finally { q.add(w); } return null; } }; futures.add(executor.submit(c)); } executor.shutdown(); executor.awaitTermination(5, TimeUnit.SECONDS); int count = errorCount.get(); if (count > 0) { fail("Expected no problems (round "+round+"/"+maxRounds +"); got "+count+", first with: "+errorRef.get()); } final int EXP_COMPL = ((REP_COUNT + 7) / 8); int compl = completedCount.get(); if (compl < (EXP_COMPL-10) || compl > EXP_COMPL) { fail("Expected about "+EXP_COMPL+" completed rounds, got: "+compl); } } } AsyncFieldNamesTest.java000066400000000000000000000114111356164247300351070ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncFieldNamesTest extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); private final JsonFactory JSON_APOS_F = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES) .build(); // Mainly to test "fast" parse for shortish names public void testSimpleFieldNames() throws IOException { for (String name : new String[] { "", "a", "ab", "abc", "abcd", "abcd1", "abcd12", "abcd123", "abcd1234", "abcd1234a", "abcd1234ab", "abcd1234abc", "abcd1234abcd", "abcd1234abcd1" }) { _testSimpleFieldName(name); } } private void _testSimpleFieldName(String fieldName) throws IOException { // use long buffer to ensure fast decoding may be used AsyncReaderWrapper r = asyncForBytes(JSON_F, 99, _jsonDoc(String.format("{\"%s\":true} \r", fieldName)), 0); assertNull(r.currentToken()); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals(fieldName, r.currentName()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); assertToken(JsonToken.END_OBJECT, r.nextToken()); assertNull(r.nextToken()); JsonLocation loc = r.parser().getCurrentLocation(); assertEquals(2, loc.getLineNr()); assertEquals(1, loc.getColumnNr()); } public void testEscapedFieldNames() throws IOException { _testEscapedFieldNames("\\'foo\\'", "'foo'"); _testEscapedFieldNames("\\'foobar\\'", "'foobar'"); _testEscapedFieldNames("\\'foo \\u0026 bar\\'", "'foo & bar'"); _testEscapedFieldNames("Something \\'longer\\'?", "Something 'longer'?"); _testEscapedFieldNames("\\u00A7", "\u00A7"); _testEscapedFieldNames("\\u4567", "\u4567"); _testEscapedFieldNames("Unicode: \\u00A7 and \\u4567?", "Unicode: \u00A7 and \u4567?"); } private void _testEscapedFieldNames(String nameEncoded, String nameExp) throws IOException { byte[] doc; StringWriter w; nameEncoded = aposToQuotes(nameEncoded); nameExp = aposToQuotes(nameExp); w = new StringWriter(); w.append("{\""); w.append(nameEncoded); w.append("\":true}"); doc = w.toString().getBytes("UTF-8"); _testEscapedFieldNames(doc, nameExp, 0, 99); _testEscapedFieldNames(doc, nameExp, 0, 5); _testEscapedFieldNames(doc, nameExp, 0, 3); _testEscapedFieldNames(doc, nameExp, 0, 2); _testEscapedFieldNames(doc, nameExp, 0, 1); _testEscapedFieldNames(doc, nameExp, 1, 99); _testEscapedFieldNames(doc, nameExp, 1, 3); _testEscapedFieldNames(doc, nameExp, 1, 1); w = new StringWriter(); w.append("{'"); w.append(nameEncoded); w.append("':true}"); doc = w.toString().getBytes("UTF-8"); _testEscapedAposFieldNames(doc, nameExp, 0, 99); _testEscapedAposFieldNames(doc, nameExp, 0, 5); _testEscapedAposFieldNames(doc, nameExp, 0, 3); _testEscapedAposFieldNames(doc, nameExp, 0, 2); _testEscapedAposFieldNames(doc, nameExp, 0, 1); _testEscapedAposFieldNames(doc, nameExp, 1, 99); _testEscapedAposFieldNames(doc, nameExp, 1, 3); _testEscapedAposFieldNames(doc, nameExp, 1, 1); } private void _testEscapedFieldNames(byte[] doc, String expName, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(JSON_F, readSize, doc, offset); assertNull(r.currentToken()); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals(expName, r.currentName()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); r.close(); assertNull(r.nextToken()); } private void _testEscapedAposFieldNames(byte[] doc, String expName, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(JSON_APOS_F, readSize, doc, offset); assertNull(r.currentToken()); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals(expName, r.currentName()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); r.close(); assertNull(r.nextToken()); } } AsyncInvalidCharsTest.java000066400000000000000000000101761356164247300354560ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.ByteArrayOutputStream; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; // Tests for verifying things such as handling of invalid control characters; // decoding of UTF-8 BOM. public class AsyncInvalidCharsTest extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); public void testUtf8BOMHandling() throws Exception { _testUtf8BOMHandling(0, 99); _testUtf8BOMHandling(0, 5); _testUtf8BOMHandling(0, 3); _testUtf8BOMHandling(0, 2); _testUtf8BOMHandling(0, 1); _testUtf8BOMHandling(2, 99); _testUtf8BOMHandling(2, 1); } private void _testUtf8BOMHandling(int offset, int readSize) throws Exception { _testUTF8BomOk(offset, readSize); _testUTF8BomFail(offset, readSize, 1, "Unexpected byte 0x5b following 0xEF; should get 0xBB as second byte"); _testUTF8BomFail(offset, readSize, 2, "Unexpected byte 0x5b following 0xEF 0xBB; should get 0xBF as third byte"); } private void _testUTF8BomOk(int offset, int readSize) 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[] doc = bytes.toByteArray(); AsyncReaderWrapper p = asyncForBytes(JSON_F, readSize, doc, offset); assertEquals(JsonToken.START_ARRAY, p.nextToken()); // should also have skipped first 3 bytes of BOM; but do we have offset available? /* 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.parser().getTokenLocation(); // so if BOM was consider in-stream (part of input), this should expect 3: // (NOTE: this is location for START_ARRAY token, now) assertEquals(-1, loc.getCharOffset()); // !!! TODO: fix location handling /* assertEquals(0, loc.getByteOffset()); assertEquals(1, loc.getLineNr()); assertEquals(1, loc.getColumnNr()); */ assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(JsonToken.END_ARRAY, p.nextToken()); p.close(); } private void _testUTF8BomFail(int offset, int readSize, int okBytes, String verify) throws Exception { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); bytes.write(0xEF); if (okBytes > 1) { bytes.write(0xBB); } bytes.write("[ 1 ]".getBytes("UTF-8")); byte[] doc = bytes.toByteArray(); AsyncReaderWrapper p = asyncForBytes(JSON_F, readSize, doc, offset); try { assertEquals(JsonToken.START_ARRAY, p.nextToken()); fail("Should not pass"); } catch (JsonParseException e) { verifyException(e, verify); } } public void testHandlingOfInvalidSpace() throws Exception { _testHandlingOfInvalidSpace(0, 99); _testHandlingOfInvalidSpace(0, 3); _testHandlingOfInvalidSpace(0, 1); _testHandlingOfInvalidSpace(1, 99); _testHandlingOfInvalidSpace(2, 1); } private void _testHandlingOfInvalidSpace(int offset, int readSize) throws Exception { final String doc = "{ \u0008 \"a\":1}"; AsyncReaderWrapper p = asyncForBytes(JSON_F, readSize, _jsonDoc(doc), offset); assertToken(JsonToken.START_OBJECT, p.nextToken()); try { p.nextToken(); fail("Should have failed"); } catch (JsonParseException e) { verifyException(e, "Illegal character"); // and correct error code verifyException(e, "code 8"); } p.close(); } } AsyncLocationTest.java000066400000000000000000000031571356164247300346600ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.async.ByteArrayFeeder; public class AsyncLocationTest extends AsyncTestBase { private final JsonFactory DEFAULT_F = new JsonFactory(); // for [core#531] public void testLocationOffsets() throws Exception { JsonParser parser = DEFAULT_F.createNonBlockingByteArrayParser(); ByteArrayFeeder feeder = (ByteArrayFeeder) parser.getNonBlockingInputFeeder(); byte[] input = utf8Bytes("[[["); feeder.feedInput(input, 2, 3); assertEquals(JsonToken.START_ARRAY, parser.nextToken()); assertEquals(1, parser.getCurrentLocation().getByteOffset()); assertEquals(1, parser.getTokenLocation().getByteOffset()); assertEquals(1, parser.getCurrentLocation().getLineNr()); assertEquals(1, parser.getTokenLocation().getLineNr()); assertEquals(2, parser.getCurrentLocation().getColumnNr()); assertEquals(1, parser.getTokenLocation().getColumnNr()); feeder.feedInput(input, 0, 1); assertEquals(JsonToken.START_ARRAY, parser.nextToken()); assertEquals(2, parser.getCurrentLocation().getByteOffset()); assertEquals(2, parser.getTokenLocation().getByteOffset()); assertEquals(1, parser.getCurrentLocation().getLineNr()); assertEquals(1, parser.getTokenLocation().getLineNr()); assertEquals(3, parser.getCurrentLocation().getColumnNr()); assertEquals(2, parser.getTokenLocation().getColumnNr()); parser.close(); } } AsyncMissingValuesInArrayTest.java000066400000000000000000000162361356164247300371710ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.IOException; import java.util.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class AsyncMissingValuesInArrayTest extends AsyncTestBase { private final JsonFactory factory; private final HashSet features; public AsyncMissingValuesInArrayTest(Collection features) { this.features = new HashSet(features); JsonFactoryBuilder b = (JsonFactoryBuilder) JsonFactory.builder(); for (JsonReadFeature feature : features) { b = b.enable(feature); } factory = b.build(); } @Parameterized.Parameters(name = "Features {0}") public static Collection> getTestCases() { List> cases = new ArrayList>(); cases.add(EnumSet.noneOf(JsonReadFeature.class)); cases.add(EnumSet.of(JsonReadFeature.ALLOW_MISSING_VALUES)); cases.add(EnumSet.of(JsonReadFeature.ALLOW_TRAILING_COMMA)); cases.add(EnumSet.of(JsonReadFeature.ALLOW_MISSING_VALUES, JsonReadFeature.ALLOW_TRAILING_COMMA)); return cases; } // Could test, but this case is covered by other tests anyway /* @Test public void testArrayBasic() throws Exception { String json = "[\"a\", \"b\"]"; AsyncReaderWrapper p = createParser(factory, json); assertEquals(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("a", p.currentText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("b", p.currentText()); assertEquals(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } */ @Test public void testArrayInnerComma() throws Exception { String json = "[\"a\",, \"b\"]"; AsyncReaderWrapper p = createParser(factory, json); assertEquals(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("a", p.currentText()); if (!features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { assertUnexpected(p, ','); return; } assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("b", p.currentText()); assertEquals(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } @Test public void testArrayLeadingComma() throws Exception { String json = "[,\"a\", \"b\"]"; AsyncReaderWrapper p = createParser(factory, json); assertEquals(JsonToken.START_ARRAY, p.nextToken()); if (!features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { assertUnexpected(p, ','); return; } assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("a", p.currentText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("b", p.currentText()); assertEquals(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); p.close(); } @Test public void testArrayTrailingComma() throws Exception { String json = "[\"a\", \"b\",]"; AsyncReaderWrapper p = createParser(factory, json); assertEquals(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("a", p.currentText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("b", p.currentText()); // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES if (features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } else if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } else { assertUnexpected(p, ']'); } p.close(); } @Test public void testArrayTrailingCommas() throws Exception { String json = "[\"a\", \"b\",,]"; AsyncReaderWrapper p = createParser(factory, json); assertEquals(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("a", p.currentText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("b", p.currentText()); // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES) && features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } else if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } else { assertUnexpected(p, ','); } p.close(); } @Test public void testArrayTrailingCommasTriple() throws Exception { String json = "[\"a\", \"b\",,,]"; AsyncReaderWrapper p = createParser(factory, json); assertEquals(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("a", p.currentText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("b", p.currentText()); // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES) && features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } else if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } else { assertUnexpected(p, ','); } p.close(); } private void assertEnd(AsyncReaderWrapper p) throws IOException { JsonToken next = p.nextToken(); assertNull("expected end of stream but found " + next, next); } private void assertUnexpected(AsyncReaderWrapper p, char c) throws IOException { try { p.nextToken(); fail("No exception thrown"); } catch (JsonParseException e) { verifyException(e, String.format("Unexpected character ('%s' (code %d))", c, (int) c)); } } private AsyncReaderWrapper createParser(JsonFactory f, String doc) throws IOException { int bytesPerRead = 3; // should vary but... AsyncReaderWrapper p = asyncForBytes(f, bytesPerRead, _jsonDoc(doc), 0); return p; } } AsyncMissingValuesInObjectTest.java000066400000000000000000000114301356164247300373100ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.IOException; import java.util.*; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; @RunWith(Parameterized.class) public class AsyncMissingValuesInObjectTest extends AsyncTestBase { private JsonFactory factory; private HashSet features; public AsyncMissingValuesInObjectTest(Collection features) { this.features = new HashSet(features); JsonFactoryBuilder b = (JsonFactoryBuilder) JsonFactory.builder(); for (JsonReadFeature feature : features) { b = b.enable(feature); } factory = b.build(); } @Parameterized.Parameters(name = "Features {0}") public static Collection> getTestCases() { List> cases = new ArrayList>(); cases.add(EnumSet.noneOf(JsonReadFeature.class)); cases.add(EnumSet.of(JsonReadFeature.ALLOW_MISSING_VALUES)); cases.add(EnumSet.of(JsonReadFeature.ALLOW_TRAILING_COMMA)); cases.add(EnumSet.of(JsonReadFeature.ALLOW_MISSING_VALUES, JsonReadFeature.ALLOW_TRAILING_COMMA)); return cases; } @Test public void testObjectBasic() throws Exception { String json = "{\"a\": true, \"b\": false}"; AsyncReaderWrapper p = createParser(factory, json); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("a", p.currentText()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("b", p.currentText()); assertToken(JsonToken.VALUE_FALSE, p.nextToken()); assertEquals(JsonToken.END_OBJECT, p.nextToken()); assertEnd(p); p.close(); } @Test public void testObjectInnerComma() throws Exception { String json = "{\"a\": true,, \"b\": false}"; AsyncReaderWrapper p = createParser(factory, json); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("a", p.currentText()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertUnexpected(p, ','); p.close(); } @Test public void testObjectLeadingComma() throws Exception { String json = "{,\"a\": true, \"b\": false}"; AsyncReaderWrapper p = createParser(factory, json); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertUnexpected(p, ','); p.close(); } @Test public void testObjectTrailingComma() throws Exception { String json = "{\"a\": true, \"b\": false,}"; AsyncReaderWrapper p = createParser(factory, json); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("a", p.currentText()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("b", p.currentText()); assertToken(JsonToken.VALUE_FALSE, p.nextToken()); if (features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { assertToken(JsonToken.END_OBJECT, p.nextToken()); assertEnd(p); } else { assertUnexpected(p, '}'); } p.close(); } @Test public void testObjectTrailingCommas() throws Exception { String json = "{\"a\": true, \"b\": false,,}"; AsyncReaderWrapper p = createParser(factory, json); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("a", p.currentText()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("b", p.currentText()); assertToken(JsonToken.VALUE_FALSE, p.nextToken()); assertUnexpected(p, ','); p.close(); } private void assertEnd(AsyncReaderWrapper p) throws IOException { JsonToken next = p.nextToken(); assertNull("expected end of stream but found " + next, next); } private void assertUnexpected(AsyncReaderWrapper p, char c) throws IOException { try { p.nextToken(); fail("No exception thrown"); } catch (JsonParseException e) { verifyException(e, String.format("Unexpected character ('%s' (code %d))", c, (int) c)); } } private AsyncReaderWrapper createParser(JsonFactory f, String doc) throws IOException { int bytesPerRead = 3; // should vary but... AsyncReaderWrapper p = asyncForBytes(f, bytesPerRead, _jsonDoc(doc), 0); return p; } } AsyncNonStdNumbersTest.java000066400000000000000000000152541356164247300356520ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncNonStdNumbersTest extends AsyncTestBase { private final JsonFactory DEFAULT_F = new JsonFactory(); @SuppressWarnings("deprecation") public void testDefaultsForAsync() throws Exception { assertFalse(DEFAULT_F.isEnabled(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS)); } public void testDisallowNaN() throws Exception { final String JSON = "[ NaN]"; // without enabling, should get an exception AsyncReaderWrapper p = createParser(DEFAULT_F, JSON, 1); assertToken(JsonToken.START_ARRAY, p.nextToken()); try { p.nextToken(); fail("Expected exception"); } catch (Exception e) { verifyException(e, "non-standard"); } finally { p.close(); } } public void testAllowNaN() throws Exception { final String JSON = "[ NaN]"; JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS) .build(); _testAllowNaN(f, JSON, 99); _testAllowNaN(f, JSON, 5); _testAllowNaN(f, JSON, 3); _testAllowNaN(f, JSON, 2); _testAllowNaN(f, JSON, 1); } private void _testAllowNaN(JsonFactory f, String doc, int readBytes) throws Exception { AsyncReaderWrapper p = createParser(f, doc, readBytes); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); double d = p.getDoubleValue(); assertTrue(Double.isNaN(d)); assertEquals("NaN", p.currentText()); 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 = JsonFactory.builder() .configure(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS, true) .build(); p = createParser(f, doc, readBytes); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); } public void testDisallowInf() throws Exception { // these are serializations of JDK itself: _testDisallowInf(DEFAULT_F, "Infinity", 99); _testDisallowInf(DEFAULT_F, "Infinity", 1); _testDisallowInf(DEFAULT_F, "-Infinity", 99); _testDisallowInf(DEFAULT_F, "-Infinity", 1); // and this is sort of alias for first one _testDisallowInf(DEFAULT_F, "+Infinity", 99); _testDisallowInf(DEFAULT_F, "+Infinity", 1); // And these may or may not be supported as further aliases // 06-Jun-2017, tatu: Problematic for now since they share same prefix; can // be supported, eventually, if really care. For now leave it be. // _testDisallowInf(DEFAULT_F, "-INF"); // _testDisallowInf(DEFAULT_F, "+INF"); } private void _testDisallowInf(JsonFactory f, String token, int readBytes) throws Exception { final String JSON = String.format("[%s]", token); // without enabling, should get an exception AsyncReaderWrapper p = createParser(f, JSON, readBytes); assertToken(JsonToken.START_ARRAY, p.nextToken()); try { JsonToken t = p.nextToken(); fail("Expected exception; got "+t+" (text ["+p.currentText()+"])"); } catch (Exception e) { verifyException(e, "Non-standard token '"+token+"'"); } finally { p.close(); } } public void testAllowInf() throws Exception { JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS) .build(); String JSON = "[ Infinity, +Infinity, -Infinity ]"; _testAllowInf(f, JSON, 99); _testAllowInf(f, JSON, 5); _testAllowInf(f, JSON, 3); _testAllowInf(f, JSON, 2); _testAllowInf(f, JSON, 1); JSON = "[Infinity,+Infinity,-Infinity]"; _testAllowInf(f, JSON, 99); _testAllowInf(f, JSON, 1); JSON = "[Infinity , +Infinity , -Infinity]"; _testAllowInf(f, JSON, 99); _testAllowInf(f, JSON, 1); } private void _testAllowInf(JsonFactory f, String doc, int readBytes) throws Exception { // 06-Jun-2017, tatu: Leave out "-INF" and "+INF" for now due to overlap with // somewhat more standard (wrt JDK) "-Infinity" and "+Infinity" AsyncReaderWrapper p = createParser(f, doc, readBytes); assertToken(JsonToken.START_ARRAY, p.nextToken()); double d; assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); d = p.getDoubleValue(); assertEquals("Infinity", p.currentText()); assertTrue(Double.isInfinite(d)); assertTrue(d == Double.POSITIVE_INFINITY); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); d = p.getDoubleValue(); assertEquals("+Infinity", p.currentText()); assertTrue(Double.isInfinite(d)); assertTrue(d == Double.POSITIVE_INFINITY); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); d = p.getDoubleValue(); assertEquals("-Infinity", p.currentText()); assertTrue(Double.isInfinite(d)); assertTrue(d == Double.NEGATIVE_INFINITY); assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); // finally, should also work with skipping f = JsonFactory.builder() .configure(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS, true) .build(); p = createParser(f, doc, readBytes); 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.END_ARRAY, p.nextToken()); p.close(); } private AsyncReaderWrapper createParser(JsonFactory f, String doc, int readBytes) throws IOException { return asyncForBytes(f, readBytes, _jsonDoc(doc), 1); } } AsyncNonStdParsingTest.java000066400000000000000000000354111356164247300356370ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncNonStdParsingTest extends AsyncTestBase { public void testLargeUnquotedNames() throws Exception { final JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES) .build(); StringBuilder sb = new StringBuilder(5000); sb.append("[\n"); 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 doc = sb.toString(); _testLargeUnquoted(f, REPS, doc, 0, 99); _testLargeUnquoted(f, REPS, doc, 0, 5); _testLargeUnquoted(f, REPS, doc, 0, 3); _testLargeUnquoted(f, REPS, doc, 0, 2); _testLargeUnquoted(f, REPS, doc, 0, 1); _testLargeUnquoted(f, REPS, doc, 1, 99); _testLargeUnquoted(f, REPS, doc, 1, 1); } private void _testLargeUnquoted(JsonFactory f, int reps, String doc, int offset, int readSize) throws Exception { AsyncReaderWrapper p = createParser(f, doc, offset, readSize); 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.currentName()); 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(); } public void testSimpleUnquotedNames() throws Exception { final JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES) .build(); _testSimpleUnquoted(f, 0, 99); _testSimpleUnquoted(f, 0, 5); _testSimpleUnquoted(f, 0, 3); _testSimpleUnquoted(f, 0, 2); _testSimpleUnquoted(f, 0, 1); _testSimpleUnquoted(f, 1, 99); _testSimpleUnquoted(f, 1, 3); _testSimpleUnquoted(f, 1, 1); } private void _testSimpleUnquoted(JsonFactory f, int offset, int readSize) throws Exception { String doc = "{ a : 1, _foo:true, $:\"money!\", \" \":null }"; AsyncReaderWrapper p = createParser(f, doc, offset, readSize); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("a", p.currentName()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("_foo", p.currentName()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("$", p.currentName()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("money!", p.currentText()); // and then regular quoted one should still work too: assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals(" ", p.currentName()); assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.END_OBJECT, p.nextToken()); p.close(); // Another thing, as per [jackson-cre#102]: numbers p = createParser(f, "{ 123:true,4:false }", offset, readSize); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("123", p.currentName()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("4", p.currentName()); 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) */ public void testAposQuotingDisabled() throws Exception { JsonFactory f = new JsonFactory(); _testSingleQuotesDefault(f, 0, 99); _testSingleQuotesDefault(f, 0, 5); _testSingleQuotesDefault(f, 0, 3); _testSingleQuotesDefault(f, 0, 1); _testSingleQuotesDefault(f, 1, 99); _testSingleQuotesDefault(f, 1, 1); } private void _testSingleQuotesDefault(JsonFactory f, int offset, int readSize) throws Exception { // First, let's see that by default they are not allowed String JSON = "[ 'text' ]"; AsyncReaderWrapper p = createParser(f, JSON, offset, readSize); 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, JSON, offset, readSize); 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. */ public void testAposQuotingEnabled() throws Exception { final JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES) .build(); _testAposQuotingEnabled(f, 0, 99); _testAposQuotingEnabled(f, 0, 5); _testAposQuotingEnabled(f, 0, 3); _testAposQuotingEnabled(f, 0, 2); _testAposQuotingEnabled(f, 0, 1); _testAposQuotingEnabled(f, 1, 99); _testAposQuotingEnabled(f, 2, 1); _testAposQuotingEnabled(f, 1, 1); } private void _testAposQuotingEnabled(JsonFactory f, int offset, int readSize) throws Exception { String UNINAME = String.format("Uni%c-key-%c", UNICODE_2BYTES, UNICODE_3BYTES); String UNIVALUE = String.format("Uni%c-value-%c", UNICODE_3BYTES, UNICODE_2BYTES); String JSON = String.format( "{ 'a' : 1, \"foobar\": 'b', '_abcde1234':'d', '\"' : '\"\"', '':'', '%s':'%s'}", UNINAME, UNIVALUE); AsyncReaderWrapper p = createParser(f, JSON, offset, readSize); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("a", p.currentText()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals("1", p.currentText()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("foobar", p.currentText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("b", p.currentText()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("_abcde1234", p.currentText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("d", p.currentText()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("\"", p.currentText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("\"\"", p.currentText()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("", p.currentText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("", p.currentText()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals(UNINAME, p.currentText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals(UNIVALUE, p.currentText()); assertToken(JsonToken.END_OBJECT, p.nextToken()); p.close(); JSON = "{'b':1,'array':[{'b':3}],'ob':{'b':4,'x':0,'y':'"+UNICODE_SEGMENT+"','a':false }}"; p = createParser(f, JSON, offset, readSize); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("b", p.currentName()); 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.currentName()); 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.currentName()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(4, p.getIntValue()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("x", p.currentName()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(0, p.getIntValue()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("y", p.currentName()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals(UNICODE_SEGMENT, p.currentText()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("a", p.currentName()); 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 public void testSingleQuotesEscaped() throws Exception { final JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES) .build(); _testSingleQuotesEscaped(f, 0, 99); _testSingleQuotesEscaped(f, 0, 5); _testSingleQuotesEscaped(f, 0, 3); _testSingleQuotesEscaped(f, 0, 1); _testSingleQuotesEscaped(f, 1, 99); _testSingleQuotesEscaped(f, 1, 1); } private void _testSingleQuotesEscaped(JsonFactory f, int offset, int readSize) throws Exception { String JSON = "[ '16\\'' ]"; AsyncReaderWrapper p = createParser(f, JSON, offset, readSize); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("16'", p.currentText()); assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); } public void testNonStandardNameChars() throws Exception { final JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES) .build(); _testNonStandardNameChars(f, 0, 99); _testNonStandardNameChars(f, 0, 6); _testNonStandardNameChars(f, 0, 3); _testNonStandardNameChars(f, 0, 1); _testNonStandardNameChars(f, 1, 99); _testNonStandardNameChars(f, 2, 1); } private void _testNonStandardNameChars(JsonFactory f, int offset, int readSize) throws Exception { String JSON = "{ @type : \"mytype\", #color : 123, *error* : true, " +" hyphen-ated : \"yes\", me+my : null" +"}"; AsyncReaderWrapper p = createParser(f, JSON, offset, readSize); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("@type", p.currentText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("mytype", p.currentText()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("#color", p.currentText()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(123, p.getIntValue()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("*error*", p.currentText()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("hyphen-ated", p.currentText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("yes", p.currentText()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("me+my", p.currentText()); assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.END_OBJECT, p.nextToken()); p.close(); } public void testNonStandarBackslashQuotingForValues(int mode) throws Exception { _testNonStandarBackslashQuoting(0, 99); _testNonStandarBackslashQuoting(0, 6); _testNonStandarBackslashQuoting(0, 3); _testNonStandarBackslashQuoting(0, 1); _testNonStandarBackslashQuoting(2, 99); _testNonStandarBackslashQuoting(1, 1); } private void _testNonStandarBackslashQuoting( int offset, int readSize) throws Exception { // first: verify that we get an exception JsonFactory f = new JsonFactory(); final String JSON = quote("\\'"); AsyncReaderWrapper p = createParser(f, JSON, offset, readSize); try { p.nextToken(); p.currentText(); 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 = f.rebuild() .enable(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER) .build(); p = createParser(f, JSON, offset, readSize); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("'", p.currentText()); p.close(); } private AsyncReaderWrapper createParser(JsonFactory f, String doc, int offset, int readSize) throws IOException { return asyncForBytes(f, readSize, _jsonDoc(doc), offset); } } AsyncNumberCoercionTest.java000066400000000000000000000254121356164247300360200ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser.NumberType; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.exc.InputCoercionException; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; import com.fasterxml.jackson.core.JsonToken; public class AsyncNumberCoercionTest extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); /* /********************************************************** /* Numeric coercions, integral /********************************************************** */ public void testToIntCoercion() throws Exception { AsyncReaderWrapper p; // long->int p = createParser("1"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(1L, p.getLongValue()); assertEquals(1, p.getIntValue()); p.close(); // BigInteger->int p = createParser("10"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(BigInteger.TEN, p.getBigIntegerValue()); assertEquals(10, p.getIntValue()); p.close(); // double->int p = createParser("2"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(2.0, p.getDoubleValue()); assertEquals(2, p.getIntValue()); p.close(); p = createParser("0.1"); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); assertEquals(0.1, p.getDoubleValue()); assertEquals(0, p.getIntValue()); p.close(); // BigDecimal->int p = createParser("10"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(BigDecimal.TEN, p.getDecimalValue()); assertEquals(10, p.getIntValue()); p.close(); } public void testToIntFailing() throws Exception { AsyncReaderWrapper p; // long -> error long big = 1L + Integer.MAX_VALUE; p = createParser(String.valueOf(big)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(big, p.getLongValue()); try { p.getIntValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of int"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Integer.TYPE, e.getTargetType()); } long small = -1L + Integer.MIN_VALUE; p = createParser(String.valueOf(small)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(Long.valueOf(small), p.getNumberValue()); assertEquals(small, p.getLongValue()); try { p.getIntValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of int"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Integer.TYPE, e.getTargetType()); } // double -> error p = createParser(String.valueOf(big)+".0"); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); assertEquals((double) big, p.getDoubleValue()); try { p.getIntValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of int"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Integer.TYPE, e.getTargetType()); } p = createParser(String.valueOf(small)+".0"); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); assertEquals((double) small, p.getDoubleValue()); try { p.getIntValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of int"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Integer.TYPE, e.getTargetType()); } // BigInteger -> error p = createParser(String.valueOf(big)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(BigInteger.valueOf(big), p.getBigIntegerValue()); try { p.getIntValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of int"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Integer.TYPE, e.getTargetType()); } p = createParser(String.valueOf(small)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(BigInteger.valueOf(small), p.getBigIntegerValue()); try { p.getIntValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of int"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Integer.TYPE, e.getTargetType()); } } public void testToLongCoercion() throws Exception { AsyncReaderWrapper p; // int->long p = createParser("1"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(1, p.getIntValue()); assertEquals(1L, p.getLongValue()); p.close(); // BigInteger->long long biggish = 12345678901L; p = createParser(String.valueOf(biggish)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(BigInteger.valueOf(biggish), p.getBigIntegerValue()); assertEquals(biggish, p.getLongValue()); p.close(); // double->long p = createParser("2"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(2.0, p.getDoubleValue()); assertEquals(2L, p.getLongValue()); p.close(); // BigDecimal->long p = createParser("10"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(BigDecimal.TEN, p.getDecimalValue()); assertEquals(10, p.getLongValue()); p.close(); } public void testToLongFailing() throws Exception { AsyncReaderWrapper p; // BigInteger -> error BigInteger big = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.TEN); p = createParser(String.valueOf(big)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(NumberType.BIG_INTEGER, p.getNumberType()); assertEquals(big, p.getBigIntegerValue()); assertEquals(big, p.getNumberValue()); try { p.getLongValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of long"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Long.TYPE, e.getTargetType()); } BigInteger small = BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.TEN); p = createParser(String.valueOf(small)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(small, p.getBigIntegerValue()); try { p.getLongValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of long"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Long.TYPE, e.getTargetType()); } } public void testToBigIntegerCoercion() throws Exception { AsyncReaderWrapper p; p = createParser("1"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // int to BigInteger assertEquals(1, p.getIntValue()); assertEquals(BigInteger.ONE, p.getBigIntegerValue()); p.close(); p = createParser("2.0"); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); // double to BigInteger assertEquals(2.0, p.getDoubleValue()); assertEquals(BigInteger.valueOf(2L), p.getBigIntegerValue()); p.close(); p = createParser(String.valueOf(Long.MAX_VALUE)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // long to BigInteger assertEquals(Long.MAX_VALUE, p.getLongValue()); assertEquals(BigInteger.valueOf(Long.MAX_VALUE), p.getBigIntegerValue()); p.close(); p = createParser(" 200.0"); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); // BigDecimal to BigInteger assertEquals(new BigDecimal("200.0"), p.getDecimalValue()); assertEquals(BigInteger.valueOf(200L), p.getBigIntegerValue()); p.close(); } /* /********************************************************** /* Numeric coercions, floating point /********************************************************** */ public void testToDoubleCoercion() throws Exception { AsyncReaderWrapper p; // BigDecimal->double p = createParser("100.5"); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); assertEquals(new BigDecimal("100.5"), p.getDecimalValue()); assertEquals(100.5, p.getDoubleValue()); p.close(); p = createParser("10"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(BigInteger.TEN, p.getBigIntegerValue()); assertEquals(10.0, p.getDoubleValue()); p.close(); } public void testToBigDecimalCoercion() throws Exception { AsyncReaderWrapper p; p = createParser("1"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // int to BigDecimal assertEquals(1, p.getIntValue()); assertEquals(BigDecimal.ONE, p.getDecimalValue()); p.close(); p = createParser(String.valueOf(Long.MAX_VALUE)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // long to BigDecimal assertEquals(Long.MAX_VALUE, p.getLongValue()); assertEquals(BigDecimal.valueOf(Long.MAX_VALUE), p.getDecimalValue()); p.close(); BigInteger biggie = BigInteger.valueOf(Long.MAX_VALUE).multiply(BigInteger.TEN); p = createParser(String.valueOf(biggie)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // BigInteger to BigDecimal assertEquals(biggie, p.getBigIntegerValue()); assertEquals(new BigDecimal(biggie), p.getDecimalValue()); p.close(); } private AsyncReaderWrapper createParser(String doc) throws IOException { return asyncForBytes(JSON_F, 1, _jsonDoc(doc), 1); } } AsyncNumberLeadingZeroesTest.java000066400000000000000000000075271356164247300370210ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncNumberLeadingZeroesTest extends AsyncTestBase { @SuppressWarnings("deprecation") public void testDefaultsForAsync() throws Exception { JsonFactory f = new JsonFactory(); assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS)); } public void testLeadingZeroesInt() throws Exception { _testLeadingZeroesInt("00003", 3); _testLeadingZeroesInt("00003 ", 3); _testLeadingZeroesInt(" 00003", 3); _testLeadingZeroesInt("-00007", -7); _testLeadingZeroesInt("-00007 ", -7); _testLeadingZeroesInt(" -00007", -7); _testLeadingZeroesInt("056", 56); _testLeadingZeroesInt("056 ", 56); _testLeadingZeroesInt(" 056", 56); _testLeadingZeroesInt("-04", -4); _testLeadingZeroesInt("-04 ", -4); _testLeadingZeroesInt(" -04", -4); _testLeadingZeroesInt("0"+Integer.MAX_VALUE, Integer.MAX_VALUE); _testLeadingZeroesInt(" 0"+Integer.MAX_VALUE, Integer.MAX_VALUE); _testLeadingZeroesInt("0"+Integer.MAX_VALUE+" ", Integer.MAX_VALUE); } public void _testLeadingZeroesInt(String valueStr, int value) throws Exception { // first: verify that we get an exception JsonFactory f = new JsonFactory(); String JSON = valueStr; AsyncReaderWrapper p = createParser(f, JSON); try { p.nextToken(); p.currentText(); 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 = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS) .build(); p = createParser(f, JSON); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(value, p.getIntValue()); assertEquals(String.valueOf(value), p.currentText()); p.close(); } public void testLeadingZeroesFloat() throws Exception { _testLeadingZeroesFloat("00.25", 0.25); _testLeadingZeroesFloat(" 00.25", 0.25); _testLeadingZeroesFloat("00.25 ", 0.25); _testLeadingZeroesFloat("-000.5", -0.5); _testLeadingZeroesFloat(" -000.5", -0.5); _testLeadingZeroesFloat("-000.5 ", -0.5); } public void _testLeadingZeroesFloat(String valueStr, double value) throws Exception { // first: verify that we get an exception JsonFactory f = new JsonFactory(); String JSON = valueStr; AsyncReaderWrapper p = createParser(f, JSON); try { p.nextToken(); p.currentText(); 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 = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS) .build(); p = createParser(f, JSON); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); assertEquals(String.valueOf(value), p.currentText()); assertEquals(value, p.getDoubleValue()); p.close(); } private AsyncReaderWrapper createParser(JsonFactory f, String doc) throws IOException { return asyncForBytes(f, 1, _jsonDoc(doc), 1); } } AsyncParserNamesTest.java000066400000000000000000000116731356164247300353320ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.*; import java.util.Random; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.json.async.NonBlockingJsonParserBase; import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; /** * Tests to verify symbol table handling works as expected, wrt symbol reuse. */ public class AsyncParserNamesTest extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); public void testLongNames() throws IOException { _testWithName(generateName(5000)); } public void testEvenLongerName() throws Exception { StringBuilder nameBuf = new StringBuilder("longString"); int minLength = 9000; for (int i = 1; nameBuf.length() < minLength; ++i) { nameBuf.append("." + i); } String name = nameBuf.toString(); _testWithName(name); } private void _testWithName(String name) throws IOException { byte[] doc = _jsonDoc("{"+quote(name)+":13}"); AsyncReaderWrapper p = asyncForBytes(JSON_F, 37, doc, 0); assertNull(p.currentToken()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals(name, p.currentName()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(13, p.getIntValue()); assertEquals(name, p.currentName()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertNull(p.nextToken()); p.close(); } public void testSymbolTable() throws IOException { final String STR1 = "a"; byte[] doc = _jsonDoc("{ "+quote(STR1)+":1, \"foobar\":2, \"longername\":3 }"); JsonFactory f = JSON_F; AsyncReaderWrapper p = asyncForBytes(f, 5, doc, 0); final ByteQuadsCanonicalizer symbols1 = ((NonBlockingJsonParserBase) p.parser()).symbolTableForTests(); assertEquals(0, symbols1.size()); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertEquals(JsonToken.FIELD_NAME, p.nextToken()); // field names are interned: assertSame(STR1, p.currentName()); assertEquals(1, symbols1.size()); assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(JsonToken.FIELD_NAME, p.nextToken()); assertSame("foobar", p.currentName()); assertEquals(2, symbols1.size()); assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(JsonToken.FIELD_NAME, p.nextToken()); assertSame("longername", p.currentName()); assertEquals(3, symbols1.size()); assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(JsonToken.END_OBJECT, p.nextToken()); assertNull(p.nextToken()); assertEquals(3, symbols1.size()); p.close(); // but let's verify that symbol table gets reused properly p = asyncForBytes(f, 5, doc, 0); final ByteQuadsCanonicalizer symbols2 = ((NonBlockingJsonParserBase) p.parser()).symbolTableForTests(); // symbol tables are not reused, but contents are: assertNotSame(symbols1, symbols2); assertEquals(3, symbols2.size()); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertEquals(JsonToken.FIELD_NAME, p.nextToken()); // field names are interned: assertSame(STR1, p.currentName()); assertEquals(3, symbols2.size()); assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(JsonToken.FIELD_NAME, p.nextToken()); assertSame("foobar", p.currentName()); assertEquals(3, symbols2.size()); assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(JsonToken.FIELD_NAME, p.nextToken()); assertSame("longername", p.currentName()); assertEquals(3, symbols2.size()); assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(JsonToken.END_OBJECT, p.nextToken()); assertNull(p.nextToken()); assertEquals(3, symbols2.size()); p.close(); assertEquals(3, symbols2.size()); p.close(); } /* /********************************************************** /* Helper methods /********************************************************** */ private String generateName(int minLen) { StringBuilder sb = new StringBuilder(); Random rnd = new Random(123); while (sb.length() < minLen) { int ch = rnd.nextInt(96); if (ch < 32) { // ascii (single byte) sb.append((char) (48 + ch)); } else if (ch < 64) { // 2 byte sb.append((char) (128 + ch)); } else { // 3 byte sb.append((char) (4000 + ch)); } } return sb.toString(); } } AsyncPointerFromContext563Test.java000066400000000000000000000143051356164247300371540ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncPointerFromContext563Test extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); // [core#563] public void testPointerWithAsyncParser() throws Exception { final String SIMPLE = aposToQuotes("{'a':123,'array':[1,2,[3],5,{'obInArray':4}]," +"'ob':{'first':[false,true],'second':{'sub':37}},'b':true}"); byte[] SIMPLE_BYTES = SIMPLE.getBytes("UTF-8"); _testPointerWithAsyncParser(SIMPLE_BYTES, 0, 1000); _testPointerWithAsyncParser(SIMPLE_BYTES, 0, 7); _testPointerWithAsyncParser(SIMPLE_BYTES, 0, 3); _testPointerWithAsyncParser(SIMPLE_BYTES, 0, 2); _testPointerWithAsyncParser(SIMPLE_BYTES, 0, 1); _testPointerWithAsyncParser(SIMPLE_BYTES, 20, 5); _testPointerWithAsyncParser(SIMPLE_BYTES, 14, 1); } public void _testPointerWithAsyncParser(byte[] doc, int offset, int readSize) throws Exception { AsyncReaderWrapper p = asyncForBytes(JSON_F, readSize, doc, offset); // by default should just get "empty" assertSame(JsonPointer.empty(), p.getParsingContext().pathAsPointer()); // let's just traverse, then: assertToken(JsonToken.START_OBJECT, p.nextToken()); assertSame(JsonPointer.empty(), p.getParsingContext().pathAsPointer()); assertEquals("", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // a assertEquals("/a", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals("/a", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // array assertEquals("/array", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertEquals("/array", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 1 assertEquals("/array/0", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 2 assertEquals("/array/1", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertEquals("/array/2", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 3 assertEquals("/array/2/0", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEquals("/array/2", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 5 assertEquals("/array/3", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertEquals("/array/4", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // obInArray assertEquals("/array/4/obInArray", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 4 assertEquals("/array/4/obInArray", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertEquals("/array/4", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.END_ARRAY, p.nextToken()); // /array assertEquals("/array", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // ob assertEquals("/ob", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertEquals("/ob", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // first assertEquals("/ob/first", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertEquals("/ob/first", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_FALSE, p.nextToken()); assertEquals("/ob/first/0", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertEquals("/ob/first/1", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEquals("/ob/first", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // second assertEquals("/ob/second", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertEquals("/ob/second", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // sub assertEquals("/ob/second/sub", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // 37 assertEquals("/ob/second/sub", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertEquals("/ob/second", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.END_OBJECT, p.nextToken()); // /ob assertEquals("/ob", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); // b assertEquals("/b", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertEquals("/b", p.getParsingContext().pathAsPointer().toString()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertSame(JsonPointer.empty(), p.getParsingContext().pathAsPointer()); // note: wrapper maps to `null`, plain async-parser would give NOT_AVAILABLE assertNull(p.nextToken()); p.close(); } } AsyncRootNumbersTest.java000066400000000000000000000077421356164247300353730ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncRootNumbersTest extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); public void testRootInts() throws Exception { _testRootInts("10", 10); _testRootInts(" 10", 10); _testRootInts("10 ", 10); _testRootInts("0", 0); _testRootInts(" 0", 0); _testRootInts("0 ", 0); _testRootInts("-1234", -1234); _testRootInts(" -1234", -1234); _testRootInts(" -1234 ", -1234); } private void _testRootInts(String doc, int value) throws Exception { byte[] input = _jsonDoc(doc); JsonFactory f = JSON_F; _testRootInts(value, f, input, 0, 90); _testRootInts(value, f, input, 0, 3); _testRootInts(value, f, input, 0, 2); _testRootInts(value, f, input, 0, 1); _testRootInts(value, f, input, 1, 90); _testRootInts(value, f, input, 1, 3); _testRootInts(value, f, input, 1, 1); } private void _testRootInts(int value, JsonFactory f, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); assertNull(r.currentToken()); assertToken(JsonToken.VALUE_NUMBER_INT, r.nextToken()); assertEquals(value, r.getIntValue()); assertNull(r.nextToken()); assertTrue(r.isClosed()); } public void testRootDoublesSimple() throws Exception { _testRootDoubles("10.0", 10.0); _testRootDoubles(" 10.0", 10.0); _testRootDoubles("10.0 ", 10.0); _testRootDoubles("-1234.25", -1234.25); _testRootDoubles(" -1234.25", -1234.25); _testRootDoubles(" -1234.25 ", -1234.25); _testRootDoubles("0.25", 0.25); _testRootDoubles(" 0.25", 0.25); _testRootDoubles("0.25 ", 0.25); } public void testRootDoublesScientific() throws Exception { _testRootDoubles("9e3", 9e3); _testRootDoubles(" 9e3", 9e3); _testRootDoubles("9e3 ", 9e3); _testRootDoubles("9e-2", 9e-2); _testRootDoubles(" 9e-2", 9e-2); _testRootDoubles("9e-2 ", 9e-2); _testRootDoubles("-12.5e3", -12.5e3); _testRootDoubles(" -12.5e3", -12.5e3); _testRootDoubles(" -12.5e3 ", -12.5e3); _testRootDoubles("-12.5E3", -12.5e3); _testRootDoubles(" -12.5E3", -12.5e3); _testRootDoubles("-12.5E3 ", -12.5e3); _testRootDoubles("-12.5E-2", -12.5e-2); _testRootDoubles(" -12.5E-2", -12.5e-2); _testRootDoubles(" -12.5E-2 ", -12.5e-2); _testRootDoubles("0e-05", 0e-5); _testRootDoubles("0e-5 ", 0e-5); _testRootDoubles(" 0e-5", 0e-5); _testRootDoubles("0e1", 0e1); _testRootDoubles("0e1 ", 0e1); _testRootDoubles(" 0e1", 0e1); } private void _testRootDoubles(String doc, double value) throws Exception { byte[] input = _jsonDoc(doc); JsonFactory f = JSON_F; _testRootDoubles(value, f, input, 0, 90); _testRootDoubles(value, f, input, 0, 3); _testRootDoubles(value, f, input, 0, 2); _testRootDoubles(value, f, input, 0, 1); _testRootDoubles(value, f, input, 1, 90); _testRootDoubles(value, f, input, 1, 3); _testRootDoubles(value, f, input, 1, 1); } private void _testRootDoubles(double value, JsonFactory f, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); assertNull(r.currentToken()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, r.nextToken()); assertEquals(value, r.getDoubleValue()); assertNull(r.nextToken()); assertTrue(r.isClosed()); } } AsyncRootValuesTest.java000066400000000000000000000125231356164247300352100ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.ByteArrayOutputStream; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncRootValuesTest extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); /* /********************************************************************** /* Simple token (true, false, null) tests /********************************************************************** */ public void testTokenRootTokens() throws Exception { _testTokenRootTokens(JsonToken.VALUE_TRUE, "true"); _testTokenRootTokens(JsonToken.VALUE_FALSE, "false"); _testTokenRootTokens(JsonToken.VALUE_NULL, "null"); _testTokenRootTokens(JsonToken.VALUE_TRUE, "true "); _testTokenRootTokens(JsonToken.VALUE_FALSE, "false "); _testTokenRootTokens(JsonToken.VALUE_NULL, "null "); } private void _testTokenRootTokens(JsonToken expToken, String doc) throws Exception { byte[] input = _jsonDoc(doc); JsonFactory f = JSON_F; _testTokenRootTokens(expToken, f, input, 0, 90); _testTokenRootTokens(expToken, f, input, 0, 3); _testTokenRootTokens(expToken, f, input, 0, 2); _testTokenRootTokens(expToken, f, input, 0, 1); _testTokenRootTokens(expToken, f, input, 1, 90); _testTokenRootTokens(expToken, f, input, 1, 3); _testTokenRootTokens(expToken, f, input, 1, 1); } private void _testTokenRootTokens(JsonToken expToken, JsonFactory f, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); assertNull(r.currentToken()); assertToken(expToken, r.nextToken()); assertNull(r.nextToken()); assertTrue(r.isClosed()); } /* /********************************************************************** /* Root-level sequences /********************************************************************** */ public void testTokenRootSequence() throws Exception { byte[] input = _jsonDoc("\n[ true, false,\nnull ,null\n,true,false]"); JsonFactory f = JSON_F; _testTokenRootSequence(f, input, 0, 900); _testTokenRootSequence(f, input, 0, 3); _testTokenRootSequence(f, input, 0, 1); _testTokenRootSequence(f, input, 1, 900); _testTokenRootSequence(f, input, 1, 3); _testTokenRootSequence(f, input, 1, 1); } private void _testTokenRootSequence(JsonFactory f, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); assertNull(r.currentToken()); assertToken(JsonToken.START_ARRAY, r.nextToken()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); assertToken(JsonToken.VALUE_FALSE, r.nextToken()); assertToken(JsonToken.VALUE_NULL, r.nextToken()); assertToken(JsonToken.VALUE_NULL, r.nextToken()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); assertToken(JsonToken.VALUE_FALSE, r.nextToken()); assertToken(JsonToken.END_ARRAY, r.nextToken()); assertNull(r.nextToken()); assertTrue(r.isClosed()); } public void testMixedRootSequence() throws Exception { ByteArrayOutputStream bytes = new ByteArrayOutputStream(100); // Let's simply concatenate documents... bytes.write(_jsonDoc("{ \"a\" : 4 }")); bytes.write(_jsonDoc("[ 12, -987,false ]")); bytes.write(_jsonDoc(" 12356")); bytes.write(_jsonDoc(" true")); byte[] input = bytes.toByteArray(); JsonFactory f = JSON_F; _testMixedRootSequence(f, input, 0, 100); _testMixedRootSequence(f, input, 0, 3); _testMixedRootSequence(f, input, 0, 1); _testMixedRootSequence(f, input, 1, 100); _testMixedRootSequence(f, input, 1, 3); _testMixedRootSequence(f, input, 1, 1); } private void _testMixedRootSequence(JsonFactory f, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); assertNull(r.currentToken()); // { "a":4 } assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals("a", r.currentName()); assertToken(JsonToken.VALUE_NUMBER_INT, r.nextToken()); assertEquals(4, r.getIntValue()); assertToken(JsonToken.END_OBJECT, r.nextToken()); // [ 12, -987, false] assertToken(JsonToken.START_ARRAY, r.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, r.nextToken()); assertEquals(12, r.getIntValue()); assertToken(JsonToken.VALUE_NUMBER_INT, r.nextToken()); assertEquals(-987, r.getIntValue()); assertToken(JsonToken.VALUE_FALSE, r.nextToken()); assertToken(JsonToken.END_ARRAY, r.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, r.nextToken()); assertEquals(12356, r.getIntValue()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); assertNull(r.nextToken()); assertTrue(r.isClosed()); } } AsyncScalarArrayTest.java000066400000000000000000000335261356164247300353170ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.*; import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.JsonParser.NumberType; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncScalarArrayTest extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); /* /********************************************************************** /* Simple token (true, false, null) tests /********************************************************************** */ public void testTokens() throws IOException { byte[] data = _jsonDoc(" [ true, false ,true , null,false , null]"); JsonFactory f = JSON_F; // first: no offsets _testTokens(f, data, 0, 100); _testTokens(f, data, 0, 5); _testTokens(f, data, 0, 3); _testTokens(f, data, 0, 2); _testTokens(f, data, 0, 1); // then with offsets _testTokens(f, data, 1, 100); _testTokens(f, data, 1, 3); _testTokens(f, data, 1, 1); } private void _testTokens(JsonFactory f, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); // start with "no token" assertNull(r.currentToken()); assertToken(JsonToken.START_ARRAY, r.nextToken()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); assertToken(JsonToken.VALUE_FALSE, r.nextToken()); assertEquals("false", r.currentText()); assertEquals("false", r.currentTextViaCharacters()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); assertToken(JsonToken.VALUE_NULL, r.nextToken()); assertEquals("null", r.currentText()); assertEquals("null", r.currentTextViaCharacters()); assertToken(JsonToken.VALUE_FALSE, r.nextToken()); assertToken(JsonToken.VALUE_NULL, r.nextToken()); assertToken(JsonToken.END_ARRAY, r.nextToken()); // and end up with "no token" as well assertNull(r.nextToken()); assertTrue(r.isClosed()); } /* /********************************************************************** /* Int / long tests /********************************************************************** */ public void testInts() throws IOException { final int[] input = new int[] { 1, -1, 16, -17, 0, 131, -0, -155, 1000, -3000, 0xFFFF, -99999, Integer.MAX_VALUE, 0, Integer.MIN_VALUE }; StringBuilder sb = new StringBuilder().append("["); for (int i = 0; i < input.length; ++i) { if (i > 0) sb.append(','); sb.append(input[i]); } byte[] data = _jsonDoc(sb.append(']').toString()); JsonFactory f = JSON_F; _testInts(f, input, data, 0, 100); _testInts(f, input, data, 0, 3); _testInts(f, input, data, 0, 1); _testInts(f, input, data, 1, 100); _testInts(f, input, data, 1, 3); _testInts(f, input, data, 1, 1); } private void _testInts(JsonFactory f, int[] values, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); // start with "no token" assertNull(r.currentToken()); assertToken(JsonToken.START_ARRAY, r.nextToken()); for (int i = 0; i < values.length; ++i) { assertToken(JsonToken.VALUE_NUMBER_INT, r.nextToken()); assertEquals(values[i], r.getIntValue()); assertEquals(NumberType.INT, r.getNumberType()); // and then couple of special modes, just to get better test coverage String asStr = String.valueOf(values[i]); assertEquals(asStr, r.currentText()); StringWriter sw = new StringWriter(); assertEquals(asStr.length(), r.parser().getText(sw)); assertEquals(asStr, sw.toString()); } assertToken(JsonToken.END_ARRAY, r.nextToken()); // and end up with "no token" as well assertNull(r.nextToken()); assertTrue(r.isClosed()); } public void testLong() throws IOException { final long[] input = new long[] { // JsonParser will determine minimum size needed, so can't do these // 1, -1, 16, -17, 131, -155, 1000, -3000, 0xFFFF, -99999, -1L + Integer.MIN_VALUE, 1L + Integer.MAX_VALUE, 19L * Integer.MIN_VALUE, 27L * Integer.MAX_VALUE, Long.MIN_VALUE, Long.MAX_VALUE }; ByteArrayOutputStream bytes = new ByteArrayOutputStream(100); JsonFactory f = JSON_F; JsonGenerator g = f.createGenerator(bytes); g.writeStartArray(); for (int i = 0; i < input.length; ++i) { g.writeNumber(input[i]); } g.writeEndArray(); g.close(); byte[] data = bytes.toByteArray(); _testLong(f, input, data, 0, 100); _testLong(f, input, data, 0, 3); _testLong(f, input, data, 0, 1); _testLong(f, input, data, 1, 100); _testLong(f, input, data, 1, 3); _testLong(f, input, data, 1, 1); } private void _testLong(JsonFactory f, long[] values, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); // start with "no token" assertNull(r.currentToken()); assertToken(JsonToken.START_ARRAY, r.nextToken()); for (int i = 0; i < values.length; ++i) { assertToken(JsonToken.VALUE_NUMBER_INT, r.nextToken()); assertEquals(values[i], r.getLongValue()); assertEquals(NumberType.LONG, r.getNumberType()); } assertToken(JsonToken.END_ARRAY, r.nextToken()); // and end up with "no token" as well assertNull(r.nextToken()); assertTrue(r.isClosed()); } /* /********************************************************************** /* Floating point /********************************************************************** */ public void testFloats() throws IOException { final float[] input = new float[] { 0.0f, 0.25f, -0.5f, 10000.125f, - 99999.075f }; ByteArrayOutputStream bytes = new ByteArrayOutputStream(100); JsonFactory f = JSON_F; JsonGenerator g = f.createGenerator(bytes); g.writeStartArray(); for (int i = 0; i < input.length; ++i) { g.writeNumber(input[i]); } g.writeEndArray(); g.close(); byte[] data = bytes.toByteArray(); _testFloats(f, input, data, 0, 100); _testFloats(f, input, data, 0, 3); _testFloats(f, input, data, 0, 1); _testFloats(f, input, data, 1, 100); _testFloats(f, input, data, 1, 3); _testFloats(f, input, data, 1, 1); } private void _testFloats(JsonFactory f, float[] values, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); // start with "no token" assertNull(r.currentToken()); assertToken(JsonToken.START_ARRAY, r.nextToken()); for (int i = 0; i < values.length; ++i) { assertToken(JsonToken.VALUE_NUMBER_FLOAT, r.nextToken()); assertEquals(values[i], r.getFloatValue()); // json can't distinguish floats from doubles so assertEquals(NumberType.DOUBLE, r.getNumberType()); } assertToken(JsonToken.END_ARRAY, r.nextToken()); // and end up with "no token" as well assertNull(r.nextToken()); assertTrue(r.isClosed()); } public void testDoubles() throws IOException { final double[] input = new double[] { 0.0, 0.25, -0.5, 10000.125, -99999.075 }; ByteArrayOutputStream bytes = new ByteArrayOutputStream(100); JsonFactory f = JSON_F; JsonGenerator g = f.createGenerator(bytes); g.writeStartArray(); for (int i = 0; i < input.length; ++i) { g.writeNumber(input[i]); } g.writeEndArray(); g.close(); byte[] data = bytes.toByteArray(); _testDoubles(f, input, data, 0, 99); _testDoubles(f, input, data, 0, 3); _testDoubles(f, input, data, 0, 1); _testDoubles(f, input, data, 1, 99); _testDoubles(f, input, data, 1, 3); _testDoubles(f, input, data, 1, 1); } private void _testDoubles(JsonFactory f, double[] values, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); // start with "no token" assertNull(r.currentToken()); assertToken(JsonToken.START_ARRAY, r.nextToken()); for (int i = 0; i < values.length; ++i) { assertToken(JsonToken.VALUE_NUMBER_FLOAT, r.nextToken()); assertEquals(String.format("Entry #%d: %s (textual '%s')", i, values[i], r.currentText()), values[i], r.getDoubleValue()); assertEquals(NumberType.DOUBLE, r.getNumberType()); } assertToken(JsonToken.END_ARRAY, r.nextToken()); // and end up with "no token" as well assertNull(r.nextToken()); assertTrue(r.isClosed()); } /* /********************************************************************** /* BigInteger, BigDecimal /********************************************************************** */ public void testBigIntegers() throws IOException { BigInteger bigBase = BigInteger.valueOf(Long.MAX_VALUE); final BigInteger[] input = new BigInteger[] { // Since JSON doesn't denote "real" type, just deduces from magnitude, // let's not test any values within int/long range /* BigInteger.ZERO, BigInteger.ONE, BigInteger.TEN, BigInteger.valueOf(-999L), bigBase, */ bigBase.shiftLeft(100).add(BigInteger.valueOf(123456789L)), bigBase.add(bigBase), bigBase.multiply(BigInteger.valueOf(17)), bigBase.negate().subtract(BigInteger.TEN) }; ByteArrayOutputStream bytes = new ByteArrayOutputStream(100); JsonFactory f = JSON_F; JsonGenerator g = f.createGenerator(bytes); g.writeStartArray(); for (int i = 0; i < input.length; ++i) { g.writeNumber(input[i]); } g.writeEndArray(); g.close(); byte[] data = bytes.toByteArray(); _testBigIntegers(f, input, data, 0, 100); _testBigIntegers(f, input, data, 0, 3); _testBigIntegers(f, input, data, 0, 1); _testBigIntegers(f, input, data, 1, 100); _testBigIntegers(f, input, data, 2, 3); _testBigIntegers(f, input, data, 3, 1); } private void _testBigIntegers(JsonFactory f, BigInteger[] values, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); // start with "no token" assertNull(r.currentToken()); assertToken(JsonToken.START_ARRAY, r.nextToken()); for (int i = 0; i < values.length; ++i) { BigInteger expValue = values[i]; assertToken(JsonToken.VALUE_NUMBER_INT, r.nextToken()); assertEquals(expValue, r.getBigIntegerValue()); assertEquals(NumberType.BIG_INTEGER, r.getNumberType()); } assertToken(JsonToken.END_ARRAY, r.nextToken()); assertNull(r.nextToken()); assertTrue(r.isClosed()); } public void testBigDecimals() throws IOException { BigDecimal bigBase = new BigDecimal("1234567890344656736.125"); final BigDecimal[] input = new BigDecimal[] { // 04-Jun-2017, tatu: these look like integral numbers in JSON so can't use: // BigDecimal.ZERO, // BigDecimal.ONE, // BigDecimal.TEN, BigDecimal.valueOf(-999.25), bigBase, bigBase.divide(new BigDecimal("5")), bigBase.add(bigBase), bigBase.multiply(new BigDecimal("1.23")), bigBase.negate() }; ByteArrayOutputStream bytes = new ByteArrayOutputStream(100); JsonFactory f = JSON_F; JsonGenerator g = f.createGenerator(bytes); g.writeStartArray(); for (int i = 0; i < input.length; ++i) { g.writeNumber(input[i]); } g.writeEndArray(); g.close(); byte[] data = bytes.toByteArray(); _testBigDecimals(f, input, data, 0, 100); _testBigDecimals(f, input, data, 0, 3); _testBigDecimals(f, input, data, 0, 1); _testBigDecimals(f, input, data, 1, 100); _testBigDecimals(f, input, data, 2, 3); _testBigDecimals(f, input, data, 3, 1); } private void _testBigDecimals(JsonFactory f, BigDecimal[] values, byte[] doc, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, doc, offset); // start with "no token" assertNull(r.currentToken()); assertToken(JsonToken.START_ARRAY, r.nextToken()); for (int i = 0; i < values.length; ++i) { BigDecimal expValue = values[i]; assertToken(JsonToken.VALUE_NUMBER_FLOAT, r.nextToken()); assertEquals(expValue, r.getDecimalValue()); assertEquals(NumberType.BIG_DECIMAL, r.getNumberType()); } assertToken(JsonToken.END_ARRAY, r.nextToken()); assertNull(r.nextToken()); assertTrue(r.isClosed()); } } AsyncScopeMatchingTest.java000066400000000000000000000100141356164247300356220ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; /** * Set of basic unit tests for verifying that Array/Object scopes * are properly matched. */ public class AsyncScopeMatchingTest extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); public void testUnclosedArray(int mode) throws Exception { AsyncReaderWrapper p = asyncForBytes(JSON_F, 3, _jsonDoc("[ 1, 2 "), 0); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(2, p.getIntValue()); try { p.nextToken(); fail("Expected an exception for unclosed ARRAY (mode: "+mode+")"); } catch (JsonParseException pe) { verifyException(pe, "expected close marker for ARRAY"); } } public void testUnclosedObject(int mode) throws Exception { AsyncReaderWrapper p = asyncForBytes(JSON_F, 3, _jsonDoc("{ \"key\" : 3 "), 0); 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 (mode: "+mode+")"); } catch (JsonParseException pe) { verifyException(pe, "expected close marker for OBJECT"); } } public void testEOFInName(int mode) throws Exception { final String JSON = "{ \"abcd"; AsyncReaderWrapper p = asyncForBytes(JSON_F, 3, _jsonDoc(JSON), 0); 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 testMismatchArrayToObject() throws Exception { final String JSON = "[ 1, 2 }"; AsyncReaderWrapper p = asyncForBytes(JSON_F, 3, _jsonDoc(JSON), 0); 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 { final String JSON = "{ ]"; AsyncReaderWrapper p = asyncForBytes(JSON_F, 3, _jsonDoc(JSON), 0); 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(int mode) throws Exception { final String JSON = "{ \"a\" \"b\" }"; AsyncReaderWrapper p = asyncForBytes(JSON_F, 3, _jsonDoc(JSON), 0); 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(); } } AsyncSimpleNestedTest.java000066400000000000000000000220251356164247300354770ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncSimpleNestedTest extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); /* /********************************************************************** /* Test methods, success /********************************************************************** */ public void testStuffInObject() throws Exception { byte[] data = _jsonDoc(aposToQuotes( "{'foobar':[1,2,-999],'emptyObject':{},'emptyArray':[], 'other':{'':null} }")); JsonFactory f = JSON_F; _testStuffInObject(f, data, 0, 100); _testStuffInObject(f, data, 0, 3); _testStuffInObject(f, data, 0, 1); _testStuffInObject(f, data, 1, 100); _testStuffInObject(f, data, 1, 3); _testStuffInObject(f, data, 1, 1); } private void _testStuffInObject(JsonFactory f, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertFalse(r.parser().hasTextCharacters()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals("foobar", r.currentName()); assertToken(JsonToken.START_ARRAY, r.nextToken()); assertEquals("[", r.currentText()); assertToken(JsonToken.VALUE_NUMBER_INT, r.nextToken()); assertEquals(1, r.getIntValue()); assertToken(JsonToken.VALUE_NUMBER_INT, r.nextToken()); assertEquals(2, r.getIntValue()); assertToken(JsonToken.VALUE_NUMBER_INT, r.nextToken()); assertEquals(-999, r.getIntValue()); assertToken(JsonToken.END_ARRAY, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals("emptyObject", r.currentName()); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.END_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals("emptyArray", r.currentName()); assertToken(JsonToken.START_ARRAY, r.nextToken()); assertToken(JsonToken.END_ARRAY, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals("other", r.currentName()); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals("", r.currentName()); assertToken(JsonToken.VALUE_NULL, r.nextToken()); assertToken(JsonToken.END_OBJECT, r.nextToken()); assertToken(JsonToken.END_OBJECT, r.nextToken()); // another twist: close in the middle, verify r = asyncForBytes(f, readSize, data, offset); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); r.parser().close(); assertTrue(r.parser().isClosed()); assertNull(r.parser().nextToken()); } public void testStuffInArray() throws Exception { byte[] data = _jsonDoc(aposToQuotes("[true,{'moreStuff':0},[null],{'extraOrdinary':23}]")); JsonFactory f = JSON_F; _testStuffInArray(f, data, 0, 100); _testStuffInArray(f, data, 0, 3); _testStuffInArray(f, data, 0, 1); _testStuffInArray(f, data, 3, 100); _testStuffInArray(f, data, 3, 3); _testStuffInArray(f, data, 3, 1); } private void _testStuffInArray(JsonFactory f, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); assertToken(JsonToken.START_ARRAY, r.nextToken()); assertFalse(r.parser().hasTextCharacters()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertEquals("{", r.currentText()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals("moreStuff", r.currentName()); assertToken(JsonToken.VALUE_NUMBER_INT, r.nextToken()); assertEquals(0L, r.getLongValue()); assertToken(JsonToken.END_OBJECT, r.nextToken()); assertToken(JsonToken.START_ARRAY, r.nextToken()); assertToken(JsonToken.VALUE_NULL, r.nextToken()); assertToken(JsonToken.END_ARRAY, r.nextToken()); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals("extraOrdinary", r.currentName()); assertToken(JsonToken.VALUE_NUMBER_INT, r.nextToken()); assertEquals(23, r.getIntValue()); assertToken(JsonToken.END_OBJECT, r.nextToken()); assertToken(JsonToken.END_ARRAY, r.nextToken()); } final static String SHORT_NAME = String.format("u-%s", UNICODE_SEGMENT); final static String LONG_NAME = String.format("Unicode-with-some-longer-name-%s", UNICODE_SEGMENT); public void testStuffInArray2() throws Exception { byte[] data = _jsonDoc(aposToQuotes(String.format( "[{'%s':true},{'%s':false},{'%s':true},{'%s':false}]", SHORT_NAME, LONG_NAME, LONG_NAME, SHORT_NAME))); JsonFactory f = JSON_F; _testStuffInArray2(f, data, 0, 100); _testStuffInArray2(f, data, 0, 3); _testStuffInArray2(f, data, 0, 1); _testStuffInArray2(f, data, 3, 100); _testStuffInArray2(f, data, 3, 3); _testStuffInArray2(f, data, 3, 1); } private void _testStuffInArray2(JsonFactory f, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); assertToken(JsonToken.START_ARRAY, r.nextToken()); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals(SHORT_NAME, r.currentName()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); assertToken(JsonToken.END_OBJECT, r.nextToken()); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals(LONG_NAME, r.currentName()); assertToken(JsonToken.VALUE_FALSE, r.nextToken()); assertToken(JsonToken.END_OBJECT, r.nextToken()); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals(LONG_NAME, r.currentName()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); assertToken(JsonToken.END_OBJECT, r.nextToken()); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals(SHORT_NAME, r.currentName()); assertToken(JsonToken.VALUE_FALSE, r.nextToken()); assertToken(JsonToken.END_OBJECT, r.nextToken()); assertToken(JsonToken.END_ARRAY, r.nextToken()); } /* /********************************************************************** /* Test methods, fail checking /********************************************************************** */ public void testMismatchedArray() throws Exception { byte[] data = _jsonDoc(aposToQuotes("[ }")); JsonFactory f = JSON_F; _testMismatchedArray(f, data, 0, 99); _testMismatchedArray(f, data, 0, 3); _testMismatchedArray(f, data, 0, 2); _testMismatchedArray(f, data, 0, 1); _testMismatchedArray(f, data, 1, 3); _testMismatchedArray(f, data, 1, 1); } private void _testMismatchedArray(JsonFactory f, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); assertToken(JsonToken.START_ARRAY, r.nextToken()); try { r.nextToken(); fail("Should not pass"); } catch (JsonParseException e) { verifyException(e, "Unexpected close marker '}': expected ']'"); } } public void testMismatchedObject() throws Exception { byte[] data = _jsonDoc(aposToQuotes("{ ]")); JsonFactory f = JSON_F; _testMismatchedObject(f, data, 0, 99); _testMismatchedObject(f, data, 0, 3); _testMismatchedObject(f, data, 0, 2); _testMismatchedObject(f, data, 0, 1); _testMismatchedObject(f, data, 1, 3); _testMismatchedObject(f, data, 1, 1); } private void _testMismatchedObject(JsonFactory f, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); assertToken(JsonToken.START_OBJECT, r.nextToken()); try { r.nextToken(); fail("Should not pass"); } catch (JsonParseException e) { verifyException(e, "Unexpected close marker ']': expected '}'"); } } } AsyncSimpleObjectTest.java000066400000000000000000000147171356164247300354740ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigDecimal; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.JsonParser.NumberType; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncSimpleObjectTest extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); /* /********************************************************************** /* Test methods /********************************************************************** */ private final static String UNICODE_SHORT_NAME = "Unicode"+UNICODE_3BYTES+"RlzOk"; private final static String UNICODE_LONG_NAME = "Unicode-with-"+UNICODE_3BYTES+"-much-longer"; public void testBooleans() throws IOException { final JsonFactory f = JSON_F; byte[] data = _jsonDoc(aposToQuotes( "{ 'a':true, 'b':false, 'acdc':true, '"+UNICODE_SHORT_NAME+"':true, 'a1234567':false," +"'"+UNICODE_LONG_NAME+"': true }")); // first, no offsets _testBooleans(f, data, 0, 100); _testBooleans(f, data, 0, 3); _testBooleans(f, data, 0, 1); // then with some _testBooleans(f, data, 1, 100); _testBooleans(f, data, 1, 3); _testBooleans(f, data, 1, 1); } private void _testBooleans(JsonFactory f, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); // start with "no token" assertNull(r.currentToken()); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals("a", r.currentText()); // by default no cheap access to char[] version: assertFalse(r.parser().hasTextCharacters()); // but... char[] ch = r.parser().getTextCharacters(); assertEquals(0, r.parser().getTextOffset()); assertEquals(1, r.parser().getTextLength()); assertEquals("a", new String(ch, 0, 1)); assertTrue(r.parser().hasTextCharacters()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals("b", r.currentText()); assertToken(JsonToken.VALUE_FALSE, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals("acdc", r.currentText()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals(UNICODE_SHORT_NAME, r.currentText()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals("a1234567", r.currentText()); assertToken(JsonToken.VALUE_FALSE, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals(UNICODE_LONG_NAME, r.currentText()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); // and for fun let's verify can't access this as number or binary try { r.getDoubleValue(); fail("Should not pass"); } catch (JsonProcessingException e) { verifyException(e, "Current token (VALUE_TRUE) not numeric"); } try { r.parser().getBinaryValue(); fail("Should not pass"); } catch (JsonProcessingException e) { verifyException(e, "Current token (VALUE_TRUE) not"); verifyException(e, "can not access as binary"); } assertToken(JsonToken.END_OBJECT, r.nextToken()); // and end up with "no token" as well assertNull(r.nextToken()); assertTrue(r.isClosed()); } private final int NUMBER_EXP_I = -123456789; private final double NUMBER_EXP_D = 1024798.125; private final BigDecimal NUMBER_EXP_BD = new BigDecimal("1243565768679065.1247305834"); public void testNumbers() throws IOException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(100); JsonFactory f = JSON_F; JsonGenerator g = f.createGenerator(bytes); g.writeStartObject(); g.writeNumberField("i1", NUMBER_EXP_I); g.writeNumberField("doubley", NUMBER_EXP_D); g.writeFieldName("biggieDecimal"); g.writeNumber(NUMBER_EXP_BD.toString()); g.writeEndObject(); g.close(); byte[] data = bytes.toByteArray(); // first, no offsets _testNumbers(f, data, 0, 100); _testNumbers(f, data, 0, 5); _testNumbers(f, data, 0, 3); _testNumbers(f, data, 0, 2); _testNumbers(f, data, 0, 1); // then with some _testNumbers(f, data, 1, 100); _testNumbers(f, data, 1, 3); _testNumbers(f, data, 1, 1); } private void _testNumbers(JsonFactory f, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); // start with "no token" assertNull(r.currentToken()); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals("i1", r.currentText()); assertToken(JsonToken.VALUE_NUMBER_INT, r.nextToken()); assertEquals(NumberType.INT, r.getNumberType()); assertEquals(NUMBER_EXP_I, r.getIntValue()); assertEquals((double)NUMBER_EXP_I, r.getDoubleValue()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals("doubley", r.currentText()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, r.nextToken()); assertEquals(NumberType.DOUBLE, r.getNumberType()); assertEquals(NUMBER_EXP_D, r.getDoubleValue()); assertEquals((long) NUMBER_EXP_D, r.getLongValue()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals("biggieDecimal", r.currentText()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, r.nextToken()); // can't really tell double/BigDecimal apart in plain json assertEquals(NumberType.DOUBLE, r.getNumberType()); assertEquals(NUMBER_EXP_BD, r.getDecimalValue()); assertEquals(""+NUMBER_EXP_BD, r.currentText()); assertToken(JsonToken.END_OBJECT, r.nextToken()); // and end up with "no token" as well assertNull(r.nextToken()); assertTrue(r.isClosed()); } } AsyncStringArrayTest.java000066400000000000000000000133201356164247300353460ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.ByteArrayOutputStream; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncStringArrayTest extends AsyncTestBase { private final static String str0to9 = "1234567890"; private final static String LONG_ASCII; static { int len = 12000; StringBuilder sb = new StringBuilder(len); for (int i = 0; i < len; ++i) { sb.append((char) ('a' + i & 31)); } LONG_ASCII = sb.toString(); } private final JsonFactory JSON_F = new JsonFactory(); public void testShortAsciiStrings() throws IOException { final String[] input = new String[] { "Test", "", "1", // 60 chars, to stay short String.format("%s%s%s%s%s%s", str0to9,str0to9,str0to9,str0to9,str0to9,str0to9,str0to9), // "And unicode: "+UNICODE_2BYTES+" / "+UNICODE_3BYTES, // plus let's do back refs: "Test", "124" }; JsonFactory f = JSON_F; byte[] data = _stringDoc(f, input); // first: require headers, no offsets _testStrings(f, input, data, 0, 100); _testStrings(f, input, data, 0, 3); _testStrings(f, input, data, 0, 1); // then with some offsets: _testStrings(f, input, data, 1, 100); _testStrings(f, input, data, 1, 3); _testStrings(f, input, data, 1, 1); } public void testShortUnicodeStrings() throws IOException { final String repeat = "Test: "+UNICODE_2BYTES; final String[] input = new String[] { repeat, "", ""+UNICODE_3BYTES, ""+UNICODE_2BYTES, // 60 chars, to stay short String.format("%s %c %s %c %s", str0to9, UNICODE_3BYTES, str0to9, UNICODE_2BYTES, str0to9), "Test", repeat, "!" }; JsonFactory f = JSON_F; byte[] data = _stringDoc(f, input); // first: require headers, no offsets _testStrings(f, input, data, 0, 100); _testStrings(f, input, data, 0, 3); _testStrings(f, input, data, 0, 1); // then with some offsets: _testStrings(f, input, data, 1, 100); _testStrings(f, input, data, 1, 3); _testStrings(f, input, data, 1, 1); } public void testLongAsciiStrings() throws IOException { final String[] input = new String[] { // ~100 chars for long(er) content String.format("%s %s %s %s %s %s %s %s %s %s %s %s", str0to9,str0to9,"...",str0to9,"/", str0to9, str0to9,"",str0to9,str0to9,"...",str0to9), LONG_ASCII }; JsonFactory f = JSON_F; byte[] data = _stringDoc(f, input); // first: require headers, no offsets _testStrings(f, input, data, 0, 9000); _testStrings(f, input, data, 0, 1); _testStrings(f, input, data, 0, 3); // then with some offsets: _testStrings(f, input, data, 1, 9000); _testStrings(f, input, data, 1, 3); _testStrings(f, input, data, 1, 1); } public void testLongUnicodeStrings() throws IOException { // ~100 chars for long(er) content final String LONG = String.format("%s %s %s %s %s%s %s %s %s %s %s %s%c %s", str0to9,str0to9,UNICODE_2BYTES,str0to9,UNICODE_3BYTES,UNICODE_3BYTES, str0to9, str0to9,UNICODE_3BYTES,str0to9,str0to9,UNICODE_2BYTES,UNICODE_2BYTES,str0to9); final String[] input = new String[] { // let's vary length slightly to try to trigger edge conditions LONG, LONG + ".", LONG + "..", LONG + "..." }; JsonFactory f = JSON_F; byte[] data = _stringDoc(f, input); // first: require headers, no offsets _testStrings(f, input, data, 0, 9000); _testStrings(f, input, data, 0, 3); _testStrings(f, input, data, 0, 1); // then with some offsets: _testStrings(f, input, data, 1, 9000); _testStrings(f, input, data, 1, 3); _testStrings(f, input, data, 1, 1); } private void _testStrings(JsonFactory f, String[] values, byte[] data, int offset, int readSize) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); // start with "no token" assertNull(r.currentToken()); assertToken(JsonToken.START_ARRAY, r.nextToken()); for (int i = 0; i < values.length; ++i) { assertToken(JsonToken.VALUE_STRING, r.nextToken()); assertEquals(values[i], r.currentText()); // 13-May-2017, tatu: Rules of whether efficient char[] does or does not // exist vary... So let's NOT try to determine at this point. // assertTrue(r.parser().hasTextCharacters()); } assertToken(JsonToken.END_ARRAY, r.nextToken()); // and end up with "no token" as well assertNull(r.nextToken()); assertTrue(r.isClosed()); } private byte[] _stringDoc(JsonFactory f, String[] input) throws IOException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(100); JsonGenerator g = f.createGenerator(bytes); g.writeStartArray(); for (int i = 0; i < input.length; ++i) { g.writeString(input[i]); } g.writeEndArray(); g.close(); return bytes.toByteArray(); } } AsyncStringObjectTest.java000066400000000000000000000110061356164247300354750ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncStringObjectTest extends AsyncTestBase { private final static String STR0_9 = "0123456789"; private final static String ASCII_SHORT_NAME = "a"+STR0_9+"z"; private final static String UNICODE_SHORT_NAME = "Unicode"+UNICODE_3BYTES+"RlzOk"; private final static String UNICODE_LONG_NAME = String.format( "Unicode-"+UNICODE_3BYTES+"-%s-%s-%s-"+UNICODE_2BYTES+"-%s-%s-%s-"+UNICODE_3BYTES+"-%s-%s-%s", STR0_9, STR0_9, STR0_9, STR0_9, STR0_9, STR0_9, STR0_9, STR0_9, STR0_9); private final JsonFactory JSON_F = new JsonFactory(); public void testBasicFieldsNames() throws IOException { final String json = aposToQuotes(String.format("{'%s':'%s','%s':'%s','%s':'%s'}", UNICODE_SHORT_NAME, UNICODE_LONG_NAME, UNICODE_LONG_NAME, UNICODE_SHORT_NAME, ASCII_SHORT_NAME, ASCII_SHORT_NAME)); final JsonFactory f = JSON_F; byte[] data = _jsonDoc(json); _testBasicFieldsNames(f, data, 0, 100); _testBasicFieldsNames(f, data, 0, 3); _testBasicFieldsNames(f, data, 0, 1); _testBasicFieldsNames(f, data, 1, 100); _testBasicFieldsNames(f, data, 1, 3); _testBasicFieldsNames(f, data, 1, 1); } private void _testBasicFieldsNames(JsonFactory f, byte[] data, int offset, int readSize) throws IOException { _testBasicFieldsNames2(f, data, offset, readSize, true); _testBasicFieldsNames2(f, data, offset, readSize, false); } private void _testBasicFieldsNames2(JsonFactory f, byte[] data, int offset, int readSize, boolean verifyContents) throws IOException { AsyncReaderWrapper r = asyncForBytes(f, readSize, data, offset); // start with "no token" assertNull(r.currentToken()); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); if (verifyContents) { assertEquals(UNICODE_SHORT_NAME, r.currentName()); assertEquals(UNICODE_SHORT_NAME, r.currentText()); } assertToken(JsonToken.VALUE_STRING, r.nextToken()); // also, should always be accessible this way: if (verifyContents) { assertTrue(r.parser().hasTextCharacters()); assertEquals(UNICODE_LONG_NAME, r.currentText()); } assertToken(JsonToken.FIELD_NAME, r.nextToken()); if (verifyContents) { assertEquals(UNICODE_LONG_NAME, r.currentName()); assertEquals(UNICODE_LONG_NAME, r.currentText()); } assertToken(JsonToken.VALUE_STRING, r.nextToken()); if (verifyContents) { assertEquals(UNICODE_SHORT_NAME, r.currentText()); } // and ASCII entry assertToken(JsonToken.FIELD_NAME, r.nextToken()); if (verifyContents) { assertEquals(ASCII_SHORT_NAME, r.currentName()); assertEquals(ASCII_SHORT_NAME, r.currentText()); } assertToken(JsonToken.VALUE_STRING, r.nextToken()); if (verifyContents) { assertEquals(ASCII_SHORT_NAME, r.currentText()); } assertToken(JsonToken.END_OBJECT, r.nextToken()); assertNull(r.nextToken()); // Second round, try with alternate read method if (verifyContents) { r = asyncForBytes(f, readSize, data, offset); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals(UNICODE_SHORT_NAME, r.currentTextViaWriter()); assertToken(JsonToken.VALUE_STRING, r.nextToken()); assertEquals(UNICODE_LONG_NAME, r.currentTextViaWriter()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals(UNICODE_LONG_NAME, r.currentTextViaWriter()); assertToken(JsonToken.VALUE_STRING, r.nextToken()); assertEquals(UNICODE_SHORT_NAME, r.currentTextViaWriter()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals(ASCII_SHORT_NAME, r.currentTextViaWriter()); assertToken(JsonToken.VALUE_STRING, r.nextToken()); assertEquals(ASCII_SHORT_NAME, r.currentTextViaWriter()); assertToken(JsonToken.END_OBJECT, r.nextToken()); } } } AsyncTokenFilterTest.java000066400000000000000000000052251356164247300353340ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.filter.FilteringParserDelegate; import com.fasterxml.jackson.core.filter.TokenFilter; // [core#462], [core#463] public class AsyncTokenFilterTest extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); private final static String INPUT_STRING = aposToQuotes("{'a': 1, 'b': [2, {'c': 3}]}"); private final static byte[] INPUT_BYTES = utf8Bytes(INPUT_STRING); private final static TokenFilter TOKEN_FILTER = new TokenFilter() { @Override public TokenFilter includeProperty(String name) { return name == "a" ? TokenFilter.INCLUDE_ALL : null; } }; private final static JsonToken[] EXPECTED_TOKENS = new JsonToken[]{ JsonToken.START_OBJECT, JsonToken.FIELD_NAME, JsonToken.VALUE_NUMBER_INT, JsonToken.END_OBJECT }; // Passes if (but only if) all content is actually available public void testFilteredNonBlockingParserAllContent() throws IOException { NonBlockingJsonParser nonBlockingParser = (NonBlockingJsonParser) JSON_F.createNonBlockingByteArrayParser(); FilteringParserDelegate filteredParser = new FilteringParserDelegate(nonBlockingParser, TOKEN_FILTER, true, true); nonBlockingParser.feedInput(INPUT_BYTES, 0, INPUT_BYTES.length); int expectedIdx = 0; while (expectedIdx < EXPECTED_TOKENS.length) { // grab next token JsonToken actual = filteredParser.nextToken(); // make sure it's the right one and mark it as seen. assertToken(EXPECTED_TOKENS[expectedIdx], actual); expectedIdx++; } filteredParser.close(); nonBlockingParser.close(); } public void testSkipChildrenFailOnSplit() throws IOException { NonBlockingJsonParser nbParser = (NonBlockingJsonParser) JSON_F.createNonBlockingByteArrayParser(); @SuppressWarnings("resource") FilteringParserDelegate filteredParser = new FilteringParserDelegate(nbParser, TOKEN_FILTER, true, true); nbParser.feedInput(INPUT_BYTES, 0, 5); assertToken(JsonToken.START_OBJECT, nbParser.nextToken()); try { nbParser.skipChildren(); fail("Should not pass!"); } catch (JsonParseException e) { verifyException(e, "not enough content available"); verifyException(e, "skipChildren()"); } nbParser.close(); filteredParser.close(); } } AsyncUnicodeHandlingTest.java000066400000000000000000000064051356164247300361420ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/asyncpackage com.fasterxml.jackson.core.json.async; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncUnicodeHandlingTest extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); public void testShortUnicodeWithSurrogates() throws IOException { JsonFactory f = JSON_F; // first, no buffer boundaries _testUnicodeWithSurrogates(f, 28, 99); _testUnicodeWithSurrogates(f, 53, 99); // then small chunks _testUnicodeWithSurrogates(f, 28, 3); _testUnicodeWithSurrogates(f, 53, 5); // and finally one-by-one _testUnicodeWithSurrogates(f, 28, 1); _testUnicodeWithSurrogates(f, 53, 1); } public void testLongUnicodeWithSurrogates() throws IOException { JsonFactory f = JSON_F; _testUnicodeWithSurrogates(f, 230, Integer.MAX_VALUE); _testUnicodeWithSurrogates(f, 700, Integer.MAX_VALUE); _testUnicodeWithSurrogates(f, 9600, Integer.MAX_VALUE); _testUnicodeWithSurrogates(f, 230, 3); _testUnicodeWithSurrogates(f, 700, 3); _testUnicodeWithSurrogates(f, 9600, 3); _testUnicodeWithSurrogates(f, 230, 1); _testUnicodeWithSurrogates(f, 700, 1); _testUnicodeWithSurrogates(f, 9600, 1); } private void _testUnicodeWithSurrogates(JsonFactory f, int length, int readSize) throws IOException { final String SURROGATE_CHARS = "\ud834\udd1e"; StringBuilder sb = new StringBuilder(length+200); while (sb.length() < length) { sb.append(SURROGATE_CHARS); sb.append(sb.length()); if ((sb.length() & 1) == 1) { sb.append("\u00A3"); } else { sb.append("\u3800"); } } final String TEXT = sb.toString(); final String quoted = quote(TEXT); byte[] data = _jsonDoc(quoted); AsyncReaderWrapper r = asyncForBytes(f, readSize, data, 0); assertToken(JsonToken.VALUE_STRING, r.nextToken()); assertEquals(TEXT, r.currentText()); assertNull(r.nextToken()); r.close(); // Then same but skipping r = asyncForBytes(f, readSize, data, 0); assertToken(JsonToken.VALUE_STRING, r.nextToken()); assertNull(r.nextToken()); r.close(); // Also, verify that it works as field name data = _jsonDoc("{"+quoted+":true}"); r = asyncForBytes(f, readSize, data, 0); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertEquals(TEXT, r.currentName()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); assertToken(JsonToken.END_OBJECT, r.nextToken()); assertNull(r.nextToken()); r.close(); // and skipping r = asyncForBytes(f, readSize, data, 0); assertToken(JsonToken.START_OBJECT, r.nextToken()); assertToken(JsonToken.FIELD_NAME, r.nextToken()); assertToken(JsonToken.VALUE_TRUE, r.nextToken()); assertToken(JsonToken.END_OBJECT, r.nextToken()); r.close(); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/json/async/ConfigTest.java000066400000000000000000000021071356164247300333700ustar00rootroot00000000000000package com.fasterxml.jackson.core.json.async; import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class ConfigTest extends AsyncTestBase { private final JsonFactory DEFAULT_F = new JsonFactory(); public void testFactoryDefaults() throws IOException { assertTrue(DEFAULT_F.canParseAsync()); } public void testAsyncParerDefaults() throws IOException { byte[] data = _jsonDoc("[true,false]"); AsyncReaderWrapper r = asyncForBytes(DEFAULT_F, 100, data, 0); JsonParser p = r.parser(); assertTrue(p.canParseAsync()); assertNull(p.getCodec()); assertNull(p.getInputSource()); assertEquals(-1, p.releaseBuffered(new StringWriter())); assertEquals(0, p.releaseBuffered(new ByteArrayOutputStream())); assertToken(JsonToken.START_ARRAY, r.nextToken()); assertEquals(11, p.releaseBuffered(new ByteArrayOutputStream())); p.close(); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/main/000077500000000000000000000000001356164247300273165ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/main/TestArrayParsing.java000066400000000000000000000144571356164247300334360ustar00rootroot00000000000000package com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.json.JsonReadFeature; /** * Set of additional unit for verifying array parsing, specifically * edge cases. */ public class TestArrayParsing extends com.fasterxml.jackson.core.BaseTest { public void testValidEmpty() throws Exception { final String DOC = "[ \n ]"; JsonParser jp = createParserUsingStream(DOC, "UTF-8"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertNull(jp.nextToken()); jp.close(); } public void testInvalidEmptyMissingClose() throws Exception { final String DOC = "[ "; JsonParser jp = createParserUsingStream(DOC, "UTF-8"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); try { jp.nextToken(); fail("Expected a parsing error for missing array close marker"); } catch (JsonParseException jex) { verifyException(jex, "expected close marker for ARRAY"); } jp.close(); } public void testInvalidMissingFieldName() throws Exception { final String DOC = "[ : 3 ] "; JsonParser jp = createParserUsingStream(DOC, "UTF-8"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); try { jp.nextToken(); fail("Expected a parsing error for odd character"); } catch (JsonParseException jex) { verifyException(jex, "Unexpected character"); } jp.close(); } public void testInvalidExtraComma() throws Exception { final String DOC = "[ 24, ] "; JsonParser jp = createParserUsingStream(DOC, "UTF-8"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(24, jp.getIntValue()); try { jp.nextToken(); fail("Expected a parsing error for missing array close marker"); } catch (JsonParseException jex) { verifyException(jex, "expected a value"); } 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 = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_MISSING_VALUES) .build(); 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 = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_MISSING_VALUES) .build(); 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(); } } TestGeneratorArray.java000066400000000000000000000072321356164247300336730ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; import java.io.*; /** * Set of basic unit tests for verifying that the Array write methods * of {@link JsonGenerator} work as expected. */ public class TestGeneratorArray extends com.fasterxml.jackson.core.BaseTest { public void testEmptyArrayWrite() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); JsonStreamContext ctxt = gen.getOutputContext(); assertTrue(ctxt.inRoot()); assertFalse(ctxt.inArray()); assertFalse(ctxt.inObject()); assertEquals(0, ctxt.getEntryCount()); assertEquals(0, ctxt.getCurrentIndex()); gen.writeStartArray(); ctxt = gen.getOutputContext(); assertFalse(ctxt.inRoot()); assertTrue(ctxt.inArray()); assertFalse(ctxt.inObject()); assertEquals(0, ctxt.getEntryCount()); assertEquals(0, ctxt.getCurrentIndex()); gen.writeEndArray(); ctxt = gen.getOutputContext(); assertTrue("Should be in root, was "+ctxt.typeDesc(), ctxt.inRoot()); assertFalse(ctxt.inArray()); assertFalse(ctxt.inObject()); assertEquals(1, ctxt.getEntryCount()); // Index won't yet move assertEquals(0, ctxt.getCurrentIndex()); gen.close(); String docStr = sw.toString(); JsonParser jp = createParserUsingReader(docStr); assertEquals(JsonToken.START_ARRAY, jp.nextToken()); assertEquals(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); // Ok, then array with nested empty array sw = new StringWriter(); gen = new JsonFactory().createGenerator(sw); gen.writeStartArray(); gen.writeStartArray(); gen.writeEndArray(); gen.writeEndArray(); gen.close(); docStr = sw.toString(); jp = createParserUsingReader(docStr); assertEquals(JsonToken.START_ARRAY, jp.nextToken()); assertEquals(JsonToken.START_ARRAY, jp.nextToken()); assertEquals(JsonToken.END_ARRAY, jp.nextToken()); assertEquals(JsonToken.END_ARRAY, jp.nextToken()); assertEquals(null, jp.nextToken()); jp.close(); } public void testInvalidArrayWrite() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); gen.writeStartArray(); // Mismatch: try { gen.writeEndObject(); fail("Expected an exception for mismatched array/object write"); } catch (JsonGenerationException e) { verifyException(e, "Current context not Object"); } gen.close(); } public void testSimpleArrayWrite() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); gen.writeStartArray(); gen.writeNumber(13); gen.writeBoolean(true); gen.writeString("foobar"); gen.writeEndArray(); gen.close(); String docStr = sw.toString(); JsonParser jp = createParserUsingReader(docStr); assertEquals(JsonToken.START_ARRAY, jp.nextToken()); assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(13, jp.getIntValue()); assertEquals(JsonToken.VALUE_TRUE, jp.nextToken()); assertEquals(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("foobar", jp.getText()); assertEquals(JsonToken.END_ARRAY, jp.nextToken()); assertEquals(null, jp.nextToken()); jp.close(); } } TestGeneratorClosing.java000066400000000000000000000135551356164247300342200ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.testsupport.ByteOutputStreamForTesting; import com.fasterxml.jackson.core.testsupport.StringWriterForTesting; import java.io.*; /** * Set of basic unit tests that verify aspect of closing a * {@link JsonGenerator} instance. This includes both closing * of physical resources (target), and logical content * (json content tree) *

* Specifically, features * JsonGenerator.Feature#AUTO_CLOSE_TARGET * and * JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT * are tested. */ public class TestGeneratorClosing extends BaseTest { /* /********************************************************** /* Unit tests /********************************************************** */ /** * This unit test checks the default behaviour; with no auto-close, no * automatic closing should occur, nor explicit one unless specific * forcing method is used. */ public void testNoAutoCloseGenerator() throws Exception { JsonFactory f = new JsonFactory(); // Check the default settings assertTrue(f.isEnabled(JsonGenerator.Feature.AUTO_CLOSE_TARGET)); // then change f.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); assertFalse(f.isEnabled(JsonGenerator.Feature.AUTO_CLOSE_TARGET)); @SuppressWarnings("resource") ByteOutputStreamForTesting output = new ByteOutputStreamForTesting(); JsonGenerator jg = f.createGenerator(output); // shouldn't be closed to begin with... assertFalse(output.isClosed()); jg.writeNumber(39); // regular close won't close it either: jg.close(); assertFalse(output.isClosed()); } public void testCloseGenerator() throws Exception { JsonFactory f = new JsonFactory(); f.enable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); @SuppressWarnings("resource") ByteOutputStreamForTesting output = new ByteOutputStreamForTesting(); JsonGenerator jg = f.createGenerator(output); // shouldn't be closed to begin with... assertFalse(output.isClosed()); jg.writeNumber(39); // but close() should now close the writer jg.close(); assertTrue(output.isClosed()); } public void testNoAutoCloseOutputStream() throws Exception { JsonFactory f = new JsonFactory(); f.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); @SuppressWarnings("resource") ByteOutputStreamForTesting output = new ByteOutputStreamForTesting(); JsonGenerator jg = f.createGenerator(output, JsonEncoding.UTF8); assertFalse(output.isClosed()); jg.writeNumber(39); jg.close(); assertFalse(output.isClosed()); } public void testAutoCloseArraysAndObjects() throws Exception { JsonFactory f = new JsonFactory(); // let's verify default setting, first: assertTrue(f.isEnabled(StreamWriteFeature.AUTO_CLOSE_CONTENT)); StringWriter sw = new StringWriter(); // First, test arrays: JsonGenerator jg = f.createGenerator(sw); jg.writeStartArray(); jg.close(); assertEquals("[]", sw.toString()); // Then objects sw = new StringWriter(); jg = f.createGenerator(sw); jg.writeStartObject(); jg.close(); assertEquals("{}", sw.toString()); } public void testNoAutoCloseArraysAndObjects() throws Exception { JsonFactory f = JsonFactory.builder() .disable(StreamWriteFeature.AUTO_CLOSE_CONTENT) .build(); StringWriter sw = new StringWriter(); JsonGenerator jg = f.createGenerator(sw); jg.writeStartArray(); jg.close(); // shouldn't close assertEquals("[", sw.toString()); // Then objects sw = new StringWriter(); jg = f.createGenerator(sw); jg.writeStartObject(); jg.close(); assertEquals("{", sw.toString()); } @SuppressWarnings("resource") public void testAutoFlushOrNot() throws Exception { JsonFactory f = new JsonFactory(); assertTrue(f.isEnabled(StreamWriteFeature.FLUSH_PASSED_TO_STREAM)); StringWriterForTesting sw = new StringWriterForTesting(); JsonGenerator jg = f.createGenerator(sw); jg.writeStartArray(); jg.writeEndArray(); assertEquals(0, sw.flushCount); jg.flush(); assertEquals(1, sw.flushCount); jg.close(); // ditto with stream ByteOutputStreamForTesting bytes = new ByteOutputStreamForTesting(); jg = f.createGenerator(bytes, JsonEncoding.UTF8); jg.writeStartArray(); jg.writeEndArray(); assertEquals(0, bytes.flushCount); jg.flush(); assertEquals(1, bytes.flushCount); assertEquals(2, bytes.toByteArray().length); jg.close(); // then disable and we should not see flushing again... f = JsonFactory.builder() .disable(StreamWriteFeature.FLUSH_PASSED_TO_STREAM) .build(); // first with a Writer sw = new StringWriterForTesting(); jg = f.createGenerator(sw); jg.writeStartArray(); jg.writeEndArray(); assertEquals(0, sw.flushCount); jg.flush(); assertEquals(0, sw.flushCount); jg.close(); assertEquals("[]", sw.toString()); // and then with OutputStream bytes = new ByteOutputStreamForTesting(); jg = f.createGenerator(bytes, JsonEncoding.UTF8); jg.writeStartArray(); jg.writeEndArray(); assertEquals(0, bytes.flushCount); jg.flush(); assertEquals(0, bytes.flushCount); jg.close(); assertEquals(2, bytes.toByteArray().length); } } TestGeneratorCopy.java000066400000000000000000000050631356164247300335270ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; import java.io.*; /** * Set of basic unit tests for verifying that copy-through methods * of {@link JsonGenerator} work as expected. */ public class TestGeneratorCopy extends BaseTest { public void testCopyRootTokens() throws IOException { JsonFactory jf = new JsonFactory(); final String DOC = "\"text\\non two lines\" true false 2.0"; JsonParser jp = jf.createParser(new StringReader(DOC)); StringWriter sw = new StringWriter(); JsonGenerator gen = jf.createGenerator(sw); JsonToken t; while ((t = jp.nextToken()) != null) { gen.copyCurrentEvent(jp); // should not change parser state: assertToken(t, jp.currentToken()); } jp.close(); gen.close(); assertEquals("\"text\\non two lines\" true false 2.0", sw.toString()); } public void testCopyArrayTokens() throws IOException { JsonFactory jf = new JsonFactory(); final String DOC = "123 [ 1, null, [ false ] ]"; JsonParser jp = jf.createParser(new StringReader(DOC)); StringWriter sw = new StringWriter(); JsonGenerator gen = jf.createGenerator(sw); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); gen.copyCurrentEvent(jp); // should not change parser state: 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.currentToken()); jp.close(); gen.close(); assertEquals("123 [1,null,[false]]", sw.toString()); } public void testCopyObjectTokens() throws IOException { JsonFactory jf = new JsonFactory(); final String DOC = "{ \"a\":1, \"b\":[{ \"c\" : null }] }"; JsonParser jp = jf.createParser(new StringReader(DOC)); StringWriter sw = new StringWriter(); JsonGenerator gen = jf.createGenerator(sw); assertToken(JsonToken.START_OBJECT, jp.nextToken()); gen.copyCurrentStructure(jp); // which will advance parser to matching end Object assertToken(JsonToken.END_OBJECT, jp.currentToken()); jp.close(); gen.close(); assertEquals("{\"a\":1,\"b\":[{\"c\":null}]}", sw.toString()); } } TestGeneratorMisc.java000066400000000000000000000207321356164247300335100ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import java.io.*; import java.math.BigDecimal; import java.math.BigInteger; import java.util.concurrent.atomic.*; import com.fasterxml.jackson.core.*; /** * Set of basic unit tests for verifying basic generator * features. */ @SuppressWarnings("resource") public class TestGeneratorMisc extends com.fasterxml.jackson.core.BaseTest { private final JsonFactory JSON_F = new JsonFactory(); /* /********************************************************** /* Tests for closing, status /********************************************************** */ public void testIsClosed() throws IOException { for (int i = 0; i < 2; ++i) { boolean stream = ((i & 1) == 0); JsonGenerator jg = stream ? JSON_F.createGenerator(new StringWriter()) : JSON_F.createGenerator(new ByteArrayOutputStream(), JsonEncoding.UTF8) ; assertFalse(jg.isClosed()); jg.writeStartArray(); jg.writeNumber(-1); jg.writeEndArray(); assertFalse(jg.isClosed()); jg.close(); assertTrue(jg.isClosed()); jg.close(); assertTrue(jg.isClosed()); } } // Also, "very simple" objects are supported even without Codec: public void testSimpleWriteObject() throws IOException { // note: NOT mapping factory, for this test StringWriter sw = new StringWriter(); JsonGenerator gen = JSON_F.createGenerator(sw); gen.writeStartArray(); // simple wrappers first gen.writeObject(1); gen.writeObject((short) -2); gen.writeObject((long) 3); gen.writeObject((byte) -4); gen.writeObject(0.25); gen.writeObject(-0.125f); gen.writeObject(Boolean.TRUE); gen.close(); String act = sw.toString().trim(); assertEquals("[1,-2,3,-4,0.25,-0.125,true]", act); // then other basic types sw = new StringWriter(); gen = JSON_F.createGenerator(sw); gen.writeStartArray(); gen.writeObject(BigInteger.valueOf(1234)); gen.writeObject(new BigDecimal(0.5)); gen.writeEndArray(); gen.close(); act = sw.toString().trim(); assertEquals("[1234,0.5]", act); // then Atomic types sw = new StringWriter(); gen = JSON_F.createGenerator(sw); gen.writeStartArray(); gen.writeObject(new AtomicBoolean(false)); gen.writeObject(new AtomicInteger(13)); gen.writeObject(new AtomicLong(-127L)); gen.writeEndArray(); gen.close(); act = sw.toString().trim(); assertEquals("[false,13,-127]", act); } /* /********************************************************** /* Tests for raw output /********************************************************** */ public void testRaw() throws IOException { StringWriter sw = new StringWriter(); JsonGenerator gen = JSON_F.createGenerator(sw); gen.writeStartArray(); gen.writeRaw("-123, true"); gen.writeRaw(", \"x\" "); gen.writeEndArray(); gen.close(); JsonParser jp = createParserUsingReader(sw.toString()); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(-123, jp.getIntValue()); assertToken(JsonToken.VALUE_TRUE, jp.nextToken()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("x", jp.getText()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); } public void testRawValue() throws IOException { StringWriter sw = new StringWriter(); JsonGenerator gen = JSON_F.createGenerator(sw); gen.writeStartArray(); gen.writeRawValue("7"); gen.writeRawValue("[ null ]"); gen.writeRawValue("false"); gen.writeEndArray(); gen.close(); JsonParser jp = createParserUsingReader(sw.toString()); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(7, jp.getIntValue()); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NULL, jp.nextToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_FALSE, jp.nextToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); } /* /********************************************************** /* Tests for object writing /********************************************************** */ /** * Unit test that tries to trigger buffer-boundary conditions */ public void testLongerObjects() throws Exception { _testLongerObjects(JSON_F, 0); _testLongerObjects(JSON_F, 1); _testLongerObjects(JSON_F, 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; } 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; } g.writeFieldName(name); g.writeNumber(index-1); } g.writeRaw('\n'); } } 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(); } /* /********************************************************** /* Tests, other /********************************************************** */ // NOTE: test for binary data under `base64/` tests public void testAsEmbedded() throws Exception { JsonGenerator g; StringWriter sw = new StringWriter(); g = JSON_F.createGenerator(sw); g.writeEmbeddedObject(null); g.close(); assertEquals("null", sw.toString()); ByteArrayOutputStream bytes = new ByteArrayOutputStream(100); g = JSON_F.createGenerator(bytes); g.writeEmbeddedObject(null); g.close(); assertEquals("null", bytes.toString("UTF-8")); // also, for fun, try illegal unknown thingy try { g = JSON_F.createGenerator(bytes); // try writing a Class object g.writeEmbeddedObject(getClass()); fail("Expected an exception"); g.close(); // never gets here } catch (JsonGenerationException e) { verifyException(e, "No native support for"); } } } TestGeneratorObject.java000066400000000000000000000164141356164247300340250ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; import java.io.*; import java.math.BigDecimal; /** * Set of basic unit tests for verifying that the Object write methods * of {@link JsonGenerator} work as expected. */ public class TestGeneratorObject extends BaseTest { public void testEmptyObjectWrite() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); JsonStreamContext ctxt = gen.getOutputContext(); assertTrue(ctxt.inRoot()); assertFalse(ctxt.inArray()); assertFalse(ctxt.inObject()); assertEquals(0, ctxt.getEntryCount()); assertEquals(0, ctxt.getCurrentIndex()); gen.writeStartObject(); ctxt = gen.getOutputContext(); assertFalse(ctxt.inRoot()); assertFalse(ctxt.inArray()); assertTrue(ctxt.inObject()); assertEquals(0, ctxt.getEntryCount()); assertEquals(0, ctxt.getCurrentIndex()); gen.writeEndObject(); ctxt = gen.getOutputContext(); assertTrue(ctxt.inRoot()); assertFalse(ctxt.inArray()); assertFalse(ctxt.inObject()); assertEquals(1, ctxt.getEntryCount()); // Index won't yet move assertEquals(0, ctxt.getCurrentIndex()); gen.close(); String docStr = sw.toString(); JsonParser jp = createParserUsingReader(docStr); assertEquals(JsonToken.START_OBJECT, jp.nextToken()); assertEquals(JsonToken.END_OBJECT, jp.nextToken()); assertEquals(null, jp.nextToken()); jp.close(); } public void testInvalidObjectWrite() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); gen.writeStartObject(); // Mismatch: try { gen.writeEndArray(); fail("Expected an exception for mismatched array/object write"); } catch (JsonGenerationException e) { verifyException(e, "Current context not Array"); } gen.close(); } public void testSimpleObjectWrite() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); gen.writeStartObject(); gen.writeFieldName("first"); gen.writeNumber(-901); gen.writeFieldName("sec"); gen.writeBoolean(false); gen.writeFieldName("3rd!"); // JSON field names are just strings, not ids with restrictions gen.writeString("yee-haw"); gen.writeEndObject(); gen.close(); String docStr = sw.toString(); JsonParser jp = createParserUsingReader(docStr); assertEquals(JsonToken.START_OBJECT, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("first", jp.getText()); assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(-901, jp.getIntValue()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("sec", jp.getText()); assertEquals(JsonToken.VALUE_FALSE, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("3rd!", jp.getText()); assertEquals(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("yee-haw", jp.getText()); assertEquals(JsonToken.END_OBJECT, jp.nextToken()); assertEquals(null, jp.nextToken()); jp.close(); } /** * Methods to test functionality added for [JACKSON-26] */ public void testConvenienceMethods() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); gen.writeStartObject(); final BigDecimal dec = new BigDecimal("0.1"); final String TEXT = "\"some\nString!\""; gen.writeNullField("null"); gen.writeBooleanField("bt", true); gen.writeBooleanField("bf", false); gen.writeNumberField("int", -1289); gen.writeNumberField("dec", dec); gen.writeObjectFieldStart("ob"); gen.writeStringField("str", TEXT); gen.writeEndObject(); gen.writeArrayFieldStart("arr"); gen.writeEndArray(); gen.writeEndObject(); gen.close(); String docStr = sw.toString(); JsonParser jp = createParserUsingReader(docStr); assertEquals(JsonToken.START_OBJECT, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("null", jp.getText()); assertEquals(JsonToken.VALUE_NULL, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("bt", jp.getText()); assertEquals(JsonToken.VALUE_TRUE, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("bf", jp.getText()); assertEquals(JsonToken.VALUE_FALSE, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("int", jp.getText()); assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("dec", jp.getText()); assertEquals(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("ob", jp.getText()); assertEquals(JsonToken.START_OBJECT, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("str", jp.getText()); assertEquals(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals(TEXT, getAndVerifyText(jp)); assertEquals(JsonToken.END_OBJECT, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("arr", jp.getText()); assertEquals(JsonToken.START_ARRAY, jp.nextToken()); assertEquals(JsonToken.END_ARRAY, jp.nextToken()); assertEquals(JsonToken.END_OBJECT, jp.nextToken()); assertEquals(null, jp.nextToken()); jp.close(); } /** * Tests to cover [JACKSON-164] */ public void testConvenienceMethodsWithNulls() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); gen.writeStartObject(); gen.writeStringField("str", null); gen.writeNumberField("num", null); gen.writeObjectField("obj", null); gen.writeEndObject(); gen.close(); String docStr = sw.toString(); JsonParser jp = createParserUsingReader(docStr); assertEquals(JsonToken.START_OBJECT, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("str", jp.getCurrentName()); assertEquals(JsonToken.VALUE_NULL, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("num", jp.getCurrentName()); assertEquals(JsonToken.VALUE_NULL, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("obj", jp.getCurrentName()); assertEquals(JsonToken.VALUE_NULL, jp.nextToken()); assertEquals(JsonToken.END_OBJECT, jp.nextToken()); jp.close(); } } TestNumberParsing.java000066400000000000000000000114621356164247300335220ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.NumberInput; /** * Set of basic unit tests for verifying that the low-level number * handling methods work as expected. */ public class TestNumberParsing extends com.fasterxml.jackson.core.BaseTest { public void testIntParsing() throws Exception { char[] testChars = "123456789".toCharArray(); assertEquals(3, NumberInput.parseInt(testChars, 2, 1)); assertEquals(123, NumberInput.parseInt(testChars, 0, 3)); assertEquals(2345, NumberInput.parseInt(testChars, 1, 4)); assertEquals(9, NumberInput.parseInt(testChars, 8, 1)); assertEquals(456789, NumberInput.parseInt(testChars, 3, 6)); assertEquals(23456, NumberInput.parseInt(testChars, 1, 5)); assertEquals(123456789, NumberInput.parseInt(testChars, 0, 9)); testChars = "32".toCharArray(); assertEquals(32, NumberInput.parseInt(testChars, 0, 2)); testChars = "189".toCharArray(); assertEquals(189, NumberInput.parseInt(testChars, 0, 3)); testChars = "10".toCharArray(); assertEquals(10, NumberInput.parseInt(testChars, 0, 2)); assertEquals(0, NumberInput.parseInt(testChars, 1, 1)); } public void testIntParsingWithStrings() throws Exception { assertEquals(3, NumberInput.parseInt("3")); assertEquals(0, NumberInput.parseInt("0")); assertEquals(-3, NumberInput.parseInt("-3")); assertEquals(27, NumberInput.parseInt("27")); assertEquals(-31, NumberInput.parseInt("-31")); assertEquals(271, NumberInput.parseInt("271")); assertEquals(-131, NumberInput.parseInt("-131")); assertEquals(2709, NumberInput.parseInt("2709")); assertEquals(-9999, NumberInput.parseInt("-9999")); assertEquals(Integer.MIN_VALUE, NumberInput.parseInt(""+Integer.MIN_VALUE)); assertEquals(Integer.MAX_VALUE, NumberInput.parseInt(""+Integer.MAX_VALUE)); } public void testLongParsing() throws Exception { char[] testChars = "123456789012345678".toCharArray(); assertEquals(123456789012345678L, NumberInput.parseLong(testChars, 0, testChars.length)); } // Unit test for [JACKSON-491] public void testLongBoundsChecks() throws Exception { String minLong = String.valueOf(Long.MIN_VALUE).substring(1); String maxLong = String.valueOf(Long.MAX_VALUE); final String VALUE_491 = "1323372036854775807"; // is within range (JACKSON-491) final String OVERFLOW = "9999999999999999999"; // and this one is clearly out assertTrue(NumberInput.inLongRange(minLong, true)); assertTrue(NumberInput.inLongRange(maxLong, false)); assertTrue(NumberInput.inLongRange(VALUE_491, true)); assertTrue(NumberInput.inLongRange(VALUE_491, false)); assertFalse(NumberInput.inLongRange(OVERFLOW, false)); assertFalse(NumberInput.inLongRange(OVERFLOW, true)); char[] cbuf = minLong.toCharArray(); assertTrue(NumberInput.inLongRange(cbuf, 0, cbuf.length, true)); cbuf = maxLong.toCharArray(); assertTrue(NumberInput.inLongRange(cbuf, 0, cbuf.length, false)); cbuf = VALUE_491.toCharArray(); assertTrue(NumberInput.inLongRange(cbuf, 0, cbuf.length, true)); assertTrue(NumberInput.inLongRange(cbuf, 0, cbuf.length, false)); cbuf = OVERFLOW.toCharArray(); assertFalse(NumberInput.inLongRange(cbuf, 0, cbuf.length, true)); assertFalse(NumberInput.inLongRange(cbuf, 0, cbuf.length, false)); } public void testFloatBoundary146Chars() throws Exception { final char[] arr = new char[50005]; final JsonFactory f = new JsonFactory(); for(int i = 500; i != 9000; ++i) { java.util.Arrays.fill(arr, 0, i, ' '); arr[i] = '-'; arr[i + 1] = '1'; arr[i + 2] = 'e'; arr[i + 3] = '-'; arr[i + 4] = '1'; CharArrayReader r = new CharArrayReader(arr, 0, i+5); JsonParser p = f.createParser(r); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); p.close(); } } public void testFloatBoundary146Bytes() throws Exception { final byte[] arr = new byte[50005]; final JsonFactory f = new JsonFactory(); for(int i = 500; i != 9000; ++i) { java.util.Arrays.fill(arr, 0, i, (byte) 0x20); arr[i] = '-'; arr[i + 1] = '1'; arr[i + 2] = 'e'; arr[i + 3] = '-'; arr[i + 4] = '1'; ByteArrayInputStream in = new ByteArrayInputStream(arr, 0, i+5); JsonParser p = f.createParser(in); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); p.close(); } } } TestParserClosing.java000066400000000000000000000131731356164247300335220ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import static org.junit.Assert.*; import com.fasterxml.jackson.core.*; import java.io.*; /** * Set of basic unit tests that verify that the closing (or not) of * the underlying source occurs as expected and specified * by documentation. */ public class TestParserClosing extends BaseTest { /** * This unit test checks the default behaviour; with no auto-close, no * automatic closing should occur, nor explicit one unless specific * forcing method is used. */ public void testNoAutoCloseReader() throws Exception { final String DOC = "[ 1 ]"; // Check the default settings assertTrue(sharedStreamFactory().isEnabled(StreamReadFeature.AUTO_CLOSE_SOURCE)); // then change JsonFactory f = JsonFactory.builder() .disable(StreamReadFeature.AUTO_CLOSE_SOURCE) .build(); assertFalse(f.isEnabled(StreamReadFeature.AUTO_CLOSE_SOURCE)); { assertFalse(f.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); } @SuppressWarnings("resource") MyReader input = new MyReader(DOC); JsonParser jp = f.createParser(input); // shouldn't be closed to begin with... assertFalse(input.isClosed()); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertNull(jp.nextToken()); // normally would be closed now assertFalse(input.isClosed()); // regular close won't close it either: jp.close(); assertFalse(input.isClosed()); } @SuppressWarnings("resource") public void testAutoCloseReader() throws Exception { final String DOC = "[ 1 ]"; JsonFactory f = JsonFactory.builder() .enable(StreamReadFeature.AUTO_CLOSE_SOURCE) .build(); MyReader input = new MyReader(DOC); JsonParser jp = f.createParser(input); assertFalse(input.isClosed()); assertToken(JsonToken.START_ARRAY, jp.nextToken()); // but can close half-way through jp.close(); assertTrue(input.isClosed()); // And then let's test implicit close at the end too: input = new MyReader(DOC); jp = f.createParser(input); assertFalse(input.isClosed()); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertNull(jp.nextToken()); assertTrue(input.isClosed()); } @SuppressWarnings("resource") public void testNoAutoCloseInputStream() throws Exception { final String DOC = "[ 1 ]"; JsonFactory f = JsonFactory.builder() .disable(StreamReadFeature.AUTO_CLOSE_SOURCE) .build(); MyStream input = new MyStream(DOC.getBytes("UTF-8")); JsonParser jp = f.createParser(input); // shouldn't be closed to begin with... assertFalse(input.isClosed()); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertNull(jp.nextToken()); // normally would be closed now assertFalse(input.isClosed()); // regular close won't close it either: jp.close(); assertFalse(input.isClosed()); } // [JACKSON-287] public void testReleaseContentBytes() throws Exception { byte[] input = "[1]foobar".getBytes("UTF-8"); JsonParser jp = new JsonFactory().createParser(input); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); ByteArrayOutputStream out = new ByteArrayOutputStream(); // theoretically could have only read subset; but current impl is more greedy assertEquals(6, jp.releaseBuffered(out)); assertArrayEquals("foobar".getBytes("UTF-8"), out.toByteArray()); jp.close(); } public void testReleaseContentChars() throws Exception { JsonParser jp = new JsonFactory().createParser("[true]xyz"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_TRUE, jp.nextToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); StringWriter sw = new StringWriter(); // theoretically could have only read subset; but current impl is more greedy assertEquals(3, jp.releaseBuffered(sw)); assertEquals("xyz", sw.toString()); jp.close(); } /* /********************************************************** /* Helper classes /********************************************************** */ final static class MyReader extends StringReader { boolean mIsClosed = false; public MyReader(String contents) { super(contents); } @Override public void close() { mIsClosed = true; super.close(); } public boolean isClosed() { return mIsClosed; } } final static class MyStream extends ByteArrayInputStream { boolean mIsClosed = false; public MyStream(byte[] data) { super(data); } @Override public void close() throws IOException { mIsClosed = true; super.close(); } public boolean isClosed() { return mIsClosed; } } } TestParserFeatures.java000066400000000000000000000101611356164247300336740ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.json.JsonReadFeature; /** * Unit tests for verifying that additional JsonParser.Feature * settings work as expected. */ public class TestParserFeatures extends com.fasterxml.jackson.core.BaseTest { public void testDefaultSettings() throws Exception { JsonFactory f = new JsonFactory(); assertTrue(f.isEnabled(StreamReadFeature.AUTO_CLOSE_SOURCE)); JsonParser p = f.createParser(new StringReader("{}")); _testDefaultSettings(p); p.close(); p = f.createParser(new ByteArrayInputStream("{}".getBytes("UTF-8"))); _testDefaultSettings(p); p.close(); } @SuppressWarnings("deprecation") public void testDeprecatedDefaultSettings() throws Exception { JsonFactory f = sharedStreamFactory(); assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_COMMENTS)); assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS)); assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES)); assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_SINGLE_QUOTES)); } public void testQuotesRequired() throws Exception { _testQuotesRequired(false); _testQuotesRequired(true); } // // Tests for [JACKSON-208], unquoted tabs: public void testTabsDefault() throws Exception { _testTabsDefault(false); _testTabsDefault(true); } public void testTabsEnabled() throws Exception { _testTabsEnabled(false); _testTabsEnabled(true); } /* /**************************************************************** /* Secondary test methods /**************************************************************** */ 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 p = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON) ; assertToken(JsonToken.START_OBJECT, p.nextToken()); try { p.nextToken(); } catch (JsonParseException je) { verifyException(je, EXP_ERROR_FRAGMENT); } finally { p.close(); } } // // // Tests for [JACKSON-208] private void _testTabsDefault(boolean useStream) throws Exception { JsonFactory f = new JsonFactory(); // First, let's see that by default unquoted tabs are illegal String JSON = "[\"tab:\t\"]"; JsonParser p = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.START_ARRAY, p.nextToken()); try { p.nextToken(); p.getText(); fail("Expected exception"); } catch (JsonParseException e) { verifyException(e, "Illegal unquoted character"); } finally { p.close(); } } private void _testTabsEnabled(boolean useStream) throws Exception { JsonFactory f = JsonFactory.builder() .configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS, true) .build(); String FIELD = "a\tb"; String VALUE = "\t"; String JSON = "{ "+quote(FIELD)+" : "+quote(VALUE)+"}"; JsonParser p = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); 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(); } } TestParserLinefeeds.java000066400000000000000000000036351356164247300340240ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; import java.io.IOException; /** * Set of basic unit tests for verifying that the basic parser * functionality works as expected. */ public class TestParserLinefeeds extends BaseTest { public void testCR() throws Exception { _testLinefeeds("\r", true); _testLinefeeds("\r", false); } public void testLF() throws Exception { _testLinefeeds("\n", true); _testLinefeeds("\n", false); } public void testCRLF() throws Exception { _testLinefeeds("\r\n", true); _testLinefeeds("\r\n", false); } /* /********************************************************** /* Helper methods /********************************************************** */ private void _testLinefeeds(String lf, boolean useStream) throws IOException { String DOC = "[1,@2,@-478@]"; DOC = DOC.replaceAll("@", lf); JsonParser jp = useStream ? createParserUsingStream(DOC, "UTF-8") : createParserUsingReader(DOC); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertEquals(1, jp.getCurrentLocation().getLineNr()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(1, jp.getIntValue()); assertEquals(1, jp.getCurrentLocation().getLineNr()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(2, jp.getIntValue()); assertEquals(2, jp.getCurrentLocation().getLineNr()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(-478, jp.getIntValue()); assertEquals(3, jp.getCurrentLocation().getLineNr()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertEquals(4, jp.getCurrentLocation().getLineNr()); jp.close(); } } TestParserWithObjects.java000066400000000000000000000137101356164247300343460ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import java.io.*; import com.fasterxml.jackson.core.*; /** * Unit tests for verifying that object mapping functionality can * be accessed using JsonParser. */ public class TestParserWithObjects extends com.fasterxml.jackson.core.BaseTest { /* /********************************************************** /* Test for simple traversal with data mapping /********************************************************** */ public void testNextValue() throws IOException { // Let's test both byte-backed and Reader-based one _testNextValueBasic(false); _testNextValueBasic(true); } // [JACKSON-395] public void testNextValueNested() throws IOException { // Let's test both byte-backed and Reader-based one _testNextValueNested(false); _testNextValueNested(true); } @SuppressWarnings("resource") public void testIsClosed() throws IOException { for (int i = 0; i < 4; ++i) { String JSON = "[ 1, 2, 3 ]"; boolean stream = ((i & 1) == 0); JsonParser jp = stream ? createParserUsingStream(JSON, "UTF-8") : createParserUsingReader(JSON); boolean partial = ((i & 2) == 0); assertFalse(jp.isClosed()); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertFalse(jp.isClosed()); if (partial) { jp.close(); assertTrue(jp.isClosed()); } else { assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertNull(jp.nextToken()); assertTrue(jp.isClosed()); } } } /* /********************************************************** /* Supporting methods /********************************************************** */ private void _testNextValueBasic(boolean useStream) throws IOException { // first array, no change to default JsonParser jp = _getParser("[ 1, 2, 3, 4 ]", useStream); assertToken(JsonToken.START_ARRAY, jp.nextValue()); for (int i = 1; i <= 4; ++i) { assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextValue()); assertEquals(i, jp.getIntValue()); } assertToken(JsonToken.END_ARRAY, jp.nextValue()); assertNull(jp.nextValue()); jp.close(); // then Object, is different jp = _getParser("{ \"3\" :3, \"4\": 4, \"5\" : 5 }", useStream); assertToken(JsonToken.START_OBJECT, jp.nextValue()); for (int i = 3; i <= 5; ++i) { assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextValue()); assertEquals(String.valueOf(i), jp.getCurrentName()); assertEquals(i, jp.getIntValue()); } assertToken(JsonToken.END_OBJECT, jp.nextValue()); assertNull(jp.nextValue()); jp.close(); // and then mixed... jp = _getParser("[ true, [ ], { \"a\" : 3 } ]", useStream); assertToken(JsonToken.START_ARRAY, jp.nextValue()); assertToken(JsonToken.VALUE_TRUE, jp.nextValue()); assertToken(JsonToken.START_ARRAY, jp.nextValue()); assertToken(JsonToken.END_ARRAY, jp.nextValue()); assertToken(JsonToken.START_OBJECT, jp.nextValue()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextValue()); assertEquals("a", jp.getCurrentName()); assertToken(JsonToken.END_OBJECT, jp.nextValue()); assertToken(JsonToken.END_ARRAY, jp.nextValue()); assertNull(jp.nextValue()); jp.close(); } // [JACKSON-395] private void _testNextValueNested(boolean useStream) throws IOException { // first array, no change to default JsonParser jp; // then object with sub-objects... jp = _getParser("{\"a\": { \"b\" : true, \"c\": false }, \"d\": 3 }", useStream); assertToken(JsonToken.START_OBJECT, jp.nextValue()); assertNull(jp.getCurrentName()); assertToken(JsonToken.START_OBJECT, jp.nextValue()); assertEquals("a", jp.getCurrentName()); assertToken(JsonToken.VALUE_TRUE, jp.nextValue()); assertEquals("b", jp.getCurrentName()); assertToken(JsonToken.VALUE_FALSE, jp.nextValue()); assertEquals("c", jp.getCurrentName()); assertToken(JsonToken.END_OBJECT, jp.nextValue()); // ideally we should match closing marker with field, too: assertEquals("a", jp.getCurrentName()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextValue()); assertEquals("d", jp.getCurrentName()); assertToken(JsonToken.END_OBJECT, jp.nextValue()); assertNull(jp.getCurrentName()); assertNull(jp.nextValue()); jp.close(); // and arrays jp = _getParser("{\"a\": [ false ] }", useStream); assertToken(JsonToken.START_OBJECT, jp.nextValue()); assertNull(jp.getCurrentName()); assertToken(JsonToken.START_ARRAY, jp.nextValue()); assertEquals("a", jp.getCurrentName()); assertToken(JsonToken.VALUE_FALSE, jp.nextValue()); assertNull(jp.getCurrentName()); assertToken(JsonToken.END_ARRAY, jp.nextValue()); // ideally we should match closing marker with field, too: assertEquals("a", jp.getCurrentName()); assertToken(JsonToken.END_OBJECT, jp.nextValue()); assertNull(jp.getCurrentName()); assertNull(jp.nextValue()); jp.close(); } 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)); } } TestPrettyPrinter.java000066400000000000000000000235241356164247300336030ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.core.util.MinimalPrettyPrinter; import com.fasterxml.jackson.core.util.Separators; import java.io.*; /** * Set of basic unit tests for verifying that indenting * option of generator works correctly */ @SuppressWarnings("serial") public class TestPrettyPrinter extends com.fasterxml.jackson.core.BaseTest { static class CountPrinter extends MinimalPrettyPrinter { @Override public void writeEndObject(JsonGenerator jg, int nrOfEntries) throws IOException, JsonGenerationException { jg.writeRaw("("+nrOfEntries+")}"); } @Override public void writeEndArray(JsonGenerator jg, int nrOfValues) throws IOException, JsonGenerationException { jg.writeRaw("("+nrOfValues+")]"); } } /* /********************************************************** /* Test methods /********************************************************** */ public void testObjectCount() throws Exception { final String EXP = "{\"x\":{\"a\":1,\"b\":2(2)}(1)}"; final JsonFactory jf = new JsonFactory(); for (int i = 0; i < 2; ++i) { boolean useBytes = (i > 0); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); StringWriter sw = new StringWriter(); JsonGenerator gen = useBytes ? jf.createGenerator(bytes) : jf.createGenerator(sw); gen.setPrettyPrinter(new CountPrinter()); gen.writeStartObject(); gen.writeFieldName("x"); gen.writeStartObject(); gen.writeNumberField("a", 1); gen.writeNumberField("b", 2); gen.writeEndObject(); gen.writeEndObject(); gen.close(); String json = useBytes ? bytes.toString("UTF-8") : sw.toString(); assertEquals(EXP, json); } } public void testArrayCount() throws Exception { final String EXP = "[6,[1,2,9(3)](2)]"; final JsonFactory jf = new JsonFactory(); for (int i = 0; i < 2; ++i) { boolean useBytes = (i > 0); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); StringWriter sw = new StringWriter(); JsonGenerator gen = useBytes ? jf.createGenerator(bytes) : jf.createGenerator(sw); gen.setPrettyPrinter(new CountPrinter()); gen.writeStartArray(); gen.writeNumber(6); gen.writeStartArray(); gen.writeNumber(1); gen.writeNumber(2); gen.writeNumber(9); gen.writeEndArray(); gen.writeEndArray(); gen.close(); String json = useBytes ? bytes.toString("UTF-8") : sw.toString(); assertEquals(EXP, json); } } public void testSimpleDocWithDefault() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); gen.useDefaultPrettyPrinter(); _verifyPrettyPrinter(gen, sw); gen.close(); } @SuppressWarnings("resource") public void testSimpleDocWithMinimal() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); // first with standard minimal gen.setPrettyPrinter(new MinimalPrettyPrinter()); String docStr = _verifyPrettyPrinter(gen, sw); // which should have no linefeeds, tabs assertEquals(-1, docStr.indexOf('\n')); assertEquals(-1, docStr.indexOf('\t')); // And then with slightly customized variant gen = new JsonFactory().createGenerator(sw); gen.setPrettyPrinter(new MinimalPrettyPrinter() { @Override // use TAB between array values public void beforeArrayValues(JsonGenerator jg) throws IOException, JsonGenerationException { jg.writeRaw("\t"); } }); docStr = _verifyPrettyPrinter(gen, sw); assertEquals(-1, docStr.indexOf('\n')); assertTrue(docStr.indexOf('\t') >= 0); gen.close(); } // [Issue#26] public void testCustomRootSeparatorWithPP() throws Exception { JsonFactory jf = new JsonFactory(); // first, no pretty-printing (will still separate root values with a space!) assertEquals("{} {} []", _generateRoot(jf, null)); // First with default pretty printer, default configs: assertEquals("{ } { } [ ]", _generateRoot(jf, new DefaultPrettyPrinter())); // then custom: assertEquals("{ }|{ }|[ ]", _generateRoot(jf, new DefaultPrettyPrinter("|"))); } // Alternative solution for [jackson-core#26] public void testCustomRootSeparatorWithFactory() throws Exception { JsonFactory f = ((JsonFactoryBuilder)JsonFactory.builder()) .rootValueSeparator("##") .build(); StringWriter sw = new StringWriter(); JsonGenerator gen = f.createGenerator(sw); gen.writeNumber(13); gen.writeBoolean(false); gen.writeNull(); gen.close(); assertEquals("13##false##null", sw.toString()); } public void testCustomSeparatorsWithMinimal() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); gen.setPrettyPrinter(new MinimalPrettyPrinter().setSeparators(Separators.createDefaultInstance() .withObjectFieldValueSeparator('=') .withObjectEntrySeparator(';') .withArrayValueSeparator('|'))); _writeTestDocument(gen); assertEquals("[3|\"abc\"|[true]|{\"f\"=null;\"f2\"=null}]", sw.toString()); gen.close(); } public void testCustomSeparatorsWithPP() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); gen.setPrettyPrinter(new DefaultPrettyPrinter().withSeparators(Separators.createDefaultInstance() .withObjectFieldValueSeparator('=') .withObjectEntrySeparator(';') .withArrayValueSeparator('|'))); _writeTestDocument(gen); gen.close(); assertEquals("[ 3| \"abc\"| [ true ]| {" + DefaultIndenter.SYS_LF + " \"f\" = null;" + DefaultIndenter.SYS_LF + " \"f2\" = null" + DefaultIndenter.SYS_LF + "} ]", sw.toString()); } public void testCustomSeparatorsWithPPWithoutSpaces() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); gen.setPrettyPrinter(new DefaultPrettyPrinter().withSeparators(Separators.createDefaultInstance() .withObjectFieldValueSeparator('=') .withObjectEntrySeparator(';') .withArrayValueSeparator('|')) .withoutSpacesInObjectEntries()); _writeTestDocument(gen); gen.close(); assertEquals("[ 3| \"abc\"| [ true ]| {" + DefaultIndenter.SYS_LF + " \"f\"=null;" + DefaultIndenter.SYS_LF + " \"f2\"=null" + DefaultIndenter.SYS_LF + "} ]", sw.toString()); } /* /********************************************************** /* Helper methods /********************************************************** */ private String _verifyPrettyPrinter(JsonGenerator gen, StringWriter sw) throws Exception { _writeTestDocument(gen); String docStr = sw.toString(); JsonParser jp = createParserUsingReader(docStr); assertEquals(JsonToken.START_ARRAY, jp.nextToken()); assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(3, jp.getIntValue()); assertEquals(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("abc", jp.getText()); assertEquals(JsonToken.START_ARRAY, jp.nextToken()); assertEquals(JsonToken.VALUE_TRUE, jp.nextToken()); assertEquals(JsonToken.END_ARRAY, jp.nextToken()); assertEquals(JsonToken.START_OBJECT, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("f", jp.getText()); assertEquals(JsonToken.VALUE_NULL, jp.nextToken()); assertEquals(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("f2", jp.getText()); assertEquals(JsonToken.VALUE_NULL, jp.nextToken()); assertEquals(JsonToken.END_OBJECT, jp.nextToken()); assertEquals(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); return docStr; } private void _writeTestDocument(JsonGenerator gen) throws IOException { gen.writeStartArray(); gen.writeNumber(3); gen.writeString("abc"); gen.writeStartArray(); gen.writeBoolean(true); gen.writeEndArray(); gen.writeStartObject(); gen.writeFieldName("f"); gen.writeNull(); gen.writeFieldName("f2"); gen.writeNull(); gen.writeEndObject(); gen.writeEndArray(); gen.close(); } protected String _generateRoot(JsonFactory jf, PrettyPrinter pp) throws IOException { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); gen.setPrettyPrinter(pp); gen.writeStartObject(); gen.writeEndObject(); gen.writeStartObject(); gen.writeEndObject(); gen.writeStartArray(); gen.writeEndArray(); gen.close(); return sw.toString(); } } TestRawStringWriting.java000066400000000000000000000137231356164247300342340ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import java.io.*; import java.util.*; import static org.junit.Assert.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.SerializedString; public class TestRawStringWriting extends com.fasterxml.jackson.core.BaseTest { /** * Unit test for "JsonGenerator.writeRawUTF8String()" */ public void testUtf8RawStrings() throws Exception { // Let's create set of Strings to output; no ctrl chars as we do raw List strings = generateStrings(new Random(28), 750000, false); ByteArrayOutputStream out = new ByteArrayOutputStream(16000); JsonFactory jf = new JsonFactory(); JsonGenerator jgen = jf.createGenerator(out, JsonEncoding.UTF8); jgen.writeStartArray(); for (byte[] str : strings) { jgen.writeRawUTF8String(str, 0, str.length); } jgen.writeEndArray(); jgen.close(); byte[] json = out.toByteArray(); // Ok: let's verify that stuff was written out ok JsonParser jp = jf.createParser(json); assertToken(JsonToken.START_ARRAY, jp.nextToken()); for (byte[] inputBytes : strings) { assertToken(JsonToken.VALUE_STRING, jp.nextToken()); String string = jp.getText(); byte[] outputBytes = string.getBytes("UTF-8"); assertEquals(inputBytes.length, outputBytes.length); assertArrayEquals(inputBytes, outputBytes); } assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); } /** * Unit test for "JsonGenerator.writeUTF8String()", which needs * to handle escaping properly */ public void testUtf8StringsWithEscaping() throws Exception { // Let's create set of Strings to output; do include control chars too: List strings = generateStrings(new Random(28), 720000, true); ByteArrayOutputStream out = new ByteArrayOutputStream(16000); JsonFactory jf = new JsonFactory(); JsonGenerator jgen = jf.createGenerator(out, JsonEncoding.UTF8); jgen.writeStartArray(); for (byte[] str : strings) { jgen.writeUTF8String(str, 0, str.length); jgen.writeRaw('\n'); } jgen.writeEndArray(); jgen.close(); byte[] json = out.toByteArray(); // Ok: let's verify that stuff was written out ok JsonParser jp = jf.createParser(json); assertToken(JsonToken.START_ARRAY, jp.nextToken()); for (byte[] inputBytes : strings) { assertToken(JsonToken.VALUE_STRING, jp.nextToken()); String string = jp.getText(); byte[] outputBytes = string.getBytes("UTF-8"); assertEquals(inputBytes.length, outputBytes.length); assertArrayEquals(inputBytes, outputBytes); } assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); } public void testWriteRawWithSerializable() throws Exception { JsonFactory jf = new JsonFactory(); _testWithRaw(jf, true); _testWithRaw(jf, false); } private void _testWithRaw(JsonFactory f, boolean useBytes) throws Exception { JsonGenerator jgen; ByteArrayOutputStream bytes = new ByteArrayOutputStream(); StringWriter sw = new StringWriter(); if (useBytes) { jgen = f.createGenerator(bytes, JsonEncoding.UTF8); } else { jgen = f.createGenerator(sw); } jgen.writeStartArray(); jgen.writeRawValue(new SerializedString("\"foo\"")); jgen.writeRawValue(new SerializedString("12")); jgen.writeRaw(new SerializedString(", false")); jgen.writeEndArray(); jgen.close(); JsonParser p = useBytes ? f.createParser(bytes.toByteArray()) : f.createParser(sw.toString()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("foo", p.getText()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(12, p.getIntValue()); assertToken(JsonToken.VALUE_FALSE, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); } /* /********************************************************** /* Helper methods /********************************************************** */ private List generateStrings(Random rnd, int totalLength, boolean includeCtrlChars) throws IOException { ArrayList strings = new ArrayList(); do { int len = 2; int bits = rnd.nextInt(13); while (--bits >= 0) { len += len; } len = 1 + ((len + len) / 3); String str = generateString(rnd, len, includeCtrlChars); byte[] bytes = str.getBytes("UTF-8"); strings.add(bytes); totalLength -= bytes.length; } while (totalLength > 0); return strings; } private String generateString(Random rnd, int length, boolean includeCtrlChars) { StringBuilder sb = new StringBuilder(length); do { int i; switch (rnd.nextInt(3)) { case 0: // 3 byte one i = 2048 + rnd.nextInt(16383); break; case 1: // 2 byte i = 128 + rnd.nextInt(1024); break; default: // ASCII i = rnd.nextInt(192); if (!includeCtrlChars) { i += 32; // but also need to avoid backslash, double-quote if (i == '\\' || i == '"') { i = '@'; // just arbitrary choice } } } sb.append((char) i); } while (sb.length() < length); return sb.toString(); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/read/000077500000000000000000000000001356164247300273055ustar00rootroot00000000000000CommentParsingTest.java000066400000000000000000000254571356164247300336740ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage com.fasterxml.jackson.core.read; import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.json.JsonReadFeature; /** * 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 { final JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_YAML_COMMENTS) .build(); _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 { final JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_YAML_COMMENTS) .build(); _testYAMLComments(f, MODE_READER); final String COMMENT = "# foo\n"; _testCommentsBeforePropValue(f, MODE_READER, COMMENT); _testCommentsBetweenArrayValues(f, MODE_READER, COMMENT); } public void testCCommentsBytes() throws Exception { final JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) .build(); 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 { final JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) .build(); final String COMMENT = "/* foo */\n"; _testCommentsBeforePropValue(f, MODE_READER, COMMENT); } public void testCppCommentsBytes() throws Exception { final JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) .build(); 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 { final JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_JAVA_COMMENTS) .build(); 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 { final JsonFactory f = JsonFactory.builder() .configure(JsonReadFeature.ALLOW_JAVA_COMMENTS, enabled) .build(); JsonParser p = createParser(f, mode, doc); assertToken(JsonToken.START_ARRAY, p.nextToken()); return p; } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/read/DataInputTest.java000066400000000000000000000026221356164247300327030ustar00rootroot00000000000000package com.fasterxml.jackson.core.read; import com.fasterxml.jackson.core.*; /* Additional testing for {@link java.io.DataInput} specific * challenges for parsing. */ public class DataInputTest extends com.fasterxml.jackson.core.BaseTest { private final JsonFactory JSON_F = new JsonFactory(); public void testEOFAfterArray() throws Exception { JsonParser p = createParser(JSON_F, MODE_DATA_INPUT, "[ 1 ] "); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertNull(p.nextToken()); p.close(); } public void testEOFAfterObject() throws Exception { JsonParser p = createParser(JSON_F, MODE_DATA_INPUT, "{ \"value\" : true }"); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertNull(p.nextToken()); p.close(); } public void testEOFAfterScalar() throws Exception { JsonParser p = createParser(JSON_F, MODE_DATA_INPUT, "\"foobar\" "); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("foobar", p.getText()); assertNull(p.nextToken()); p.close(); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/read/JsonParserTest.java000066400000000000000000000616041356164247300331050ustar00rootroot00000000000000package 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 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 = JsonFactory.builder() .configure(JsonFactory.Feature.INTERN_FIELD_NAMES, enableIntern) .build(); 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 NAME_MAP = new LinkedHashMap(); 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 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()); JsonLocation loc = p.getTokenLocation(); assertEquals(3, 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 restrictive 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(); } } LocationDuringReaderParsingTest.java000066400000000000000000000156121356164247300363260ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage com.fasterxml.jackson.core.read; import java.util.Arrays; import java.util.List; import com.fasterxml.jackson.core.BaseTest; import com.fasterxml.jackson.core.JsonLocation; import com.fasterxml.jackson.core.JsonParser; /** * Set of tests that checks getCurrentLocation() and getTokenLocation() are as expected during * parsing. */ public class LocationDuringReaderParsingTest extends BaseTest { public void testLocationAtEndOfParse() throws Exception { for (LocationTestCase test : LocationTestCase.values()) { //System.out.println(test.name()); testLocationAtEndOfParse(test); } } public void testInitialLocation() throws Exception { for (LocationTestCase test : LocationTestCase.values()) { //System.out.println(test.name()); testInitialLocation(test); } } public void testTokenLocations() throws Exception { for (LocationTestCase test : LocationTestCase.values()) { //System.out.println(test.name()); testTokenLocations(test); } } private void testLocationAtEndOfParse(LocationTestCase test) throws Exception { JsonParser p = createParserUsingReader(test.json); while (p.nextToken() != null) { p.nextToken(); } assertCurrentLocation(p, test.getFinalLocation()); p.close(); } private void testInitialLocation(LocationTestCase test) throws Exception { JsonParser p = createParserUsingReader(test.json); JsonLocation loc = p.getCurrentLocation(); p.close(); assertLocation(loc, at(1, 1, 0)); } private void testTokenLocations(LocationTestCase test) throws Exception { JsonParser p = createParserUsingReader(test.json); int i = 0; while (p.nextToken() != null) { assertTokenLocation(p, test.locations.get(i)); i++; } assertEquals(test.locations.size(), i + 1); // last LocData is end of stream p.close(); } private void assertCurrentLocation(JsonParser p, LocData loc) { assertLocation(p.getCurrentLocation(), loc); } private void assertTokenLocation(JsonParser p, LocData loc) { assertLocation(p.getTokenLocation(), loc); } private void assertLocation(JsonLocation pLoc, LocData loc) { String expected = String.format("(%d, %d, %d)", loc.lineNumber, loc.columnNumber, loc.offset); String actual = String.format("(%d, %d, %d)", pLoc.getLineNr(), pLoc.getColumnNr(), pLoc.getByteOffset() == -1 ? pLoc.getCharOffset() : pLoc.getByteOffset()); assertEquals(expected, actual); } private static class LocData { private long lineNumber; private long columnNumber; private long offset; LocData(long lineNumber, long columnNumber, long offset) { this.lineNumber = lineNumber; this.columnNumber = columnNumber; this.offset = offset; } } private static LocData at(long lineNumber, long columnNumber, long offset) { return new LocData(lineNumber, columnNumber, offset); } /** * Adapted liberally from https://github.com/leadpony/jsonp-test-suite, also * released under the Apache License v2. */ enum LocationTestCase { SIMPLE_VALUE("42", at(1, 1, 0), at(1, 3, 2)), SIMPLE_VALUE_WITH_PADDING(" 1337 ", at(1, 4, 3), at(1, 10, 9)), SIMPLE_VALUE_WITH_MULTIBYTE_CHARS("\"Правда\"", at(1, 1, 0), at(1, 9, 8) ), SIMPLE_VALUE_INCLUDING_SURROGATE_PAIR_CHARS("\"a П \uD83D\uDE01\"", at(1, 1, 0), at(1, 9, 8) // reader counts surrogate pairs as two chars ), ARRAY_IN_ONE_LINE("[\"hello\",42,true]", at(1, 1, 0), // [ at(1, 2, 1), // "hello" at(1, 10, 9), // 42 at(1, 13, 12), // true at(1, 17, 16), // ] at(1, 18, 17) // end of input ), ARRAY_IN_ONE_LINE_WITH_PADDING(" [ \"hello\" , 42 , true ] ", at(1, 3, 2), // [ at(1, 5, 4), // "hello" at(1, 17, 16), // 42 at(1, 26, 25), // true at(1, 33, 32), // ] at(1, 37, 36) // end of input ), ARRAY_IN_MULTIPLE_LINES("[\n" + " \"hello\",\n" + " 42,\n" + " true\n" + "]", at(1, 1, 0), // [ at(2, 5, 6), // "hello" at(3, 5, 19), // 42 at(4, 5, 27), // true at(5, 1, 32), // ] at(5, 2, 33) // end of input ), ARRAY_IN_MULTIPLE_LINES_WITH_WEIRD_SPACING(" [\n" + " \"hello\" , \n" + " 42 ,\n" + " true\n" + " ]", at(1, 2, 1), // [ at(2, 3, 5), // "hello" at(3, 2, 18), // 42 at(4, 7, 31), // true at(5, 2, 37), // ] at(5, 3, 38) // end of input ), ARRAY_IN_MULTIPLE_LINES_CRLF("[\r\n" + " \"hello\",\r\n" + " 42,\r\n" + " true\r\n" + "]", at(1, 1, 0), // [ at(2, 5, 7), // "hello" at(3, 5, 21), // 42 at(4, 5, 30), // true at(5, 1, 36), // ] at(5, 2, 37) // end of input ), OBJECT_IN_ONE_LINE("{\"first\":\"hello\",\"second\":42}", at(1, 1, 0), // { at(1, 2, 1), // "first" at(1, 10, 9), // "hello" at(1, 18, 17), // "second" at(1, 27, 26), // 42 at(1, 29, 28), // } at(1, 30, 29) // end of input ), OBJECT_IN_MULTIPLE_LINES("{\n" + " \"first\":\"hello\",\n" + " \"second\":42\n" + "}", at(1, 1, 0), // { at(2, 5, 6), // "first" at(2, 13, 14), // "hello" at(3, 5, 27), // "second" at(3, 14, 36), // 42 at(4, 1, 39), // } at(4, 2, 40) // end of input ), OBJECT_IN_MULTIPLE_LINES_CRLF("{\r\n" + " \"first\":\"hello\",\r\n" + " \"second\":42\r\n" + "}", at(1, 1, 0), // { at(2, 5, 7), // "first" at(2, 13, 15), // "hello" at(3, 5, 29), // "second" at(3, 14, 38), // 42 at(4, 1, 42), // } at(4, 2, 43) // end of input ), ; final String json; final List locations; LocationTestCase(String json, LocData... locations) { this.json = json; this.locations = Arrays.asList(locations); } LocData getFinalLocation() { return locations.get(locations.size() - 1); } } } LocationDuringStreamParsingTest.java000066400000000000000000000157631356164247300363660ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage com.fasterxml.jackson.core.read; import java.util.Arrays; import java.util.List; import com.fasterxml.jackson.core.BaseTest; import com.fasterxml.jackson.core.JsonLocation; import com.fasterxml.jackson.core.JsonParser; /** * Set of tests that checks getCurrentLocation() and getTokenLocation() are as expected during * parsing. */ public class LocationDuringStreamParsingTest extends BaseTest { public void testLocationAtEndOfParse() throws Exception { for (LocationTestCase test : LocationTestCase.values()) { //System.out.println(test.name()); testLocationAtEndOfParse(test); } } public void testInitialLocation() throws Exception { for (LocationTestCase test : LocationTestCase.values()) { //System.out.println(test.name()); testInitialLocation(test); } } public void testTokenLocations() throws Exception { for (LocationTestCase test : LocationTestCase.values()) { //System.out.println(test.name()); testTokenLocations(test); } } private void testLocationAtEndOfParse(LocationTestCase test) throws Exception { JsonParser p = createParserUsingStream(test.json, "UTF8"); while (p.nextToken() != null) { p.nextToken(); } assertCurrentLocation(p, test.getFinalLocation()); p.close(); } private void testInitialLocation(LocationTestCase test) throws Exception { JsonParser p = createParserUsingStream(test.json, "UTF8"); JsonLocation loc = p.getCurrentLocation(); p.close(); assertLocation(loc, at(1, 1, 0)); } private void testTokenLocations(LocationTestCase test) throws Exception { JsonParser p = createParserUsingStream(test.json, "UTF8"); int i = 0; while (p.nextToken() != null) { assertTokenLocation(p, test.locations.get(i)); i++; } assertEquals(test.locations.size(), i + 1); // last LocData is end of stream p.close(); } private void assertCurrentLocation(JsonParser p, LocData loc) { assertLocation(p.getCurrentLocation(), loc); } private void assertTokenLocation(JsonParser p, LocData loc) { assertLocation(p.getTokenLocation(), loc); } private void assertLocation(JsonLocation pLoc, LocData loc) { String expected = String.format("(%d, %d, %d)", loc.lineNumber, loc.columnNumber, loc.offset); String actual = String.format("(%d, %d, %d)", pLoc.getLineNr(), pLoc.getColumnNr(), pLoc.getByteOffset() == -1 ? pLoc.getCharOffset() : pLoc.getByteOffset()); assertEquals(expected, actual); } private static class LocData { private long lineNumber; private long columnNumber; private long offset; LocData(long lineNumber, long columnNumber, long offset) { this.lineNumber = lineNumber; this.columnNumber = columnNumber; this.offset = offset; } } private static LocData at(long lineNumber, long columnNumber, long offset) { return new LocData(lineNumber, columnNumber, offset); } /** * Adapted liberally from https://github.com/leadpony/jsonp-test-suite, also * released under the Apache License v2. */ enum LocationTestCase { SIMPLE_VALUE("42", at(1, 1, 0), at(1, 3, 2)), SIMPLE_VALUE_WITH_PADDING(" 1337 ", at(1, 4, 3), at(1, 10, 9)), SIMPLE_VALUE_WITH_MULTIBYTE_CHARS("\"Правда\"", at(1, 1, 0), at(1, 15, 14) // one byte for each ", two for each Cyrillic char ), SIMPLE_VALUE_INCLUDING_SURROGATE_PAIR_CHARS("\"a П \uD83D\uDE01\"", at(1, 1, 0), at(1, 12, 11) // one byte for each ", a and space; two for П, four for smiley emoji ), ARRAY_IN_ONE_LINE("[\"hello\",42,true]", at(1, 1, 0), // [ at(1, 2, 1), // "hello" at(1, 10, 9), // 42 at(1, 13, 12), // true at(1, 17, 16), // ] at(1, 18, 17) // end of input ), ARRAY_IN_ONE_LINE_WITH_PADDING(" [ \"hello\" , 42 , true ] ", at(1, 3, 2), // [ at(1, 5, 4), // "hello" at(1, 17, 16), // 42 at(1, 26, 25), // true at(1, 33, 32), // ] at(1, 37, 36) // end of input ), ARRAY_IN_MULTIPLE_LINES("[\n" + " \"hello\",\n" + " 42,\n" + " true\n" + "]", at(1, 1, 0), // [ at(2, 5, 6), // "hello" at(3, 5, 19), // 42 at(4, 5, 27), // true at(5, 1, 32), // ] at(5, 2, 33) // end of input ), ARRAY_IN_MULTIPLE_LINES_WITH_WEIRD_SPACING(" [\n" + " \"hello\" , \n" + " 42 ,\n" + " true\n" + " ]", at(1, 2, 1), // [ at(2, 3, 5), // "hello" at(3, 2, 18), // 42 at(4, 7, 31), // true at(5, 2, 37), // ] at(5, 3, 38) // end of input ), ARRAY_IN_MULTIPLE_LINES_CRLF("[\r\n" + " \"hello\",\r\n" + " 42,\r\n" + " true\r\n" + "]", at(1, 1, 0), // [ at(2, 5, 7), // "hello" at(3, 5, 21), // 42 at(4, 5, 30), // true at(5, 1, 36), // ] at(5, 2, 37) // end of input ), OBJECT_IN_ONE_LINE("{\"first\":\"hello\",\"second\":42}", at(1, 1, 0), // { at(1, 2, 1), // "first" at(1, 10, 9), // "hello" at(1, 18, 17), // "second" at(1, 27, 26), // 42 at(1, 29, 28), // } at(1, 30, 29) // end of input ), OBJECT_IN_MULTIPLE_LINES("{\n" + " \"first\":\"hello\",\n" + " \"second\":42\n" + "}", at(1, 1, 0), // { at(2, 5, 6), // "first" at(2, 13, 14), // "hello" at(3, 5, 27), // "second" at(3, 14, 36), // 42 at(4, 1, 39), // } at(4, 2, 40) // end of input ), OBJECT_IN_MULTIPLE_LINES_CRLF("{\r\n" + " \"first\":\"hello\",\r\n" + " \"second\":42\r\n" + "}", at(1, 1, 0), // { at(2, 5, 7), // "first" at(2, 13, 15), // "hello" at(3, 5, 29), // "second" at(3, 14, 38), // 42 at(4, 1, 42), // } at(4, 2, 43) // end of input ), ; final String json; final List locations; LocationTestCase(String json, LocData... locations) { this.json = json; this.locations = Arrays.asList(locations); } LocData getFinalLocation() { return locations.get(locations.size() - 1); } } } LocationInArrayTest.java000066400000000000000000000046071356164247300337760ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage com.fasterxml.jackson.core.read; import com.fasterxml.jackson.core.*; // Tests mostly for [core#229] public class LocationInArrayTest extends com.fasterxml.jackson.core.BaseTest { final JsonFactory JSON_F = new JsonFactory(); // for [core#229] public void testOffsetInArraysBytes() throws Exception { _testOffsetInArrays(true); } // for [core#229] public void testOffsetInArraysChars() throws Exception { _testOffsetInArrays(false); } private void _testOffsetInArrays(boolean useBytes) throws Exception { JsonParser p; final String DOC = " [10, 251,\n 3 ]"; // first, char based: p = useBytes ? JSON_F.createParser(DOC.getBytes("UTF-8")) : JSON_F.createParser(DOC.toCharArray()); assertToken(JsonToken.START_ARRAY, p.nextToken()); _assertLocation(useBytes, p.getTokenLocation(), 2L, 1, 3); _assertLocation(useBytes, p.getCurrentLocation(), 3L, 1, 4); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); _assertLocation(useBytes, p.getTokenLocation(), 3L, 1, 4); assertEquals(10, p.getIntValue()); // just to ensure read proceeds to end // 2-digits so _assertLocation(useBytes, p.getCurrentLocation(), 5L, 1, 6); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); _assertLocation(useBytes, p.getTokenLocation(), 7L, 1, 8); assertEquals(251, p.getIntValue()); // just to ensure read proceeds to end _assertLocation(useBytes, p.getCurrentLocation(), 10L, 1, 11); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); _assertLocation(useBytes, p.getTokenLocation(), 15L, 2, 4); assertEquals(3, p.getIntValue()); _assertLocation(useBytes, p.getCurrentLocation(), 16L, 2, 5); assertToken(JsonToken.END_ARRAY, p.nextToken()); _assertLocation(useBytes, p.getTokenLocation(), 18L, 2, 7); _assertLocation(useBytes, p.getCurrentLocation(), 19L, 2, 8); p.close(); } private void _assertLocation(boolean useBytes, JsonLocation loc, long offset, int row, int col) { assertEquals(row, loc.getLineNr()); assertEquals(col, loc.getColumnNr()); if (useBytes) { assertEquals(offset, loc.getByteOffset()); } else { assertEquals(offset, loc.getCharOffset()); } } } LocationInObjectTest.java000066400000000000000000000103531356164247300341210ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage com.fasterxml.jackson.core.read; import com.fasterxml.jackson.core.*; // tests for [core#37] public class LocationInObjectTest extends BaseTest { public void testOffsetWithObjectFieldsUsingUTF8() throws Exception { final JsonFactory f = new JsonFactory(); byte[] b = "{\"f1\":\"v1\",\"f2\":{\"f3\":\"v3\"},\"f4\":[true,false],\"f5\":5}".getBytes("UTF-8"); // 1 6 11 16 17 22 28 33 34 39 46 51 JsonParser p = f.createParser(b); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertEquals(JsonToken.FIELD_NAME, p.nextToken()); assertEquals(1L, p.getTokenLocation().getByteOffset()); assertEquals(JsonToken.VALUE_STRING, p.nextToken()); assertEquals(6L, p.getTokenLocation().getByteOffset()); assertEquals("f2", p.nextFieldName()); assertEquals(11L, p.getTokenLocation().getByteOffset()); assertEquals(JsonToken.START_OBJECT, p.nextValue()); assertEquals(16L, p.getTokenLocation().getByteOffset()); assertEquals("f3", p.nextFieldName()); assertEquals(17L, p.getTokenLocation().getByteOffset()); assertEquals(JsonToken.VALUE_STRING, p.nextValue()); assertEquals(22L, p.getTokenLocation().getByteOffset()); assertEquals(JsonToken.END_OBJECT, p.nextToken()); assertEquals("f4", p.nextFieldName()); assertEquals(28L, p.getTokenLocation().getByteOffset()); assertEquals(JsonToken.START_ARRAY, p.nextValue()); assertEquals(33L, p.getTokenLocation().getByteOffset()); assertEquals(JsonToken.VALUE_TRUE, p.nextValue()); assertEquals(34L, p.getTokenLocation().getByteOffset()); assertEquals(JsonToken.VALUE_FALSE, p.nextValue()); assertEquals(39L, p.getTokenLocation().getByteOffset()); assertEquals(JsonToken.END_ARRAY, p.nextToken()); assertEquals("f5", p.nextFieldName()); assertEquals(46L, p.getTokenLocation().getByteOffset()); assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(51L, p.getTokenLocation().getByteOffset()); assertEquals(JsonToken.END_OBJECT, p.nextToken()); p.close(); } public void testOffsetWithObjectFieldsUsingReader() throws Exception { final JsonFactory f = new JsonFactory(); char[] c = "{\"f1\":\"v1\",\"f2\":{\"f3\":\"v3\"},\"f4\":[true,false],\"f5\":5}".toCharArray(); // 1 6 11 16 17 22 28 33 34 39 46 51 JsonParser p = f.createParser(c); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertEquals(JsonToken.FIELD_NAME, p.nextToken()); assertEquals(1L, p.getTokenLocation().getCharOffset()); assertEquals(JsonToken.VALUE_STRING, p.nextToken()); assertEquals(6L, p.getTokenLocation().getCharOffset()); assertEquals("f2", p.nextFieldName()); assertEquals(11L, p.getTokenLocation().getCharOffset()); assertEquals(JsonToken.START_OBJECT, p.nextValue()); assertEquals(16L, p.getTokenLocation().getCharOffset()); assertEquals("f3", p.nextFieldName()); assertEquals(17L, p.getTokenLocation().getCharOffset()); assertEquals(JsonToken.VALUE_STRING, p.nextValue()); assertEquals(22L, p.getTokenLocation().getCharOffset()); assertEquals(JsonToken.END_OBJECT, p.nextToken()); assertEquals("f4", p.nextFieldName()); assertEquals(28L, p.getTokenLocation().getCharOffset()); assertEquals(JsonToken.START_ARRAY, p.nextValue()); assertEquals(33L, p.getTokenLocation().getCharOffset()); assertEquals(JsonToken.VALUE_TRUE, p.nextValue()); assertEquals(34L, p.getTokenLocation().getCharOffset()); assertEquals(JsonToken.VALUE_FALSE, p.nextValue()); assertEquals(39L, p.getTokenLocation().getCharOffset()); assertEquals(JsonToken.END_ARRAY, p.nextToken()); assertEquals("f5", p.nextFieldName()); assertEquals(46L, p.getTokenLocation().getCharOffset()); assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(51L, p.getTokenLocation().getCharOffset()); assertEquals(JsonToken.END_OBJECT, p.nextToken()); p.close(); } } LocationOffsetsTest.java000066400000000000000000000133751356164247300340440ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage com.fasterxml.jackson.core.read; import com.fasterxml.jackson.core.*; public class LocationOffsetsTest extends com.fasterxml.jackson.core.BaseTest { final JsonFactory JSON_F = new JsonFactory(); // Trivially simple unit test for basics wrt offsets public void testSimpleInitialOffsets() throws Exception { JsonLocation loc; JsonParser p; final String DOC = "{ }"; // first, char based: p = JSON_F.createParser(DOC); assertToken(JsonToken.START_OBJECT, p.nextToken()); loc = p.getTokenLocation(); assertEquals(-1L, loc.getByteOffset()); assertEquals(0L, loc.getCharOffset()); assertEquals(1, loc.getLineNr()); assertEquals(1, loc.getColumnNr()); loc = p.getCurrentLocation(); assertEquals(-1L, loc.getByteOffset()); assertEquals(1L, loc.getCharOffset()); assertEquals(1, loc.getLineNr()); assertEquals(2, loc.getColumnNr()); p.close(); // then byte-based p = JSON_F.createParser(DOC.getBytes("UTF-8")); assertToken(JsonToken.START_OBJECT, p.nextToken()); loc = p.getTokenLocation(); assertEquals(0L, loc.getByteOffset()); assertEquals(-1L, loc.getCharOffset()); assertEquals(1, loc.getLineNr()); assertEquals(1, loc.getColumnNr()); loc = p.getCurrentLocation(); assertEquals(1L, loc.getByteOffset()); assertEquals(-1L, loc.getCharOffset()); assertEquals(1, loc.getLineNr()); assertEquals(2, loc.getColumnNr()); p.close(); } // for [core#111] public void testOffsetWithInputOffset() throws Exception { JsonLocation loc; JsonParser p; // 3 spaces before, 2 after, just for padding byte[] b = " { } ".getBytes("UTF-8"); // and then peel them off p = JSON_F.createParser(b, 3, b.length-5); assertToken(JsonToken.START_OBJECT, p.nextToken()); loc = p.getTokenLocation(); assertEquals(0L, loc.getByteOffset()); assertEquals(-1L, loc.getCharOffset()); assertEquals(1, loc.getLineNr()); assertEquals(1, loc.getColumnNr()); loc = p.getCurrentLocation(); assertEquals(1L, loc.getByteOffset()); assertEquals(-1L, loc.getCharOffset()); assertEquals(1, loc.getLineNr()); assertEquals(2, loc.getColumnNr()); p.close(); } public void testOffsetWithoutInputOffset() throws Exception { JsonLocation loc; JsonParser p; // 3 spaces before, 2 after, just for padding byte[] b = " { } ".getBytes("UTF-8"); // and then peel them off p = JSON_F.createParser(b); assertToken(JsonToken.START_OBJECT, p.nextToken()); loc = p.getTokenLocation(); assertEquals(3L, loc.getByteOffset()); assertEquals(-1L, loc.getCharOffset()); assertEquals(1, loc.getLineNr()); assertEquals(4, loc.getColumnNr()); loc = p.getCurrentLocation(); assertEquals(4L, loc.getByteOffset()); assertEquals(-1L, loc.getCharOffset()); assertEquals(1, loc.getLineNr()); assertEquals(5, loc.getColumnNr()); p.close(); } // for [core#533] public void testUtf8Bom() throws Exception { JsonLocation loc; JsonParser p; byte[] b = withUtf8Bom("{ }".getBytes()); // and then peel them off p = JSON_F.createParser(b); assertToken(JsonToken.START_OBJECT, p.nextToken()); loc = p.getTokenLocation(); assertEquals(3L, loc.getByteOffset()); assertEquals(-1L, loc.getCharOffset()); assertEquals(1, loc.getLineNr()); assertEquals(4, loc.getColumnNr()); loc = p.getCurrentLocation(); assertEquals(4L, loc.getByteOffset()); assertEquals(-1L, loc.getCharOffset()); assertEquals(1, loc.getLineNr()); assertEquals(5, loc.getColumnNr()); p.close(); } public void testUtf8BomWithPadding() throws Exception { JsonLocation loc; JsonParser p; byte[] b = withUtf8Bom(" { }".getBytes()); // and then peel them off p = JSON_F.createParser(b); assertToken(JsonToken.START_OBJECT, p.nextToken()); loc = p.getTokenLocation(); assertEquals(6L, loc.getByteOffset()); assertEquals(-1L, loc.getCharOffset()); assertEquals(1, loc.getLineNr()); assertEquals(7, loc.getColumnNr()); loc = p.getCurrentLocation(); assertEquals(7L, loc.getByteOffset()); assertEquals(-1L, loc.getCharOffset()); assertEquals(1, loc.getLineNr()); assertEquals(8, loc.getColumnNr()); p.close(); } public void testUtf8BomWithInputOffset() throws Exception { JsonLocation loc; JsonParser p; byte[] b = withUtf8Bom(" { }".getBytes()); // and then peel them off p = JSON_F.createParser(b); assertToken(JsonToken.START_OBJECT, p.nextToken()); loc = p.getTokenLocation(); assertEquals(6L, loc.getByteOffset()); assertEquals(-1L, loc.getCharOffset()); assertEquals(1, loc.getLineNr()); assertEquals(7, loc.getColumnNr()); loc = p.getCurrentLocation(); assertEquals(7L, loc.getByteOffset()); assertEquals(-1L, loc.getCharOffset()); assertEquals(1, loc.getLineNr()); assertEquals(8, loc.getColumnNr()); p.close(); } private byte[] withUtf8Bom(byte[] bytes) { byte[] arr = new byte[bytes.length + 3]; // write UTF-8 BOM arr[0] = (byte) 0xEF; arr[1] = (byte) 0xBB; arr[2] = (byte) 0xBF; System.arraycopy(bytes, 0, arr, 3, bytes.length); return arr; } } NextNameParserTest.java000066400000000000000000000054541356164247300336350ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage com.fasterxml.jackson.core.read; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; public class NextNameParserTest extends com.fasterxml.jackson.core.BaseTest { public void testBasicNextNameWithReader() throws Exception { _testBasicNextName(MODE_READER); } public void testBasicNextNameWithStream() throws Exception { _testBasicNextName(MODE_INPUT_STREAM); _testBasicNextName(MODE_INPUT_STREAM_THROTTLED); } private void _testBasicNextName(int mode) throws Exception { final String DOC = aposToQuotes( "{ 'data' : { 'primary' : 15, 'vector' : [ 'yes', false ] },\n" +" 'array' : [ true, {'message':'hello', 'value' : 42, 'misc' : [1, 2] }, null, 0.25 ]\n" +"}"); JsonParser p = createParser(mode, DOC); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("data", p.currentName()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertEquals("primary", p.nextFieldName()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(15, p.getIntValue()); assertEquals("vector", p.nextFieldName()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("yes", p.getText()); assertToken(JsonToken.VALUE_FALSE, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertEquals("array", p.nextFieldName()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertEquals("message", p.nextFieldName()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("hello", p.getText()); assertEquals("value", p.nextFieldName()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(42, p.getIntValue()); assertEquals("misc", p.nextFieldName()); 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.END_ARRAY, p.nextToken()); assertToken(JsonToken.END_OBJECT, p.nextToken()); assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertToken(JsonToken.END_OBJECT, p.nextToken()); p.close(); } } NextXxxAccessTest.java000066400000000000000000000464421356164247300335130ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage 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(); } } NonStandardParserFeaturesTest.java000066400000000000000000000413201356164247300360200ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage com.fasterxml.jackson.core.read; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.json.JsonReadFeature; public class NonStandardParserFeaturesTest extends com.fasterxml.jackson.core.BaseTest { @SuppressWarnings("deprecation") public void testDefaults() { JsonFactory f = new JsonFactory(); assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS)); assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS)); } 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 /**************************************************************** */ /** * 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 = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES) .build(); 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 = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES) .build(); 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 { final JsonFactory f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES) .build(); 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(); 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 = f.rebuild() .configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true) .build(); 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(); 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 = JsonFactory.builder() .configure(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS, true) .build(); 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(); // 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 = JsonFactory.builder() .configure(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS, true) .build(); 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 = JsonFactory.builder() .configure(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS, true) .build(); 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(); // 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 = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS) .build(); 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 = JsonFactory.builder() .configure(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS, true) .build(); 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(); } } NonStandardUnquotedNamesTest.java000066400000000000000000000116361356164247300356640ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage com.fasterxml.jackson.core.read; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; public class NonStandardUnquotedNamesTest extends com.fasterxml.jackson.core.BaseTest { private final JsonFactory UNQUOTED_FIELDS_F = new JsonFactory(); { UNQUOTED_FIELDS_F.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); } 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); } // [core#510]: ArrayIndexOutOfBounds public void testUnquotedIssue510() throws Exception { // NOTE! Requires longer input buffer to trigger longer codepath char[] fullChars = new char[4001]; for (int i = 0; i < 3998; i++) { fullChars[i] = ' '; } fullChars[3998] = '{'; fullChars[3999] = 'a'; fullChars[4000] = 256; JsonParser p = UNQUOTED_FIELDS_F.createParser(new java.io.StringReader(new String(fullChars))); assertToken(JsonToken.START_OBJECT, p.nextToken()); try { p.nextToken(); fail("Should not pass"); } catch (JsonParseException e) { ; // should fail here } p.close(); } /* /**************************************************************** /* 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(); JsonParser p = createParser(UNQUOTED_FIELDS_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 { String JSON = "{ a : 1, _foo:true, $:\"money!\", \" \":null }"; JsonParser p = createParser(UNQUOTED_FIELDS_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(UNQUOTED_FIELDS_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(); } } NumberCoercionTest.java000066400000000000000000000270121356164247300336450ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage com.fasterxml.jackson.core.read; import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.JsonParser.NumberType; import com.fasterxml.jackson.core.exc.InputCoercionException; public class NumberCoercionTest extends BaseTest { /* /********************************************************** /* Numeric coercions, integral /********************************************************** */ public void testToIntCoercion() throws Exception { for (int mode : ALL_STREAMING_MODES) { JsonParser p; // long->int p = createParser(mode, "1"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(1L, p.getLongValue()); assertEquals(1, p.getIntValue()); p.close(); // BigInteger->int p = createParser(mode, "10"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(BigInteger.TEN, p.getBigIntegerValue()); assertEquals(10, p.getIntValue()); p.close(); // double->int p = createParser(mode, "2"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(2.0, p.getDoubleValue()); assertEquals(2, p.getIntValue()); p.close(); // BigDecimal->int p = createParser(mode, "10"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(BigDecimal.TEN, p.getDecimalValue()); assertEquals(10, p.getIntValue()); p.close(); } } @SuppressWarnings("resource") public void testToIntFailing() throws Exception { for (int mode : ALL_STREAMING_MODES) { JsonParser p; // long -> error long big = 1L + Integer.MAX_VALUE; p = createParser(mode, String.valueOf(big)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(big, p.getLongValue()); try { p.getIntValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of int"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Integer.TYPE, e.getTargetType()); } long small = -1L + Integer.MIN_VALUE; p = createParser(mode, String.valueOf(small)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(Long.valueOf(small), p.getNumberValue()); assertEquals(small, p.getLongValue()); try { p.getIntValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of int"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Integer.TYPE, e.getTargetType()); } // double -> error p = createParser(mode, String.valueOf(big)+".0"); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); assertEquals((double) big, p.getDoubleValue()); try { p.getIntValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of int"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Integer.TYPE, e.getTargetType()); } p = createParser(mode, String.valueOf(small)+".0"); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); assertEquals((double) small, p.getDoubleValue()); try { p.getIntValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of int"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Integer.TYPE, e.getTargetType()); } // BigInteger -> error p = createParser(mode, String.valueOf(big)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(BigInteger.valueOf(big), p.getBigIntegerValue()); try { p.getIntValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of int"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Integer.TYPE, e.getTargetType()); } p = createParser(mode, String.valueOf(small)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(BigInteger.valueOf(small), p.getBigIntegerValue()); try { p.getIntValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of int"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Integer.TYPE, e.getTargetType()); } } } public void testToLongCoercion() throws Exception { for (int mode : ALL_STREAMING_MODES) { JsonParser p; // int->long p = createParser(mode, "1"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(1, p.getIntValue()); assertEquals(1L, p.getLongValue()); p.close(); // BigInteger->long long biggish = 12345678901L; p = createParser(mode, String.valueOf(biggish)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(BigInteger.valueOf(biggish), p.getBigIntegerValue()); assertEquals(biggish, p.getLongValue()); p.close(); // double->long p = createParser(mode, "2"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(2.0, p.getDoubleValue()); assertEquals(2L, p.getLongValue()); p.close(); // BigDecimal->long p = createParser(mode, "10"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(BigDecimal.TEN, p.getDecimalValue()); assertEquals(10, p.getLongValue()); p.close(); } } @SuppressWarnings("resource") public void testToLongFailing() throws Exception { for (int mode : ALL_STREAMING_MODES) { JsonParser p; // BigInteger -> error BigInteger big = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.TEN); p = createParser(mode, String.valueOf(big)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(NumberType.BIG_INTEGER, p.getNumberType()); assertEquals(big, p.getBigIntegerValue()); assertEquals(big, p.getNumberValue()); try { p.getLongValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of long"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Long.TYPE, e.getTargetType()); } BigInteger small = BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.TEN); p = createParser(mode, String.valueOf(small)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(small, p.getBigIntegerValue()); try { p.getLongValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of long"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Long.TYPE, e.getTargetType()); } } } public void testToBigIntegerCoercion() throws Exception { for (int mode : ALL_STREAMING_MODES) { JsonParser p; p = createParser(mode, "1"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // int to BigInteger assertEquals(1, p.getIntValue()); assertEquals(BigInteger.ONE, p.getBigIntegerValue()); p.close(); p = createParser(mode, "2.0"); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); // double to BigInteger assertEquals(2.0, p.getDoubleValue()); assertEquals(BigInteger.valueOf(2L), p.getBigIntegerValue()); p.close(); p = createParser(mode, String.valueOf(Long.MAX_VALUE)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // long to BigInteger assertEquals(Long.MAX_VALUE, p.getLongValue()); assertEquals(BigInteger.valueOf(Long.MAX_VALUE), p.getBigIntegerValue()); p.close(); p = createParser(mode, " 200.0"); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); // BigDecimal to BigInteger assertEquals(new BigDecimal("200.0"), p.getDecimalValue()); assertEquals(BigInteger.valueOf(200L), p.getBigIntegerValue()); p.close(); } } /* /********************************************************** /* Numeric coercions, floating point /********************************************************** */ public void testToDoubleCoercion() throws Exception { for (int mode : ALL_STREAMING_MODES) { JsonParser p; // BigDecimal->double p = createParser(mode, "100.5"); assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); assertEquals(new BigDecimal("100.5"), p.getDecimalValue()); assertEquals(100.5, p.getDoubleValue()); p.close(); p = createParser(mode, "10"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(BigInteger.TEN, p.getBigIntegerValue()); assertEquals(10.0, p.getDoubleValue()); p.close(); } } public void testToBigDecimalCoercion() throws Exception { for (int mode : ALL_STREAMING_MODES) { JsonParser p; p = createParser(mode, "1"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // int to BigDecimal assertEquals(1, p.getIntValue()); assertEquals(BigDecimal.ONE, p.getDecimalValue()); p.close(); p = createParser(mode, String.valueOf(Long.MAX_VALUE)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // long to BigDecimal assertEquals(Long.MAX_VALUE, p.getLongValue()); assertEquals(BigDecimal.valueOf(Long.MAX_VALUE), p.getDecimalValue()); p.close(); BigInteger biggie = BigInteger.valueOf(Long.MAX_VALUE).multiply(BigInteger.TEN); p = createParser(mode, String.valueOf(biggie)); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); // BigInteger to BigDecimal assertEquals(biggie, p.getBigIntegerValue()); assertEquals(new BigDecimal(biggie), p.getDecimalValue()); p.close(); } } } NumberOverflowTest.java000066400000000000000000000115511356164247300337100ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage com.fasterxml.jackson.core.read; import java.math.BigInteger; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.exc.InputCoercionException; public class NumberOverflowTest extends com.fasterxml.jackson.core.BaseTest { private final JsonFactory FACTORY = new JsonFactory(); // NOTE: this should be long enough to trigger perf problems private final static int BIG_NUM_LEN = 199999; private final static String BIG_POS_INTEGER; static { StringBuilder sb = new StringBuilder(BIG_NUM_LEN); for (int i = 0; i < BIG_NUM_LEN; ++i) { sb.append('9'); } BIG_POS_INTEGER = sb.toString(); } private final static String BIG_POS_DOC = "["+BIG_POS_INTEGER+"]"; private final static String BIG_NEG_DOC = "[ -"+BIG_POS_INTEGER+"]"; public void testSimpleLongOverflow() 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(FACTORY, 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 (InputCoercionException 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 (InputCoercionException e) { verifyException(e, "out of range of long"); } p.close(); } } // Note: only 4 cardinal types; `short`, `byte` and `char` use same code paths // Note: due to [jackson-core#493], we'll skip DataInput-backed parser // [jackson-core#488] public void testMaliciousLongOverflow() throws Exception { for (int mode : ALL_STREAMING_MODES) { for (String doc : new String[] { BIG_POS_DOC, BIG_NEG_DOC }) { JsonParser p = createParser(mode, doc); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); try { p.getLongValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of long"); verifyException(e, "Integer with "+BIG_NUM_LEN+" digits"); } p.close(); } } } // [jackson-core#488] public void testMaliciousIntOverflow() throws Exception { for (int mode : ALL_STREAMING_MODES) { for (String doc : new String[] { BIG_POS_DOC, BIG_NEG_DOC }) { JsonParser p = createParser(mode, doc); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); try { p.getIntValue(); fail("Should not pass"); } catch (InputCoercionException e) { verifyException(e, "out of range of int"); verifyException(e, "Integer with "+BIG_NUM_LEN+" digits"); } p.close(); } } } // [jackson-core#488] public void testMaliciousBigIntToDouble() throws Exception { for (int mode : ALL_STREAMING_MODES) { final String doc = BIG_POS_DOC; JsonParser p = createParser(mode, doc); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); double d = p.getDoubleValue(); assertEquals(Double.valueOf(BIG_POS_INTEGER), d); assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); } } // [jackson-core#488] public void testMaliciousBigIntToFloat() throws Exception { for (int mode : ALL_STREAMING_MODES) { final String doc = BIG_POS_DOC; JsonParser p = createParser(mode, doc); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); float f = p.getFloatValue(); assertEquals(Float.valueOf(BIG_POS_INTEGER), f); assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); } } } NumberParsingTest.java000066400000000000000000000540531356164247300335140ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage com.fasterxml.jackson.core.read; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringReader; import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.exc.InputCoercionException; import com.fasterxml.jackson.core.json.JsonReadFeature; /** * 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 (InputCoercionException e) { verifyException(e, "out of range"); assertEquals(JsonToken.VALUE_NUMBER_INT, e.getInputType()); assertEquals(Integer.TYPE, e.getTargetType()); } 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(); } /** * 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 f = JsonFactory.builder() .enable(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS) .build(); _testParsingOfLongerSequencesWithNonNumeric(f, MODE_INPUT_STREAM); _testParsingOfLongerSequencesWithNonNumeric(f, MODE_INPUT_STREAM_THROTTLED); _testParsingOfLongerSequencesWithNonNumeric(f, MODE_READER); _testParsingOfLongerSequencesWithNonNumeric(f, MODE_DATA_INPUT); } private void _testParsingOfLongerSequencesWithNonNumeric(JsonFactory f, int mode) throws Exception { 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 = createParser(f, mode, DOC); assertToken(JsonToken.START_ARRAY, p.nextToken()); for (int j = 0; j < VCOUNT; ++j) { assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); double exp = values[i]; double act = p.getDoubleValue(); if (Double.compare(exp, act) != 0) { fail("Expected at #"+j+" value "+exp+", instead got "+act); } if (Double.isNaN(exp) || Double.isInfinite(exp)) { assertTrue(p.isNaN()); } else { assertFalse(p.isNaN()); } } 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(); } // [core#317] public void testLongerFloatingPoint() throws Exception { StringBuilder input = new StringBuilder(); for (int i = 1; i < 201; i++) { input.append(1); } input.append(".0"); final String DOC = input.toString(); // test out with both Reader and ByteArrayInputStream JsonParser p; p = FACTORY.createParser(new StringReader(DOC)); _testLongerFloat(p, DOC); p.close(); p = FACTORY.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8"))); _testLongerFloat(p, DOC); p.close(); } private void _testLongerFloat(JsonParser p, String text) throws IOException { assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); assertEquals(text, p.getText()); assertNull(p.nextToken()); } public void testInvalidNumber() throws Exception { for (int mode : ALL_MODES) { JsonParser p = createParser(mode, " -foo "); try { p.nextToken(); fail("Should not pass"); } catch (JsonParseException e) { verifyException(e, "Unexpected character ('f'"); } p.close(); } } /* /********************************************************** /* Helper methods /********************************************************** */ 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(); } } ParserDupHandlingTest.java000066400000000000000000000111611356164247300343030ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage 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 = JsonFactory.builder() .enable(StreamReadFeature.STRICT_DUPLICATE_DETECTION) .build(); 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 = JsonFactory.builder() .enable(StreamReadFeature.STRICT_DUPLICATE_DETECTION) .build(); 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+"'"); } } ParserErrorHandlingTest.java000066400000000000000000000103461356164247300346500ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage 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 testMangledIntsBytes() throws Exception { _testMangledNumbersInt(MODE_INPUT_STREAM); _testMangledNumbersInt(MODE_INPUT_STREAM_THROTTLED); // 02-Jun-2017, tatu: Fails to fail; should check whether this is expected // (since DataInput can't do look-ahead) // _testMangledNumbersInt(MODE_DATA_INPUT); } public void testMangledFloatsBytes() throws Exception { _testMangledNumbersFloat(MODE_INPUT_STREAM); _testMangledNumbersFloat(MODE_INPUT_STREAM_THROTTLED); // 02-Jun-2017, tatu: Fails as expected, unlike int one. Bit puzzling... _testMangledNumbersFloat(MODE_DATA_INPUT); } public void testMangledNumbersChars() throws Exception { _testMangledNumbersInt(MODE_READER); _testMangledNumbersFloat(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 _testMangledNumbersInt(int mode) throws Exception { JsonParser p = createParser(mode, "123true"); try { JsonToken t = p.nextToken(); fail("Should have gotten an exception; instead got token: "+t); } catch (JsonParseException e) { verifyException(e, "expected space"); } p.close(); } private void _testMangledNumbersFloat(int mode) throws Exception { // Also test with floats JsonParser p = createParser(mode, "1.5false"); try { JsonToken t = p.nextToken(); fail("Should have gotten an exception; instead got token: "+t); } catch (JsonParseException e) { verifyException(e, "expected space"); } p.close(); } } ParserScopeMatchingTest.java000066400000000000000000000123601356164247300346340ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage 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()); assertEquals(2, p.getIntValue()); try { p.nextToken(); fail("Expected an exception for unclosed ARRAY (mode: "+mode+")"); } catch (JsonParseException pe) { verifyException(pe, "expected close marker for ARRAY"); } } 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 (mode: "+mode+")"); } catch (JsonParseException pe) { verifyException(pe, "expected close marker for OBJECT"); } } 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(); } } ParserSymbolHandlingTest.java000066400000000000000000000040121356164247300350150ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage com.fasterxml.jackson.core.read; import com.fasterxml.jackson.core.*; public class ParserSymbolHandlingTest extends com.fasterxml.jackson.core.BaseTest { // For [core#148] public void testSymbolsWithNullBytes() throws Exception { JsonFactory f = new JsonFactory(); _testSymbolsWithNull(f, true); // and repeat with same factory, just for fun, and to ensure symbol table is fine _testSymbolsWithNull(f, true); } // For [core#148] public void testSymbolsWithNullChars() throws Exception { JsonFactory f = new JsonFactory(); _testSymbolsWithNull(f, false); _testSymbolsWithNull(f, false); } private void _testSymbolsWithNull(JsonFactory f, boolean useBytes) throws Exception { final String INPUT = "{\"\\u0000abc\" : 1, \"abc\":2}"; JsonParser parser = useBytes ? f.createParser(INPUT.getBytes("UTF-8")) : f.createParser(INPUT); assertToken(JsonToken.START_OBJECT, parser.nextToken()); assertToken(JsonToken.FIELD_NAME, parser.nextToken()); String currName = parser.getCurrentName(); if (!"\u0000abc".equals(currName)) { fail("Expected \\0abc (4 bytes), '"+currName+"' ("+currName.length()+")"); } assertToken(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(1, parser.getIntValue()); assertToken(JsonToken.FIELD_NAME, parser.nextToken()); currName = parser.getCurrentName(); if (!"abc".equals(currName)) { /* for (int i = 0; i < currName.length(); ++i) { System.out.println("#"+i+" -> 0x"+Integer.toHexString(currName.charAt(i))); } */ fail("Expected 'abc' (3 bytes), '"+currName+"' ("+currName.length()+")"); } assertToken(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); assertEquals(2, parser.getIntValue()); assertToken(JsonToken.END_OBJECT, parser.nextToken()); parser.close(); } } TrailingCommasTest.java000066400000000000000000000263231356164247300336500ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage com.fasterxml.jackson.core.read; import java.io.IOException; import java.util.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.SerializedString; import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.json.UTF8DataInputJsonParser; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @RunWith(Parameterized.class) @SuppressWarnings("resource") public class TrailingCommasTest extends BaseTest { private final JsonFactory factory; private final Set features; private final int mode; public TrailingCommasTest(int mode, List features) { this.features = new HashSet(features); JsonFactoryBuilder b = (JsonFactoryBuilder) JsonFactory.builder(); for (JsonReadFeature feature : features) { b = b.enable(feature); } factory = b.build(); this.mode = mode; } @Parameterized.Parameters(name = "Mode {0}, Features {1}") public static Collection getTestCases() { ArrayList cases = new ArrayList(); for (int mode : ALL_MODES) { cases.add(new Object[]{mode, Collections.emptyList()}); cases.add(new Object[]{mode, Arrays.asList(JsonReadFeature.ALLOW_MISSING_VALUES)}); cases.add(new Object[]{mode, Arrays.asList(JsonReadFeature.ALLOW_TRAILING_COMMA)}); cases.add(new Object[]{mode, Arrays.asList(JsonReadFeature.ALLOW_MISSING_VALUES, JsonReadFeature.ALLOW_TRAILING_COMMA)}); } return cases; } @Test public void testArrayBasic() throws Exception { String json = "[\"a\", \"b\"]"; JsonParser p = createParser(factory, mode, json); assertEquals(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("a", p.getText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("b", p.getText()); assertEquals(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } @Test public void testArrayInnerComma() throws Exception { String json = "[\"a\",, \"b\"]"; JsonParser p = createParser(factory, mode, json); assertEquals(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("a", p.getText()); if (!features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { assertUnexpected(p, ','); return; } assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("b", p.getText()); assertEquals(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } @Test public void testArrayLeadingComma() throws Exception { String json = "[,\"a\", \"b\"]"; JsonParser p = createParser(factory, mode, json); assertEquals(JsonToken.START_ARRAY, p.nextToken()); if (!features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { assertUnexpected(p, ','); return; } assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("a", p.getText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("b", p.getText()); assertEquals(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); p.close(); } @Test public void testArrayTrailingComma() throws Exception { String json = "[\"a\", \"b\",]"; JsonParser p = createParser(factory, mode, json); assertEquals(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("a", p.getText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("b", p.getText()); // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES if (features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } else if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } else { assertUnexpected(p, ']'); } p.close(); } @Test public void testArrayTrailingCommas() throws Exception { String json = "[\"a\", \"b\",,]"; JsonParser p = createParser(factory, mode, json); assertEquals(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("a", p.getText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("b", p.getText()); // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES) && features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } else if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } else { assertUnexpected(p, ','); } p.close(); } @Test public void testArrayTrailingCommasTriple() throws Exception { String json = "[\"a\", \"b\",,,]"; JsonParser p = createParser(factory, mode, json); assertEquals(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("a", p.getText()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertEquals("b", p.getText()); // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES) && features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } else if (features.contains(JsonReadFeature.ALLOW_MISSING_VALUES)) { assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.VALUE_NULL, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); assertEnd(p); } else { assertUnexpected(p, ','); } p.close(); } @Test public void testObjectBasic() throws Exception { String json = "{\"a\": true, \"b\": false}"; JsonParser p = createParser(factory, mode, json); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("a", p.getText()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("b", p.getText()); assertToken(JsonToken.VALUE_FALSE, p.nextToken()); assertEquals(JsonToken.END_OBJECT, p.nextToken()); assertEnd(p); p.close(); } @Test public void testObjectInnerComma() throws Exception { String json = "{\"a\": true,, \"b\": false}"; JsonParser p = createParser(factory, mode, json); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("a", p.getText()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertUnexpected(p, ','); p.close(); } @Test public void testObjectLeadingComma() throws Exception { String json = "{,\"a\": true, \"b\": false}"; JsonParser p = createParser(factory, mode, json); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertUnexpected(p, ','); p.close(); } @Test public void testObjectTrailingComma() throws Exception { String json = "{\"a\": true, \"b\": false,}"; JsonParser p = createParser(factory, mode, json); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("a", p.getText()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("b", p.getText()); assertToken(JsonToken.VALUE_FALSE, p.nextToken()); if (features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { assertToken(JsonToken.END_OBJECT, p.nextToken()); assertEnd(p); } else { assertUnexpected(p, '}'); } p.close(); } @Test public void testObjectTrailingCommaWithNextFieldName() throws Exception { String json = "{\"a\": true, \"b\": false,}"; JsonParser p = createParser(factory, mode, json); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertEquals("a", p.nextFieldName()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertEquals("b", p.nextFieldName()); assertToken(JsonToken.VALUE_FALSE, p.nextToken()); if (features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { assertEquals(null, p.nextFieldName()); assertToken(JsonToken.END_OBJECT, p.currentToken()); assertEnd(p); } else { try { p.nextFieldName(); fail("No exception thrown"); } catch (Exception e) { verifyException(e, "Unexpected character ('}' (code 125))"); } } p.close(); } @Test public void testObjectTrailingCommaWithNextFieldNameStr() throws Exception { String json = "{\"a\": true, \"b\": false,}"; JsonParser p = createParser(factory, mode, json); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertTrue(p.nextFieldName(new SerializedString("a"))); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertTrue(p.nextFieldName(new SerializedString("b"))); assertToken(JsonToken.VALUE_FALSE, p.nextToken()); if (features.contains(JsonReadFeature.ALLOW_TRAILING_COMMA)) { assertFalse(p.nextFieldName(new SerializedString("c"))); assertToken(JsonToken.END_OBJECT, p.currentToken()); assertEnd(p); } else { try { p.nextFieldName(new SerializedString("c")); fail("No exception thrown"); } catch (Exception e) { verifyException(e, "Unexpected character ('}' (code 125))"); } } p.close(); } @Test public void testObjectTrailingCommas() throws Exception { String json = "{\"a\": true, \"b\": false,,}"; JsonParser p = createParser(factory, mode, json); assertEquals(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("a", p.getText()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); assertEquals("b", p.getText()); assertToken(JsonToken.VALUE_FALSE, p.nextToken()); assertUnexpected(p, ','); p.close(); } private void assertEnd(JsonParser p) throws IOException { // Issue #325 if (!(p instanceof UTF8DataInputJsonParser)) { JsonToken next = p.nextToken(); assertNull("expected end of stream but found " + next, next); } } private void assertUnexpected(JsonParser p, char c) throws IOException { try { p.nextToken(); fail("No exception thrown"); } catch (Exception e) { verifyException(e, String.format("Unexpected character ('%s' (code %d))", c, (int) c)); } } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/read/UTF32ParseTest.java000066400000000000000000000036461356164247300326170ustar00rootroot00000000000000package com.fasterxml.jackson.core.read; import java.io.CharConversionException; import com.fasterxml.jackson.core.*; // Tests from [jackson-core#382] public class UTF32ParseTest extends BaseTest { private final JsonFactory FACTORY = new JsonFactory(); public void testSimpleEOFs() throws Exception { // 2 spaces byte[] data = { 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20 }; for (int len = 5; len <= 7; ++len) { JsonParser parser = FACTORY.createParser(data, 0, len); try { parser.nextToken(); fail("Should not pass"); } catch (CharConversionException e) { verifyException(e, "Unexpected EOF"); verifyException(e, "of a 4-byte UTF-32 char"); } parser.close(); } } public void testSimpleInvalidUTF32() throws Exception { // 2 characters, space, then something beyond valid Unicode set byte[] data = { 0x00, 0x00, 0x00, 0x20, (byte) 0xFE, (byte) 0xFF, 0x00, 0x01 }; JsonParser parser = FACTORY.createParser(data); try { parser.nextToken(); fail("Should not pass"); } catch (CharConversionException e) { verifyException(e, "Invalid UTF-32 character 0xfefe0001"); } parser.close(); } public void testSimpleSevenNullBytes() throws Exception { byte[] data = new byte[7]; JsonParser parser = FACTORY.createParser(/*ObjectReadContext.empty(), */data); try { parser.nextToken(); fail("Should not pass"); } catch (JsonParseException e) { verifyException(e, "Illegal character ((CTRL-CHAR, code 0))"); } parser.close(); } } UTF8NamesParseTest.java000066400000000000000000000201021356164247300334310ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage 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(); } } ValueConversionsTest.java000066400000000000000000000162371356164247300342470ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/readpackage 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(); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/sym/000077500000000000000000000000001356164247300272025ustar00rootroot00000000000000SymbolTableMergingTest.java000066400000000000000000000074041356164247300343610ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/sympackage com.fasterxml.jackson.core.sym; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.json.ReaderBasedJsonParser; import com.fasterxml.jackson.core.json.UTF8StreamJsonParser; /** * Unit tests for verifying that {@link JsonParser} instances properly * merge back symbols to the root symbol table */ @SuppressWarnings("serial") public class SymbolTableMergingTest extends com.fasterxml.jackson.core.BaseTest { /** * To peek into state of "root" symbol tables (parent of all symbol * tables for parsers constructed by this factory) we need to * add some methods. */ final static class MyJsonFactory extends JsonFactory { public int byteSymbolCount() { return _byteSymbolCanonicalizer.size(); } public int charSymbolCount() { return _rootCharSymbols.size(); } } final static String JSON = "{ \"a\" : 3, \"aaa\" : 4, \"_a\" : 0 }"; public void testByteSymbolsWithClose() throws Exception { _testWithClose(true); } public void testByteSymbolsWithEOF() throws Exception { MyJsonFactory f = new MyJsonFactory(); JsonParser jp = _getParser(f, JSON, true); while (jp.nextToken() != null) { // shouldn't update before hitting end assertEquals(0, f.byteSymbolCount()); } // but now should have it after hitting EOF assertEquals(3, f.byteSymbolCount()); jp.close(); assertEquals(3, f.byteSymbolCount()); } public void testHashCalc() throws Exception { CharsToNameCanonicalizer sym = CharsToNameCanonicalizer.createRoot(123); char[] str1 = "foo".toCharArray(); char[] str2 = " foo ".toCharArray(); assertEquals(sym.calcHash(str1, 0, 3), sym.calcHash(str2, 1, 3)); } public void testCharSymbolsWithClose() throws Exception { _testWithClose(false); } public void testCharSymbolsWithEOF() throws Exception { MyJsonFactory f = new MyJsonFactory(); JsonParser jp = _getParser(f, JSON, false); while (jp.nextToken() != null) { // shouldn't update before hitting end assertEquals(0, f.charSymbolCount()); } // but now should have it assertEquals(3, f.charSymbolCount()); jp.close(); assertEquals(3, f.charSymbolCount()); } /* /********************************************************** /* Helper methods /********************************************************** */ private void _testWithClose(boolean useBytes) throws IOException { MyJsonFactory f = new MyJsonFactory(); JsonParser jp = _getParser(f, JSON, useBytes); // Let's check 2 names assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // shouldn't update before close or EOF: assertEquals(0, useBytes ? f.byteSymbolCount() : f.charSymbolCount()); jp.close(); // but should after close assertEquals(2, useBytes ? f.byteSymbolCount() : f.charSymbolCount()); } private JsonParser _getParser(MyJsonFactory f, String doc, boolean useBytes) throws IOException { JsonParser jp; if (useBytes) { jp = f.createParser(doc.getBytes("UTF-8")); assertEquals(UTF8StreamJsonParser.class, jp.getClass()); assertEquals(0, f.byteSymbolCount()); } else { jp = f.createParser(doc); assertEquals(ReaderBasedJsonParser.class, jp.getClass()); assertEquals(0, f.charSymbolCount()); } return jp; } } SymbolsViaParserTest.java000066400000000000000000000060511356164247300340750ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/sympackage com.fasterxml.jackson.core.sym; import java.io.IOException; import java.util.HashSet; import com.fasterxml.jackson.core.*; /** * Tests that use symbol table functionality through parser. */ public class SymbolsViaParserTest extends com.fasterxml.jackson.core.BaseTest { // for [jackson-core#213] public void test17CharSymbols() throws Exception { _test17Chars(false); } // for [jackson-core#213] public void test17ByteSymbols() throws Exception { _test17Chars(true); } // for [jackson-core#216] public void testSymbolTableExpansionChars() throws Exception { _testSymbolTableExpansion(false); } // for [jackson-core#216] public void testSymbolTableExpansionBytes() throws Exception { _testSymbolTableExpansion(true); } /* /********************************************************** /* Secondary test methods /********************************************************** */ private void _test17Chars(boolean useBytes) throws IOException { String doc = _createDoc17(); JsonFactory f = new JsonFactory(); JsonParser p = useBytes ? f.createParser(doc.getBytes("UTF-8")) : f.createParser(doc); HashSet syms = new HashSet(); assertToken(JsonToken.START_OBJECT, p.nextToken()); for (int i = 0; i < 50; ++i) { assertToken(JsonToken.FIELD_NAME, p.nextToken()); syms.add(p.getCurrentName()); assertToken(JsonToken.VALUE_TRUE, p.nextToken()); } assertToken(JsonToken.END_OBJECT, p.nextToken()); assertEquals(50, syms.size()); p.close(); } private String _createDoc17() { StringBuilder sb = new StringBuilder(1000); sb.append("{\n"); for (int i = 1; i <= 50; ++i) { if (i > 1) { sb.append(",\n"); } sb.append("\"lengthmatters") .append(1000 + i) .append("\": true"); } sb.append("\n}"); return sb.toString(); } public void _testSymbolTableExpansion(boolean useBytes) throws Exception { JsonFactory jsonFactory = new JsonFactory(); // Important: must create separate documents to gradually build up symbol table for (int i = 0; i < 200; i++) { String field = Integer.toString(i); final String doc = "{ \"" + field + "\" : \"test\" }"; JsonParser parser = useBytes ? jsonFactory.createParser(doc.getBytes("UTF-8")) : jsonFactory.createParser(doc); assertToken(JsonToken.START_OBJECT, parser.nextToken()); assertToken(JsonToken.FIELD_NAME, parser.nextToken()); assertEquals(field, parser.getCurrentName()); assertToken(JsonToken.VALUE_STRING, parser.nextToken()); assertToken(JsonToken.END_OBJECT, parser.nextToken()); assertNull(parser.nextToken()); parser.close(); } } } TestByteBasedSymbols.java000066400000000000000000000156521356164247300340520ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/sympackage com.fasterxml.jackson.core.sym; import java.io.*; import java.lang.reflect.Field; import java.util.Random; import com.fasterxml.jackson.core.*; /** * Unit test(s) to verify that handling of (byte-based) symbol tables * is working. */ public class TestByteBasedSymbols extends com.fasterxml.jackson.core.BaseTest { final static String[] FIELD_NAMES = new String[] { "a", "b", "c", "x", "y", "b13", "abcdefg", "a123", "a0", "b0", "c0", "d0", "e0", "f0", "g0", "h0", "x2", "aa", "ba", "ab", "b31", "___x", "aX", "xxx", "a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2", "a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3", "a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1", }; /** * This unit test checks that [JACKSON-5] is fixed; if not, a * symbol table corruption should result in odd problems. */ public void testSharedSymbols() throws Exception { // MUST share a single json factory JsonFactory jf = new JsonFactory(); /* First things first: parse a dummy doc to populate * shared symbol table with some stuff */ String DOC0 = "{ \"a\" : 1, \"x\" : [ ] }"; JsonParser jp0 = createParser(jf, DOC0); /* Important: don't close, don't traverse past end. * This is needed to create partial still-in-use symbol * table... */ while (jp0.nextToken() != JsonToken.START_ARRAY) { } String doc1 = createDoc(FIELD_NAMES, true); String doc2 = createDoc(FIELD_NAMES, false); // Let's run it twice... shouldn't matter for (int x = 0; x < 2; ++x) { JsonParser jp1 = createParser(jf, doc1); JsonParser jp2 = createParser(jf, doc2); assertToken(JsonToken.START_OBJECT, jp1.nextToken()); assertToken(JsonToken.START_OBJECT, jp2.nextToken()); int len = FIELD_NAMES.length; for (int i = 0; i < len; ++i) { assertToken(JsonToken.FIELD_NAME, jp1.nextToken()); assertToken(JsonToken.FIELD_NAME, jp2.nextToken()); assertEquals(FIELD_NAMES[i], jp1.getCurrentName()); assertEquals(FIELD_NAMES[len-(i+1)], jp2.getCurrentName()); assertToken(JsonToken.VALUE_NUMBER_INT, jp1.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp2.nextToken()); assertEquals(i, jp1.getIntValue()); assertEquals(i, jp2.getIntValue()); } assertToken(JsonToken.END_OBJECT, jp1.nextToken()); assertToken(JsonToken.END_OBJECT, jp2.nextToken()); jp1.close(); jp2.close(); } jp0.close(); } public void testAuxMethodsWithNewSymboTable() throws Exception { final int A_BYTES = 0x41414141; // "AAAA" final int B_BYTES = 0x42424242; // "BBBB" ByteQuadsCanonicalizer nc = ByteQuadsCanonicalizer.createRoot() .makeChild(JsonFactory.Feature.collectDefaults()); assertNull(nc.findName(A_BYTES)); assertNull(nc.findName(A_BYTES, B_BYTES)); nc.addName("AAAA", new int[] { A_BYTES }, 1); String n1 = nc.findName(A_BYTES); assertEquals("AAAA", n1); nc.addName("AAAABBBB", new int[] { A_BYTES, B_BYTES }, 2); String n2 = nc.findName(A_BYTES, B_BYTES); assertEquals("AAAABBBB", n2); assertNotNull(n2); /* and let's then just exercise this method so it gets covered; * it's only used for debugging. */ assertNotNull(nc.toString()); } // as per name, for [core#207] public void testIssue207() throws Exception { ByteQuadsCanonicalizer nc = ByteQuadsCanonicalizer.createRoot(-523743345); Field byteSymbolCanonicalizerField = JsonFactory.class.getDeclaredField("_byteSymbolCanonicalizer"); byteSymbolCanonicalizerField.setAccessible(true); JsonFactory jsonF = new JsonFactory(); byteSymbolCanonicalizerField.set(jsonF, nc); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("{\n"); stringBuilder.append(" \"expectedGCperPosition\": null"); for (int i = 0; i < 60; ++i) { stringBuilder.append(",\n \"").append(i + 1).append("\": null"); } stringBuilder.append("\n}"); JsonParser p = jsonF.createParser(stringBuilder.toString().getBytes("UTF-8")); while (p.nextToken() != null) { } p.close(); } // [core#548] public void testQuadsIssue548() { Random r = new Random(42); ByteQuadsCanonicalizer root = ByteQuadsCanonicalizer.createRoot(); ByteQuadsCanonicalizer canon = root.makeChild(JsonFactory.Feature.collectDefaults()); int n_collisions = 25; int[] collisions = new int[n_collisions]; // generate collisions { int maybe = r.nextInt(); int hash = canon.calcHash(maybe); int target = ((hash & (2048-1)) << 2); for (int i = 0; i < collisions.length; ) { maybe = r.nextInt(); hash = canon.calcHash(maybe); int offset = ((hash & (2048-1)) << 2); if (offset == target) { collisions[i++] = maybe; } } } // fill spillover area until _needRehash is true. for(int i = 0; i < 22 ; i++) { canon.addName(Integer.toString(i), collisions[i]); } // canon._needRehash is now true, since the spillover is full // release table to update tableinfo with canon's data canon.release(); // new table pulls data from new tableinfo, that has a full spillover, but set _needRehash to false canon = root.makeChild(JsonFactory.Feature.collectDefaults()); // canon._needRehash == false, so this will try to add another item to the spillover area, even though it is full canon.addName(Integer.toString(22), collisions[22]); } /* /********************************************************** /* Helper methods /********************************************************** */ protected JsonParser createParser(JsonFactory jf, String input) throws IOException { byte[] data = input.getBytes("UTF-8"); InputStream is = new ByteArrayInputStream(data); return jf.createParser(is); } private String createDoc(String[] fieldNames, boolean add) { StringBuilder sb = new StringBuilder(); sb.append("{ "); int len = fieldNames.length; for (int i = 0; i < len; ++i) { if (i > 0) { sb.append(", "); } sb.append('"'); sb.append(add ? fieldNames[i] : fieldNames[len - (i+1)]); sb.append("\" : "); sb.append(i); } sb.append(" }"); return sb.toString(); } } TestHashCollisionChars.java000066400000000000000000000122211356164247300343440ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/sympackage com.fasterxml.jackson.core.sym; import java.util.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer; /** * Some unit tests to try to exercise part of parser code that * deals with symbol (table) management. *

* Note that the problem does not necessarily affect code at or * after Jackson 2.6, since hash calculation algorithm has been * completely changed. It may still be relevant for character-backed * sources, however. */ public class TestHashCollisionChars extends BaseTest { // // // And then a nastier variant; collisions generated using // // // CollisionGenerator // for 33 final static String[] MULT_COLLISION_FRAGMENTS = new String[] { // Ones generated for 33/65536... "9fa", "9g@", ":Ea", ":F@", ";$a", ";%@" }; // for 31 /* final static String[] MULT_COLLISION_FRAGMENTS = new String[] { // Ones generated for 31/65536... "@~~", "A_~", "A`_", "Aa@", "Ab!", "B@~", // "BA_", "BB@", "BC!", "C!~" }; */ public void testReaderCollisions() throws Exception { StringBuilder sb = new StringBuilder(); List coll = collisions(); for (String field : coll) { if (sb.length() == 0) { sb.append("{"); } else { sb.append(",\n"); } sb.append('"'); sb.append(field); sb.append("\":3"); } sb.append("}"); // First: attempt with exceptions turned on; should catch an exception JsonFactory f = JsonFactory.builder() .enable(JsonFactory.Feature.FAIL_ON_SYMBOL_HASH_OVERFLOW) .build(); JsonParser p = f.createParser(sb.toString()); try { while (p.nextToken() != null) { ; } fail("Should have failed"); } catch (IllegalStateException e) { verifyException(e, "hash collision"); } p.close(); // but then without feature, should pass f = JsonFactory.builder() .disable(JsonFactory.Feature.FAIL_ON_SYMBOL_HASH_OVERFLOW) .build(); p = f.createParser(sb.toString()); while (p.nextToken() != null) { ; } p.close(); } /* /********************************************************** /* Helper methods /********************************************************** */ static List collisions() { // we'll get 6^4, which is bit over 1k ArrayList result = new ArrayList(36 * 36); final String[] FRAGMENTS = MULT_COLLISION_FRAGMENTS; for (String str1 : FRAGMENTS) { for (String str2 : FRAGMENTS) { for (String str3 : FRAGMENTS) { for (String str4 : FRAGMENTS) { result.add(str1+str2+str3+str4); } } } } return result; } /* /********************************************************** /* Helper class(es) /********************************************************** */ /** * Helper class to use for generating substrings to use for substring * substitution collisions. */ static class CollisionGenerator { /* JDK uses 31, but Jackson `CharsToNameCanonicalizer.HASH_MULT`, * which for 2.3 is 33. */ final static int MULT = CharsToNameCanonicalizer.HASH_MULT; public void generate3(int h0) { int p1 = MULT; int p0 = MULT * MULT; // Generate chars in printable ASCII range final char minChar = (char) 32; // final char maxChar = (char) 127; final char maxChar = (char) 127; for (char c0 = minChar; c0 <= maxChar && c0 <= h0 / p0; c0++) { int h1 = h0 - c0 * p0; for (char c1 = minChar; c1 <= maxChar && c1 <= h1 / p1; c1++) { int h2 = h1 - c1 * p1; if (h2 < minChar || h2 > maxChar) { continue; } char c2 = (char) h2; if (h2 != c2) { continue; } printString(new String(new char[] { c0, c1, c2 } )); } } } void printString(String s) { //System.out.println(s.length() + " " + s.hashCode() + " " + asIntArray(s)); System.out.println(s.length() + " " + s.hashCode() + " \"" + s + "\""); } String asIntArray(String s) { StringBuilder result = new StringBuilder().append("["); for (int c = 0; c < s.length(); c++) { if (c > 0) { result.append(", "); } result.append((int) s.charAt(c)); } result.append("]"); return result.toString(); } } public static void main(String[] args) { System.out.println(""); // new CollisionGenerator().generate3(1 << 20); new CollisionGenerator().generate3(1 << 16); System.out.println(); System.out.println(""); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/sym/TestSymbolTables.java000066400000000000000000000352651356164247300333200ustar00rootroot00000000000000package com.fasterxml.jackson.core.sym; import java.io.IOException; import java.lang.reflect.Field; import java.nio.charset.Charset; import com.fasterxml.jackson.core.*; /** * Tests that directly modify/access underlying low-level symbol tables * (instead of indirectly using them via JsonParser). */ public class TestSymbolTables extends com.fasterxml.jackson.core.BaseTest { // Test for verifying stability of hashCode, wrt collisions, using // synthetic field name generation and character-based input public void testSyntheticWithChars() { // pass seed, to keep results consistent: CharsToNameCanonicalizer symbols = CharsToNameCanonicalizer.createRoot(1).makeChild(-1); final int COUNT = 12000; for (int i = 0; i < COUNT; ++i) { String id = fieldNameFor(i); char[] ch = id.toCharArray(); symbols.findSymbol(ch, 0, ch.length, symbols.calcHash(id)); } assertEquals(16384, symbols.bucketCount()); assertEquals(COUNT, symbols.size()); //System.out.printf("Char stuff: collisions %d, max-coll %d\n", symbols.collisionCount(), symbols.maxCollisionLength()); // holy guacamoley... there are way too many. 31 gives 3567 (!), 33 gives 2747 // ... at least before shuffling. Shuffling helps quite a lot, so: assertEquals(3431, symbols.collisionCount()); assertEquals(6, symbols.maxCollisionLength()); // and final validation symbols.verifyInternalConsistency(); } public void testSyntheticWithBytesNew() throws IOException { // pass seed, to keep results consistent: final int SEED = 33333; ByteQuadsCanonicalizer symbols = ByteQuadsCanonicalizer.createRoot(SEED).makeChild(JsonFactory.Feature.collectDefaults()); final int COUNT = 12000; for (int i = 0; i < COUNT; ++i) { String id = fieldNameFor(i); int[] quads = calcQuads(id.getBytes("UTF-8")); symbols.addName(id, quads, quads.length); } assertEquals(COUNT, symbols.size()); assertEquals(16384, symbols.bucketCount()); // fragile, but essential to verify low collision counts; // anywhere between 70-80% primary matches assertEquals(8534, symbols.primaryCount()); // secondary between 10-20% assertEquals(2534, symbols.secondaryCount()); // and most of remaining in tertiary assertEquals(932, symbols.tertiaryCount()); // so that spill-over is empty or close to assertEquals(0, symbols.spilloverCount()); } // [Issue#145] public void testThousandsOfSymbolsWithChars() throws IOException { final int SEED = 33333; CharsToNameCanonicalizer symbolsCRoot = CharsToNameCanonicalizer.createRoot(SEED); int exp = 0; for (int doc = 0; doc < 100; ++doc) { CharsToNameCanonicalizer symbolsC = symbolsCRoot.makeChild(JsonFactory.Feature.collectDefaults()); for (int i = 0; i < 250; ++i) { String name = "f_"+doc+"_"+i; char[] ch = name.toCharArray(); String str = symbolsC.findSymbol(ch, 0, ch.length, symbolsC.calcHash(name)); assertNotNull(str); } // validate further, just to make sure symbolsC.verifyInternalConsistency(); symbolsC.release(); exp += 250; if (exp > CharsToNameCanonicalizer.MAX_ENTRIES_FOR_REUSE) { exp = 0; } assertEquals(exp, symbolsCRoot.size()); } // Note: can not validate root instance, is not set up same way } // Since 2.6 public void testThousandsOfSymbolsWithNew() throws IOException { final int SEED = 33333; ByteQuadsCanonicalizer symbolsBRoot = ByteQuadsCanonicalizer.createRoot(SEED); final Charset utf8 = Charset.forName("UTF-8"); int exp = 0; ByteQuadsCanonicalizer symbolsB = null; // loop to get for (int doc = 0; doc < 100; ++doc) { symbolsB = symbolsBRoot.makeChild(JsonFactory.Feature.collectDefaults()); for (int i = 0; i < 250; ++i) { String name = "f_"+doc+"_"+i; int[] quads = calcQuads(name.getBytes(utf8)); symbolsB.addName(name, quads, quads.length); String n = symbolsB.findName(quads, quads.length); assertEquals(name, n); } symbolsB.release(); exp += 250; if (exp > ByteQuadsCanonicalizer.MAX_ENTRIES_FOR_REUSE) { exp = 0; } assertEquals(exp, symbolsBRoot.size()); } // 05-Feb-2015, tatu: Fragile, but it is important to ensure that collision // rates are not accidentally increased... assertEquals(6250, symbolsB.size()); assertEquals(4761, symbolsB.primaryCount()); // 80% primary hit rate assertEquals(1190, symbolsB.secondaryCount()); // 13% secondary assertEquals(299, symbolsB.tertiaryCount()); // 7% tertiary assertEquals(0, symbolsB.spilloverCount()); // and couple of leftovers } // And then one more test just for Bytes-based symbol table public void testByteBasedSymbolTable() throws Exception { // combination of short, medium1/2, long names... final String JSON = aposToQuotes("{'abc':1, 'abc\\u0000':2, '\\u0000abc':3, " // then some medium +"'abc123':4,'abcd1234':5," +"'abcd1234a':6,'abcd1234abcd':7," +"'abcd1234abcd1':8" +"}"); JsonFactory f = new JsonFactory(); JsonParser p = f.createParser(JSON.getBytes("UTF-8")); ByteQuadsCanonicalizer symbols = _findSymbols(p); assertEquals(0, symbols.size()); _streamThrough(p); assertEquals(8, symbols.size()); p.close(); // and, for fun, try again p = f.createParser(JSON.getBytes("UTF-8")); _streamThrough(p); symbols = _findSymbols(p); assertEquals(8, symbols.size()); p.close(); p = f.createParser(JSON.getBytes("UTF-8")); _streamThrough(p); symbols = _findSymbols(p); assertEquals(8, symbols.size()); p.close(); } private void _streamThrough(JsonParser p) throws IOException { while (p.nextToken() != null) { } } private ByteQuadsCanonicalizer _findSymbols(JsonParser p) throws Exception { Field syms = p.getClass().getDeclaredField("_symbols"); syms.setAccessible(true); return ((ByteQuadsCanonicalizer) syms.get(p)); } // [core#187]: unexpectedly high number of collisions for straight numbers public void testCollisionsWithChars187() throws IOException { CharsToNameCanonicalizer symbols = CharsToNameCanonicalizer.createRoot(1).makeChild(-1); final int COUNT = 30000; for (int i = 0; i < COUNT; ++i) { String id = String.valueOf(10000 + i); char[] ch = id.toCharArray(); symbols.findSymbol(ch, 0, ch.length, symbols.calcHash(id)); } assertEquals(COUNT, symbols.size()); assertEquals(65536, symbols.bucketCount()); // collision count rather high, but has to do assertEquals(7127, symbols.collisionCount()); // as well as collision counts assertEquals(4, symbols.maxCollisionLength()); } // [core#187]: unexpectedly high number of collisions for straight numbers public void testCollisionsWithBytesNew187a() throws IOException { ByteQuadsCanonicalizer symbols = ByteQuadsCanonicalizer.createRoot(1).makeChild(JsonFactory.Feature.collectDefaults()); final int COUNT = 43000; for (int i = 0; i < COUNT; ++i) { String id = String.valueOf(10000 + i); int[] quads = calcQuads(id.getBytes("UTF-8")); symbols.addName(id, quads, quads.length); } assertEquals(COUNT, symbols.size()); assertEquals(65536, symbols.bucketCount()); /* 29-Mar-2015, tatu: To get collision counts down for this * test took quite a bit of tweaking... */ assertEquals(32342, symbols.primaryCount()); assertEquals(8863, symbols.secondaryCount()); assertEquals(1795, symbols.tertiaryCount()); // finally managed to get this to 0; other variants produced thousands assertEquals(0, symbols.spilloverCount()); } // Another variant, but with 1-quad names public void testCollisionsWithBytesNew187b() throws IOException { ByteQuadsCanonicalizer symbols = ByteQuadsCanonicalizer.createRoot(1).makeChild(JsonFactory.Feature.collectDefaults()); final int COUNT = 10000; for (int i = 0; i < COUNT; ++i) { String id = String.valueOf(i); int[] quads = calcQuads(id.getBytes("UTF-8")); symbols.addName(id, quads, quads.length); } assertEquals(COUNT, symbols.size()); assertEquals(16384, symbols.bucketCount()); // fragile, but essential to verify low collision counts; // here bit low primary, 55% assertEquals(5402, symbols.primaryCount()); // secondary higher than usual, above 25% assertEquals(2744, symbols.secondaryCount()); // and most of remaining in tertiary assertEquals(1834, symbols.tertiaryCount()); // with a bit of spillover assertEquals(20, symbols.spilloverCount()); } // [core#191]: similarly, but for "short" symbols: public void testShortNameCollisionsViaParser() throws Exception { JsonFactory f = new JsonFactory(); String json = _shortDoc191(); JsonParser p; // First: ensure that char-based is fine p = f.createParser(json); while (p.nextToken() != null) { } p.close(); // and then that byte-based p = f.createParser(json.getBytes("UTF-8")); while (p.nextToken() != null) { } p.close(); } private String _shortDoc191() { StringBuilder sb = new StringBuilder(); sb.append("{\n"); for (int i = 0; i < 400; ++i) { if (i > 0) { sb.append(",\n"); } sb.append('"'); char c = (char) i; if (Character.isLetterOrDigit(c)) { sb.append((char) i); } else { sb.append(String.format("\\u%04x", i)); } sb.append("\" : "+i); } sb.append("}\n"); return sb.toString(); } // [core#191] public void testShortQuotedDirectChars() throws IOException { final int COUNT = 400; CharsToNameCanonicalizer symbols = CharsToNameCanonicalizer.createRoot(1).makeChild(-1); for (int i = 0; i < COUNT; ++i) { String id = String.format("\\u%04x", i); char[] ch = id.toCharArray(); symbols.findSymbol(ch, 0, ch.length, symbols.calcHash(id)); } assertEquals(COUNT, symbols.size()); assertEquals(1024, symbols.bucketCount()); assertEquals(50, symbols.collisionCount()); assertEquals(2, symbols.maxCollisionLength()); } public void testShortQuotedDirectBytes() throws IOException { final int COUNT = 400; ByteQuadsCanonicalizer symbols = ByteQuadsCanonicalizer.createRoot(123).makeChild(JsonFactory.Feature.collectDefaults()); for (int i = 0; i < COUNT; ++i) { String id = String.format("\\u%04x", i); int[] quads = calcQuads(id.getBytes("UTF-8")); symbols.addName(id, quads, quads.length); } assertEquals(COUNT, symbols.size()); assertEquals(512, symbols.bucketCount()); assertEquals(285, symbols.primaryCount()); assertEquals(90, symbols.secondaryCount()); assertEquals(25, symbols.tertiaryCount()); assertEquals(0, symbols.spilloverCount()); } // [core#191] public void testShortNameCollisionsDirect() throws IOException { final int COUNT = 600; // First, char-based { CharsToNameCanonicalizer symbols = CharsToNameCanonicalizer.createRoot(1).makeChild(-1); for (int i = 0; i < COUNT; ++i) { String id = String.valueOf((char) i); char[] ch = id.toCharArray(); symbols.findSymbol(ch, 0, ch.length, symbols.calcHash(id)); } assertEquals(COUNT, symbols.size()); assertEquals(1024, symbols.bucketCount()); assertEquals(16, symbols.collisionCount()); assertEquals(1, symbols.maxCollisionLength()); } } public void testShortNameCollisionsDirectNew() throws IOException { final int COUNT = 700; { ByteQuadsCanonicalizer symbols = ByteQuadsCanonicalizer.createRoot(333).makeChild(JsonFactory.Feature.collectDefaults()); for (int i = 0; i < COUNT; ++i) { String id = String.valueOf((char) i); int[] quads = calcQuads(id.getBytes("UTF-8")); symbols.addName(id, quads, quads.length); } assertEquals(COUNT, symbols.size()); assertEquals(1024, symbols.bucketCount()); // Primary is good, but secondary spills cluster in nasty way... assertEquals(564, symbols.primaryCount()); assertEquals(122, symbols.secondaryCount()); assertEquals(14, symbols.tertiaryCount()); assertEquals(0, symbols.spilloverCount()); assertEquals(COUNT, symbols.primaryCount() + symbols.secondaryCount() + symbols.tertiaryCount() + symbols.spilloverCount()); } } // to verify [jackson-core#213] -- did not fail, but ruled out low-level bug public void testLongSymbols17Bytes() throws Exception { ByteQuadsCanonicalizer symbolsB = ByteQuadsCanonicalizer.createRoot(3).makeChild(JsonFactory.Feature.collectDefaults()); CharsToNameCanonicalizer symbolsC = CharsToNameCanonicalizer.createRoot(3).makeChild(-1); for (int i = 1001; i <= 1050; ++i) { String id = "lengthmatters"+i; int[] quads = calcQuads(id.getBytes("UTF-8")); symbolsB.addName(id, quads, quads.length); char[] idChars = id.toCharArray(); symbolsC.findSymbol(idChars, 0, idChars.length, symbolsC.calcHash(id)); } assertEquals(50, symbolsB.size()); assertEquals(50, symbolsC.size()); } } TestSymbolsWithMediaItem.java000066400000000000000000000066051356164247300347000ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/sympackage com.fasterxml.jackson.core.sym; import java.io.IOException; import com.fasterxml.jackson.core.*; public class TestSymbolsWithMediaItem extends com.fasterxml.jackson.core.BaseTest { private final String JSON = aposToQuotes( "{'media' : {\n" +" 'uri' : 'http://foo.com'," +" 'title' : 'Test title 1'," +" 'width' : 640, 'height' : 480," +" 'format' : 'video/mpeg4'," +" 'duration' : 18000000," +" 'size' : 58982400," +" 'bitrate' : 262144," +" 'persons' : [ ]," +" 'player' : 'native'," +" 'copyright' : 'None'" +" },\n" +" 'images' : [ {\n" +" 'uri' : 'http://bar.com',\n" +" 'title' : 'Test title 1',\n" +" 'width' : 1024,'height' : 768,\n" +" 'size' : 'LARGE'\n" +" }, {\n" +" 'uri' : 'http://foobar.org',\n" +" 'title' : 'Javaone Keynote',\n" +" 'width' : 320, 'height' : 240,\n" +" 'size' : 'SMALL'\n" +" } ]\n" +"}\n"); public void testSmallSymbolSetWithBytes() throws IOException { final int SEED = 33333; ByteQuadsCanonicalizer symbolsRoot = ByteQuadsCanonicalizer.createRoot(SEED); ByteQuadsCanonicalizer symbols = symbolsRoot.makeChild(JsonFactory.Feature.collectDefaults()); JsonFactory f = new JsonFactory(); JsonParser p = f.createParser(JSON.getBytes("UTF-8")); JsonToken t; while ((t = p.nextToken()) != null) { if (t != JsonToken.FIELD_NAME) { continue; } String name = p.getCurrentName(); int[] quads = calcQuads(name.getBytes("UTF-8")); if (symbols.findName(quads, quads.length) != null) { continue; } symbols.addName(name, quads, quads.length); } p.close(); assertEquals(13, symbols.size()); assertEquals(12, symbols.primaryCount()); // 80% primary hit rate assertEquals(1, symbols.secondaryCount()); // 13% secondary assertEquals(0, symbols.tertiaryCount()); // 7% tertiary assertEquals(0, symbols.spilloverCount()); // and couple of leftovers } public void testSmallSymbolSetWithChars() throws IOException { final int SEED = 33333; CharsToNameCanonicalizer symbols = CharsToNameCanonicalizer.createRoot(SEED).makeChild(-1); JsonFactory f = new JsonFactory(); JsonParser p = f.createParser(JSON); JsonToken t; while ((t = p.nextToken()) != null) { if (t != JsonToken.FIELD_NAME) { continue; } String name = p.getCurrentName(); char[] ch = name.toCharArray(); symbols.findSymbol(ch, 0, ch.length, symbols.calcHash(name)); } p.close(); assertEquals(13, symbols.size()); assertEquals(13, symbols.size()); assertEquals(64, symbols.bucketCount()); // usually get 1 collision, but sometimes get lucky with 0; other times less so with 2 // (with differing shifting for hash etc) assertEquals(0, symbols.collisionCount()); assertEquals(0, symbols.maxCollisionLength()); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/testsupport/000077500000000000000000000000001356164247300310065ustar00rootroot00000000000000AsyncReaderWrapper.java000066400000000000000000000054211356164247300353350ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/testsupportpackage com.fasterxml.jackson.core.testsupport; import java.io.IOException; import java.io.StringWriter; import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonParser.NumberType; import com.fasterxml.jackson.core.JsonStreamContext; import com.fasterxml.jackson.core.JsonToken; public abstract class AsyncReaderWrapper { protected final JsonParser _streamReader; protected AsyncReaderWrapper(JsonParser sr) { _streamReader = sr; } public JsonToken currentToken() throws IOException { return _streamReader.currentToken(); } public String currentText() throws IOException { return _streamReader.getText(); } public String currentTextViaCharacters() throws IOException { char[] ch = _streamReader.getTextCharacters(); int start = _streamReader.getTextOffset(); int len = _streamReader.getTextLength(); return new String(ch, start, len); } public String currentTextViaWriter() throws IOException { StringWriter sw = new StringWriter(); int len = _streamReader.getText(sw); String str = sw.toString(); if (len != str.length()) { throw new IllegalStateException(String.format( "Reader.getText(Writer) returned %d, but wrote %d chars", len, str.length())); } return str; } public String currentName() throws IOException { return _streamReader.getCurrentName(); } public JsonParser parser() { return _streamReader; } public abstract JsonToken nextToken() throws IOException; public JsonStreamContext getParsingContext() { return _streamReader.getParsingContext(); } public int getIntValue() throws IOException { return _streamReader.getIntValue(); } public long getLongValue() throws IOException { return _streamReader.getLongValue(); } public float getFloatValue() throws IOException { return _streamReader.getFloatValue(); } public double getDoubleValue() throws IOException { return _streamReader.getDoubleValue(); } public BigInteger getBigIntegerValue() throws IOException { return _streamReader.getBigIntegerValue(); } public BigDecimal getDecimalValue() throws IOException { return _streamReader.getDecimalValue(); } public byte[] getBinaryValue() throws IOException { return _streamReader.getBinaryValue(); } public Number getNumberValue() throws IOException { return _streamReader.getNumberValue(); } public NumberType getNumberType() throws IOException { return _streamReader.getNumberType(); } public void close() throws IOException { _streamReader.close(); } public boolean isClosed() { return _streamReader.isClosed(); } } AsyncReaderWrapperForByteArray.java000066400000000000000000000034771356164247300376400ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/testsupportpackage com.fasterxml.jackson.core.testsupport; import java.io.IOException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.async.ByteArrayFeeder; /** * Helper class used with async parser */ public class AsyncReaderWrapperForByteArray extends AsyncReaderWrapper { private final byte[] _doc; private final int _bytesPerFeed; private final int _padding; private int _offset; private int _end; public AsyncReaderWrapperForByteArray(JsonParser sr, int bytesPerCall, byte[] doc, int padding) { super(sr); _bytesPerFeed = bytesPerCall; _doc = doc; _offset = 0; _end = doc.length; _padding = padding; } @Override public JsonToken nextToken() throws IOException { JsonToken token; while ((token = _streamReader.nextToken()) == JsonToken.NOT_AVAILABLE) { ByteArrayFeeder feeder = (ByteArrayFeeder) _streamReader.getNonBlockingInputFeeder(); if (!feeder.needMoreInput()) { throw new IOException("Got NOT_AVAILABLE, could not feed more input"); } int amount = Math.min(_bytesPerFeed, _end - _offset); if (amount < 1) { // end-of-input? feeder.endOfInput(); } else { // padding? if (_padding == 0) { feeder.feedInput(_doc, _offset, _offset+amount); } else { byte[] tmp = new byte[amount + _padding + _padding]; System.arraycopy(_doc, _offset, tmp, _padding, amount); feeder.feedInput(tmp, _padding, _padding+amount); } _offset += amount; } } return token; } } ByteOutputStreamForTesting.java000066400000000000000000000013601356164247300370770ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/testsupportpackage com.fasterxml.jackson.core.testsupport; import java.io.ByteArrayOutputStream; import java.io.IOException; /** * Helper class for verifying that {@link java.io.OutputStream} is (or is not) * closed and/or flushed. */ public class ByteOutputStreamForTesting extends ByteArrayOutputStream { public int closeCount = 0; public int flushCount = 0; public ByteOutputStreamForTesting() { } @Override public void close() throws IOException { ++closeCount; super.close(); } @Override public void flush() throws IOException { ++flushCount; super.flush(); } public boolean isClosed() { return closeCount > 0; } public boolean isFlushed() { return flushCount > 0; } } MockDataInput.java000066400000000000000000000045601356164247300343020ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/testsupportpackage 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 EOFException("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(); } } StringWriterForTesting.java000066400000000000000000000011221356164247300362360ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/testsupportpackage com.fasterxml.jackson.core.testsupport; import java.io.IOException; import java.io.StringWriter; public class StringWriterForTesting extends StringWriter { public int closeCount = 0; public int flushCount = 0; public StringWriterForTesting() { } @Override public void close() throws IOException { ++closeCount; super.close(); } @Override public void flush() { ++flushCount; super.flush(); } public boolean isClosed() { return closeCount > 0; } public boolean isFlushed() { return flushCount > 0; } } ThrottledInputStream.java000066400000000000000000000014501356164247300357370ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/testsupportpackage 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)); } }jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/type/000077500000000000000000000000001356164247300273535ustar00rootroot00000000000000TypeReferenceTest.java000066400000000000000000000063571356164247300335520ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/typepackage com.fasterxml.jackson.core.type; import java.util.List; import com.fasterxml.jackson.core.BaseTest; // Not much to test, but exercise to prevent code coverage tool from showing all red for package public class TypeReferenceTest extends BaseTest { static class BogusResolvedType extends ResolvedType { private final boolean _refType; public BogusResolvedType(boolean isRefType) { _refType = isRefType; } @Override public Class getRawClass() { return null; } @Override public boolean hasRawClass(Class clz) { return false; } @Override public boolean isAbstract() { return false; } @Override public boolean isConcrete() { return false; } @Override public boolean isThrowable() { return false; } @Override public boolean isArrayType() { return false; } @Override public boolean isEnumType() { return false; } @Override public boolean isInterface() { return false; } @Override public boolean isPrimitive() { return false; } @Override public boolean isFinal() { return false; } @Override public boolean isContainerType() { return false; } @Override public boolean isCollectionLikeType() { return false; } @Override public boolean isMapLikeType() { return false; } @Override public boolean hasGenericTypes() { return false; } @Override public ResolvedType getKeyType() { return null; } @Override public ResolvedType getContentType() { return null; } @Override public ResolvedType getReferencedType() { if (_refType) { return this; } return null; } @Override public int containedTypeCount() { return 0; } @Override public ResolvedType containedType(int index) { return null; } @Override public String containedTypeName(int index) { return null; } @Override public String toCanonical() { return null; } } public void testSimple() { TypeReference ref = new TypeReference>() { }; assertNotNull(ref); ref.equals(null); } @SuppressWarnings("rawtypes") public void testInvalid() { try { Object ob = new TypeReference() { }; fail("Should not pass, got: "+ob); } catch (IllegalArgumentException e) { verifyException(e, "without actual type information"); } } public void testResolvedType() { ResolvedType type1 = new BogusResolvedType(false); assertFalse(type1.isReferenceType()); ResolvedType type2 = new BogusResolvedType(true); assertTrue(type2.isReferenceType()); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/util/000077500000000000000000000000001356164247300273475ustar00rootroot00000000000000ByteArrayBuilderTest.java000066400000000000000000000013731356164247300342100ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; import org.junit.Assert; public class ByteArrayBuilderTest extends com.fasterxml.jackson.core.BaseTest { public void testSimple() throws Exception { ByteArrayBuilder b = new ByteArrayBuilder(null, 20); Assert.assertArrayEquals(new byte[0], b.toByteArray()); b.write((byte) 0); b.append(1); byte[] foo = new byte[98]; for (int i = 0; i < foo.length; ++i) { foo[i] = (byte) (2 + i); } b.write(foo); byte[] result = b.toByteArray(); assertEquals(100, result.length); for (int i = 0; i < 100; ++i) { assertEquals(i, (int) result[i]); } b.release(); b.close(); } } DefaultIndenterTest.java000066400000000000000000000021651356164247300340540ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; import org.junit.Test; import static org.junit.Assert.*; /** * Unit tests for class {@link DefaultIndenter}. * * @date 2017-07-31 * @see DefaultIndenter **/ public class DefaultIndenterTest { @Test public void testWithLinefeed() { DefaultIndenter defaultIndenter = new DefaultIndenter(); DefaultIndenter defaultIndenterTwo = defaultIndenter.withLinefeed("-XG'#x"); DefaultIndenter defaultIndenterThree = defaultIndenterTwo.withLinefeed("-XG'#x"); assertEquals("-XG'#x", defaultIndenterThree.getEol()); assertNotSame(defaultIndenterThree, defaultIndenter); assertSame(defaultIndenterThree, defaultIndenterTwo); } @Test public void testWithIndent() { DefaultIndenter defaultIndenter = new DefaultIndenter(); DefaultIndenter defaultIndenterTwo = defaultIndenter.withIndent("9Qh/6,~n"); DefaultIndenter defaultIndenterThree = defaultIndenterTwo.withIndent("9Qh/6,~n"); assertEquals("\n", defaultIndenterThree.getEol()); assertNotSame(defaultIndenterThree, defaultIndenter); assertSame(defaultIndenterThree, defaultIndenterTwo); } }RequestPayloadTest.java000066400000000000000000000021721356164247300337370ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; import org.junit.Test; import static org.junit.Assert.*; /** * Unit tests for class {@link RequestPayload}. * * @see RequestPayload **/ public class RequestPayloadTest { @SuppressWarnings("unused") @Test(expected = IllegalArgumentException.class) public void testFailsToCreateTakingCharSequenceThrowsIllegalArgumentExceptionOne() { new RequestPayload(null); } @SuppressWarnings("unused") @Test(expected = IllegalArgumentException.class) public void testFailsToCreateTakingCharSequenceThrowsIllegalArgumentExceptionTwo() { new RequestPayload(null, "UTF-8"); } @Test public void testCreateTakingCharSequenceAndCallsGetRawPayload() { CharSequence charSequence = new String(); RequestPayload requestPayload = new RequestPayload(charSequence); assertEquals("", requestPayload.getRawPayload()); } @Test public void testCreateTaking2ArgumentsAndCallsGetRawPayload() { byte[] byteArray = new byte[5]; RequestPayload requestPayload = new RequestPayload(byteArray, "/ _ \" €"); assertSame(byteArray, requestPayload.getRawPayload()); } }jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/util/SeparatorsTest.java000066400000000000000000000045211356164247300331770ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import org.junit.Test; import static org.junit.Assert.*; /** * Unit tests for class {@link Separators}. * * @date 2017-07-31 * @see Separators **/ public class SeparatorsTest { @Test public void testWithArrayValueSeparatorWithDigit() { Separators separators = new Separators('5', '5', '5'); Separators separatorsTwo = separators.withArrayValueSeparator('5'); assertEquals('5', separatorsTwo.getObjectEntrySeparator()); assertEquals('5', separatorsTwo.getObjectFieldValueSeparator()); assertEquals('5', separatorsTwo.getArrayValueSeparator()); assertSame(separatorsTwo, separators); separatorsTwo = separators.withArrayValueSeparator('6'); assertEquals('5', separatorsTwo.getObjectEntrySeparator()); assertEquals('5', separatorsTwo.getObjectFieldValueSeparator()); assertEquals('6', separatorsTwo.getArrayValueSeparator()); assertNotSame(separatorsTwo, separators); } @Test public void testWithObjectEntrySeparator() { Separators separators = new Separators('5', '5', '5'); Separators separatorsTwo = separators.withObjectEntrySeparator('!'); Separators separatorsThree = separatorsTwo.withObjectEntrySeparator('!'); assertEquals('!', separatorsThree.getObjectEntrySeparator()); assertEquals('5', separatorsThree.getObjectFieldValueSeparator()); assertSame(separatorsThree, separatorsTwo); assertEquals('5', separators.getArrayValueSeparator()); assertEquals('5', separatorsThree.getArrayValueSeparator()); assertEquals('5', separators.getObjectFieldValueSeparator()); } @Test public void testWithObjectFieldValueSeparatorWithDigit() { Separators separators = new Separators('5', '5', '5'); Separators separatorsTwo = separators.withObjectFieldValueSeparator('5'); assertEquals('5', separatorsTwo.getArrayValueSeparator()); assertSame(separatorsTwo, separators); assertEquals('5', separatorsTwo.getObjectEntrySeparator()); assertEquals('5', separatorsTwo.getObjectFieldValueSeparator()); separatorsTwo = separators.withObjectFieldValueSeparator('6'); assertEquals('5', separatorsTwo.getArrayValueSeparator()); assertNotSame(separatorsTwo, separators); assertEquals('5', separatorsTwo.getObjectEntrySeparator()); assertEquals('6', separatorsTwo.getObjectFieldValueSeparator()); } }jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/util/TestCharTypes.java000066400000000000000000000007321356164247300327560ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import com.fasterxml.jackson.core.io.CharTypes; public class TestCharTypes extends com.fasterxml.jackson.core.BaseTest { public void testQuoting() { StringBuilder sb = new StringBuilder(); CharTypes.appendQuoted(sb, "\n"); assertEquals("\\n", sb.toString()); sb = new StringBuilder(); CharTypes.appendQuoted(sb, "\u0000"); assertEquals("\\u0000", sb.toString()); } } TestDefaultPrettyPrinter.java000066400000000000000000000130541356164247300351360ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.StringWriter; import com.fasterxml.jackson.core.*; public class TestDefaultPrettyPrinter extends BaseTest { private final JsonFactory JSON_F = new JsonFactory(); public void testSystemLinefeed() throws IOException { PrettyPrinter pp = new DefaultPrettyPrinter(); String LF = System.getProperty("line.separator"); String EXP = "{" + LF + " \"name\" : \"John Doe\"," + LF + " \"age\" : 3.14" + LF + "}"; assertEquals(EXP, _printTestData(pp, false)); assertEquals(EXP, _printTestData(pp, true)); } public void testWithLineFeed() throws IOException { PrettyPrinter pp = new DefaultPrettyPrinter() .withObjectIndenter(new DefaultIndenter().withLinefeed("\n")); String EXP = "{\n" + " \"name\" : \"John Doe\",\n" + " \"age\" : 3.14\n" + "}"; assertEquals(EXP, _printTestData(pp, false)); assertEquals(EXP, _printTestData(pp, true)); } public void testWithIndent() throws IOException { PrettyPrinter pp = new DefaultPrettyPrinter() .withObjectIndenter(new DefaultIndenter().withLinefeed("\n").withIndent(" ")); String EXP = "{\n" + " \"name\" : \"John Doe\",\n" + " \"age\" : 3.14\n" + "}"; assertEquals(EXP, _printTestData(pp, false)); assertEquals(EXP, _printTestData(pp, true)); } public void testUnixLinefeed() throws IOException { PrettyPrinter pp = new DefaultPrettyPrinter() .withObjectIndenter(new DefaultIndenter(" ", "\n")); String EXP = "{\n" + " \"name\" : \"John Doe\",\n" + " \"age\" : 3.14\n" + "}"; assertEquals(EXP, _printTestData(pp, false)); assertEquals(EXP, _printTestData(pp, true)); } public void testWindowsLinefeed() throws IOException { PrettyPrinter pp = new DefaultPrettyPrinter() .withObjectIndenter(new DefaultIndenter(" ", "\r\n")); String EXP = "{\r\n" + " \"name\" : \"John Doe\",\r\n" + " \"age\" : 3.14\r\n" + "}"; assertEquals(EXP, _printTestData(pp, false)); assertEquals(EXP, _printTestData(pp, true)); } public void testTabIndent() throws IOException { PrettyPrinter pp = new DefaultPrettyPrinter() .withObjectIndenter(new DefaultIndenter("\t", "\n")); String EXP = "{\n" + "\t\"name\" : \"John Doe\",\n" + "\t\"age\" : 3.14\n" + "}"; assertEquals(EXP, _printTestData(pp, false)); assertEquals(EXP, _printTestData(pp, true)); } public void testRootSeparator() throws IOException { DefaultPrettyPrinter pp = new DefaultPrettyPrinter() .withRootSeparator("|"); final String EXP = "1|2|3"; StringWriter sw = new StringWriter(); JsonGenerator gen = JSON_F.createGenerator(sw); gen.setPrettyPrinter(pp); gen.writeNumber(1); gen.writeNumber(2); gen.writeNumber(3); gen.close(); assertEquals(EXP, sw.toString()); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); gen = JSON_F.createGenerator(bytes); gen.setPrettyPrinter(pp); gen.writeNumber(1); gen.writeNumber(2); gen.writeNumber(3); gen.close(); assertEquals(EXP, bytes.toString("UTF-8")); // Also: let's try removing separator altogether pp = pp.withRootSeparator((String) null) .withArrayIndenter(null) .withObjectIndenter(null) .withoutSpacesInObjectEntries(); sw = new StringWriter(); gen = JSON_F.createGenerator(sw); gen.setPrettyPrinter(pp); gen.writeNumber(1); gen.writeStartArray(); gen.writeNumber(2); gen.writeEndArray(); gen.writeStartObject(); gen.writeFieldName("a"); gen.writeNumber(3); gen.writeEndObject(); gen.close(); // no root separator, nor array, object assertEquals("1[2]{\"a\":3}", sw.toString()); } private String _printTestData(PrettyPrinter pp, boolean useBytes) throws IOException { JsonGenerator gen; StringWriter sw; ByteArrayOutputStream bytes; if (useBytes) { sw = null; bytes = new ByteArrayOutputStream(); gen = JSON_F.createGenerator(bytes); } else { sw = new StringWriter(); bytes = null; gen = JSON_F.createGenerator(sw); } gen.setPrettyPrinter(pp); gen.writeStartObject(); gen.writeFieldName("name"); gen.writeString("John Doe"); gen.writeFieldName("age"); gen.writeNumber(3.14); gen.writeEndObject(); gen.close(); if (useBytes) { return bytes.toString("UTF-8"); } return sw.toString(); } // [core#502]: Force sub-classes to reimplement `createInstance` public void testInvalidSubClass() throws Exception { DefaultPrettyPrinter pp = new MyPrettyPrinter(); try { pp.createInstance(); fail("Should not pass"); } catch (IllegalStateException e) { verifyException(e, "does not override"); } } @SuppressWarnings("serial") static class MyPrettyPrinter extends DefaultPrettyPrinter { } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/util/TestDelegates.java000066400000000000000000000202311356164247300327450ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import java.io.*; import java.util.Iterator; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.JsonParser.NumberType; import com.fasterxml.jackson.core.type.ResolvedType; import com.fasterxml.jackson.core.type.TypeReference; public class TestDelegates extends com.fasterxml.jackson.core.BaseTest { static class POJO { public int x = 3; } static class BogusCodec extends ObjectCodec { public Object pojoWritten; public TreeNode treeWritten; @Override public Version version() { return Version.unknownVersion(); } @Override public T readValue(JsonParser p, Class valueType) { return null; } @Override public T readValue(JsonParser p, TypeReference valueTypeRef) { return null; } @Override public T readValue(JsonParser p, ResolvedType valueType) { return null; } @Override public Iterator readValues(JsonParser p, Class valueType) { return null; } @Override public Iterator readValues(JsonParser p, TypeReference valueTypeRef) throws IOException { return null; } @Override public Iterator readValues(JsonParser p, ResolvedType valueType) { return null; } @Override public void writeValue(JsonGenerator gen, Object value) throws IOException { gen.writeString("pojo"); pojoWritten = value; } @Override public T readTree(JsonParser p) { return null; } @Override public void writeTree(JsonGenerator gen, TreeNode tree) throws IOException { gen.writeString("tree"); treeWritten = tree; } @Override public TreeNode createObjectNode() { return null; } @Override public TreeNode createArrayNode() { return null; } @Override public TreeNode missingNode() { return null; } @Override public TreeNode nullNode() { return null; } @Override public JsonParser treeAsTokens(TreeNode n) { return null; } @Override public T treeToValue(TreeNode n, Class valueType) { return null; } } static class BogusTree implements TreeNode { @Override public JsonToken asToken() { return null; } @Override public NumberType numberType() { return null; } @Override public int size() { return 0; } @Override public boolean isValueNode() { return false; } @Override public boolean isContainerNode() { return false; } @Override public boolean isMissingNode() { return false; } @Override public boolean isArray() { return false; } @Override public boolean isObject() { return false; } @Override public TreeNode get(String fieldName) { return null; } @Override public TreeNode get(int index) { return null; } @Override public TreeNode path(String fieldName) { return null; } @Override public TreeNode path(int index) { return null; } @Override public Iterator fieldNames() { return null; } @Override public TreeNode at(JsonPointer ptr) { return null; } @Override public TreeNode at(String jsonPointerExpression) { return null; } @Override public JsonParser traverse() { return null; } @Override public JsonParser traverse(ObjectCodec codec) { return null; } } private final JsonFactory JSON_F = new JsonFactory(); /** * Test default, non-overridden parser delegate. */ public void testParserDelegate() throws IOException { final String TOKEN ="foo"; JsonParser parser = JSON_F.createParser("[ 1, true, null, { } ]"); JsonParserDelegate del = new JsonParserDelegate(parser); assertNull(del.currentToken()); assertToken(JsonToken.START_ARRAY, del.nextToken()); assertEquals("[", del.getText()); assertToken(JsonToken.VALUE_NUMBER_INT, del.nextToken()); assertEquals(1, del.getIntValue()); assertToken(JsonToken.VALUE_TRUE, del.nextToken()); assertTrue(del.getBooleanValue()); assertToken(JsonToken.VALUE_NULL, del.nextToken()); assertNull(del.getCurrentValue()); del.setCurrentValue(TOKEN); assertToken(JsonToken.START_OBJECT, del.nextToken()); assertNull(del.getCurrentValue()); assertToken(JsonToken.END_OBJECT, del.nextToken()); assertEquals(TOKEN, del.getCurrentValue()); assertToken(JsonToken.END_ARRAY, del.nextToken()); del.close(); assertTrue(del.isClosed()); assertTrue(parser.isClosed()); parser.close(); } /** * Test default, non-overridden generator delegate. */ public void testGeneratorDelegate() throws IOException { final String TOKEN ="foo"; StringWriter sw = new StringWriter(); JsonGenerator g0 = JSON_F.createGenerator(sw); JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0); del.writeStartArray(); assertEquals(1, del.getOutputBuffered()); del.writeNumber(13); del.writeNull(); del.writeBoolean(false); del.writeString("foo"); // verify that we can actually set/get "current value" as expected, even with delegates assertNull(del.getCurrentValue()); del.setCurrentValue(TOKEN); del.writeStartObject(); assertNull(del.getCurrentValue()); del.writeEndObject(); assertEquals(TOKEN, del.getCurrentValue()); del.writeStartArray(0); del.writeEndArray(); del.writeEndArray(); del.flush(); del.close(); assertTrue(del.isClosed()); assertTrue(g0.isClosed()); assertEquals("[13,null,false,\"foo\",{},[]]", sw.toString()); g0.close(); } public void testNotDelegateCopyMethods() throws IOException { JsonParser jp = JSON_F.createParser("[{\"a\":[1,2,{\"b\":3}],\"c\":\"d\"},{\"e\":false},null]"); StringWriter sw = new StringWriter(); JsonGenerator jg = new JsonGeneratorDelegate(JSON_F.createGenerator(sw), false) { @Override public void writeFieldName(String name) throws IOException { super.writeFieldName(name+"-test"); super.writeBoolean(true); super.writeFieldName(name); } }; jp.nextToken(); jg.copyCurrentStructure(jp); jg.flush(); assertEquals("[{\"a-test\":true,\"a\":[1,2,{\"b-test\":true,\"b\":3}],\"c-test\":true,\"c\":\"d\"},{\"e-test\":true,\"e\":false},null]", sw.toString()); jp.close(); jg.close(); } @SuppressWarnings("resource") public void testGeneratorWithCodec() throws IOException { BogusCodec codec = new BogusCodec(); StringWriter sw = new StringWriter(); JsonGenerator g0 = JSON_F.createGenerator(sw); g0.setCodec(codec); JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0, false); del.writeStartArray(); POJO pojo = new POJO(); del.writeObject(pojo); TreeNode tree = new BogusTree(); del.writeTree(tree); del.writeEndArray(); del.close(); assertEquals("[\"pojo\",\"tree\"]", sw.toString()); assertSame(tree, codec.treeWritten); assertSame(pojo, codec.pojoWritten); } } TestNumberPrinting.java000066400000000000000000000066001356164247300337400ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; import java.util.Random; import com.fasterxml.jackson.core.io.NumberOutput; /** * Set of basic unit tests for verifying that the low-level number * printing methods work as expected. */ public class TestNumberPrinting extends com.fasterxml.jackson.core.BaseTest { public void testIntPrinting() throws Exception { assertIntPrint(0); assertIntPrint(-3); assertIntPrint(1234); assertIntPrint(-1234); assertIntPrint(56789); assertIntPrint(-56789); assertIntPrint(999999); assertIntPrint(-999999); assertIntPrint(1000000); assertIntPrint(-1000000); assertIntPrint(10000001); assertIntPrint(-10000001); assertIntPrint(-100000012); assertIntPrint(100000012); assertIntPrint(1999888777); assertIntPrint(-1999888777); assertIntPrint(Integer.MAX_VALUE); assertIntPrint(Integer.MIN_VALUE); Random rnd = new Random(12345L); for (int i = 0; i < 251000; ++i) { assertIntPrint(rnd.nextInt()); } } public void testLongPrinting() throws Exception { // First, let's just cover couple of edge cases assertLongPrint(0L, 0); assertLongPrint(1L, 0); assertLongPrint(-1L, 0); assertLongPrint(Long.MAX_VALUE, 0); assertLongPrint(Long.MIN_VALUE, 0); assertLongPrint(Long.MAX_VALUE-1L, 0); assertLongPrint(Long.MIN_VALUE+1L, 0); Random rnd = new Random(12345L); // Bigger value space, need more iterations for long for (int i = 0; i < 678000; ++i) { long l = ((long) rnd.nextInt() << 32) | (long) rnd.nextInt(); assertLongPrint(l, i); } } /* /********************************************************** /* Internal methods /********************************************************** */ private void assertIntPrint(int value) { String exp = ""+value; String act = printToString(value); 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) { String exp = ""+value; String act = printToString(value); 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) { char[] buffer = new char[12]; int offset = NumberOutput.outputInt(value, buffer, 0); return new String(buffer, 0, offset); } private String printToString(long value) { char[] buffer = new char[22]; int offset = NumberOutput.outputLong(value, buffer, 0); return new String(buffer, 0, offset); } } TestSerializedString.java000066400000000000000000000043071356164247300342610ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; import java.io.*; import java.nio.ByteBuffer; import java.util.Arrays; import com.fasterxml.jackson.core.SerializableString; import com.fasterxml.jackson.core.io.SerializedString; /** * Simple unit tests to try to verify that the default * {@link SerializableString} implementation works as expected. */ public class TestSerializedString extends com.fasterxml.jackson.core.BaseTest { public void testAppending() throws IOException { final String INPUT = "\"quo\\ted\""; final String QUOTED = "\\\"quo\\\\ted\\\""; SerializableString sstr = new SerializedString(INPUT); // sanity checks first: assertEquals(sstr.getValue(), INPUT); assertEquals(QUOTED, new String(sstr.asQuotedChars())); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); assertEquals(QUOTED.length(), sstr.writeQuotedUTF8(bytes)); assertEquals(QUOTED, bytes.toString("UTF-8")); bytes.reset(); assertEquals(INPUT.length(), sstr.writeUnquotedUTF8(bytes)); assertEquals(INPUT, bytes.toString("UTF-8")); byte[] buffer = new byte[100]; assertEquals(QUOTED.length(), sstr.appendQuotedUTF8(buffer, 3)); assertEquals(QUOTED, new String(buffer, 3, QUOTED.length())); Arrays.fill(buffer, (byte) 0); assertEquals(INPUT.length(), sstr.appendUnquotedUTF8(buffer, 5)); assertEquals(INPUT, new String(buffer, 5, INPUT.length())); } public void testFailedAccess() throws IOException { final String INPUT = "Bit longer text"; SerializableString sstr = new SerializedString(INPUT); final byte[] buffer = new byte[INPUT.length() - 2]; final char[] ch = new char[INPUT.length() - 2]; final ByteBuffer bbuf = ByteBuffer.allocate(INPUT.length() - 2); assertEquals(-1, sstr.appendQuotedUTF8(buffer, 0)); assertEquals(-1, sstr.appendQuoted(ch, 0)); assertEquals(-1, sstr.putQuotedUTF8(bbuf)); bbuf.rewind(); assertEquals(-1, sstr.appendUnquotedUTF8(buffer, 0)); assertEquals(-1, sstr.appendUnquoted(ch, 0)); assertEquals(-1, sstr.putUnquotedUTF8(bbuf)); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/util/TestTextBuffer.java000066400000000000000000000151311356164247300331310ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import com.fasterxml.jackson.core.io.NumberInput; public class TestTextBuffer extends com.fasterxml.jackson.core.BaseTest { /** * Trivially simple basic test to ensure all basic append * methods work */ public void testSimple() { TextBuffer tb = new TextBuffer(new BufferRecycler()); tb.append('a'); tb.append(new char[] { 'X', 'b' }, 1, 1); tb.append("c", 0, 1); // all fits within one buffer so it is efficient... assertTrue(tb.hasTextAsCharacters()); assertEquals(3, tb.contentsAsArray().length); assertEquals("abc", tb.toString()); assertNotNull(tb.expandCurrentSegment()); } public void testLonger() { TextBuffer tb = new TextBuffer(null); for (int i = 0; i < 2000; ++i) { tb.append("abc", 0, 3); } String str = tb.contentsAsString(); assertEquals(6000, str.length()); assertEquals(6000, tb.contentsAsArray().length); tb.resetWithShared(new char[] { 'a' }, 0, 1); assertEquals(1, tb.toString().length()); assertTrue(tb.hasTextAsCharacters()); } public void testLongAppend() { final int len = TextBuffer.MAX_SEGMENT_LEN * 3 / 2; StringBuilder sb = new StringBuilder(len); for (int i = 0; i < len; ++i) { sb.append('x'); } final String STR = sb.toString(); final String EXP = "a" + STR + "c"; // ok: first test with String: TextBuffer tb = new TextBuffer(new BufferRecycler()); tb.append('a'); tb.append(STR, 0, len); tb.append('c'); assertEquals(len+2, tb.size()); assertEquals(EXP, tb.contentsAsString()); // then char[] tb = new TextBuffer(new BufferRecycler()); tb.append('a'); tb.append(STR.toCharArray(), 0, len); tb.append('c'); assertEquals(len+2, tb.size()); assertEquals(EXP, tb.contentsAsString()); } // [core#152] public void testExpand() { TextBuffer tb = new TextBuffer(new BufferRecycler()); char[] buf = tb.getCurrentSegment(); while (buf.length < 500 * 1000) { char[] old = buf; buf = tb.expandCurrentSegment(); if (old.length >= buf.length) { fail("Expected buffer of "+old.length+" to expand, did not, length now "+buf.length); } } tb.resetWithString("Foobar"); assertEquals("Foobar", tb.contentsAsString()); } // [core#182] public void testEmpty() { TextBuffer tb = new TextBuffer(new BufferRecycler()); tb.resetWithEmpty(); assertTrue(tb.getTextBuffer().length == 0); tb.contentsAsString(); assertTrue(tb.getTextBuffer().length == 0); } public void testResetWithAndSetCurrentAndReturn() { TextBuffer textBuffer = new TextBuffer(null); textBuffer.resetWith('l'); textBuffer.setCurrentAndReturn(349); } public void testGetCurrentSegment() { TextBuffer textBuffer = new TextBuffer(null); textBuffer.emptyAndGetCurrentSegment(); // 26-Aug-2019, tatu: Value depends on "minimum segment size": textBuffer.setCurrentAndReturn(500); textBuffer.getCurrentSegment(); assertEquals(500, textBuffer.size()); } public void testAppendTakingTwoAndThreeInts() { BufferRecycler bufferRecycler = new BufferRecycler(); TextBuffer textBuffer = new TextBuffer(bufferRecycler); textBuffer.ensureNotShared(); char[] charArray = textBuffer.getTextBuffer(); textBuffer.append(charArray, 0, 200); textBuffer.append("5rmk0rx(C@aVYGN@Q", 2, 3); assertEquals(3, textBuffer.getCurrentSegmentSize()); } public void testEnsureNotSharedAndResetWithString() { BufferRecycler bufferRecycler = new BufferRecycler(); TextBuffer textBuffer = new TextBuffer(bufferRecycler); textBuffer.resetWithString(""); assertFalse(textBuffer.hasTextAsCharacters()); textBuffer.ensureNotShared(); assertEquals(0, textBuffer.getCurrentSegmentSize()); } public void testContentsAsDecimalThrowsNumberFormatException() { TextBuffer textBuffer = new TextBuffer( null); try { textBuffer.contentsAsDecimal(); fail("Expecting exception: NumberFormatException"); } catch(NumberFormatException e) { assertEquals(NumberInput.class.getName(), e.getStackTrace()[0].getClassName()); } } public void testGetTextBufferAndEmptyAndGetCurrentSegmentAndFinishCurrentSegment() { BufferRecycler bufferRecycler = new BufferRecycler(); TextBuffer textBuffer = new TextBuffer(bufferRecycler); textBuffer.emptyAndGetCurrentSegment(); textBuffer.finishCurrentSegment(); textBuffer.getTextBuffer(); assertEquals(200, textBuffer.size()); } public void testGetTextBufferAndAppendTakingCharAndContentsAsArray() { BufferRecycler bufferRecycler = new BufferRecycler(); TextBuffer textBuffer = new TextBuffer(bufferRecycler); textBuffer.append('('); textBuffer.contentsAsArray(); textBuffer.getTextBuffer(); assertEquals(1, textBuffer.getCurrentSegmentSize()); } public void testGetTextBufferAndResetWithString() { BufferRecycler bufferRecycler = new BufferRecycler(); TextBuffer textBuffer = new TextBuffer(bufferRecycler); textBuffer.resetWithString(""); assertFalse(textBuffer.hasTextAsCharacters()); textBuffer.getTextBuffer(); assertTrue(textBuffer.hasTextAsCharacters()); } public void testResetWithString() { BufferRecycler bufferRecycler = new BufferRecycler(); TextBuffer textBuffer = new TextBuffer(bufferRecycler); textBuffer.ensureNotShared(); textBuffer.finishCurrentSegment(); assertEquals(200, textBuffer.size()); textBuffer.resetWithString("asdf"); assertEquals(0, textBuffer.getTextOffset()); } public void testGetCurrentSegmentSizeResetWith() { TextBuffer textBuffer = new TextBuffer(null); textBuffer.resetWith('.'); textBuffer.resetWith('q'); assertEquals(1, textBuffer.getCurrentSegmentSize()); } public void testGetSizeFinishCurrentSegmentAndResetWith() { TextBuffer textBuffer = new TextBuffer(null); textBuffer.resetWith('.'); textBuffer.finishCurrentSegment(); textBuffer.resetWith('q'); assertEquals(2, textBuffer.size()); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/util/TestVersionUtil.java000066400000000000000000000026361356164247300333440ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.core.json.PackageVersion; import com.fasterxml.jackson.core.json.UTF8JsonGenerator; public class TestVersionUtil extends com.fasterxml.jackson.core.BaseTest { public void testVersionPartParsing() { assertEquals(13, VersionUtil.parseVersionPart("13")); assertEquals(27, VersionUtil.parseVersionPart("27.8")); assertEquals(0, VersionUtil.parseVersionPart("-3")); } public void testVersionParsing() { assertEquals(new Version(1, 2, 15, "foo", "group", "artifact"), VersionUtil.parseVersion("1.2.15-foo", "group", "artifact")); } @SuppressWarnings("deprecation") public void testMavenVersionParsing() { assertEquals(new Version(1, 2, 3, "SNAPSHOT", "foo.bar", "foo-bar"), VersionUtil.mavenVersionFor(TestVersionUtil.class.getClassLoader(), "foo.bar", "foo-bar")); } public void testPackageVersionMatches() { assertEquals(PackageVersion.VERSION, VersionUtil.versionFor(UTF8JsonGenerator.class)); } // [core#248]: make sure not to return `null` but `Version.unknownVersion()` public void testVersionForUnknownVersion() { // expecting return version.unknownVersion() instead of null assertEquals(Version.unknownVersion(), VersionUtil.versionFor(TestVersionUtil.class)); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/core/util/VersionUtilTest.java000066400000000000000000000022721356164247300333400ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import com.fasterxml.jackson.core.Version; import org.junit.Test; import static org.junit.Assert.*; /** * Unit tests for class {@link VersionUtil}. * * @date 2017-07-31 * @see VersionUtil **/ public class VersionUtilTest { @Test public void testParseVersionPartReturningPositive() { assertEquals(66, VersionUtil.parseVersionPart("66R")); } @Test public void testParseVersionReturningVersionWhereGetMajorVersionIsZero() { Version version = VersionUtil.parseVersion("#M&+m@569P", "#M&+m@569P", "com.fasterxml.jackson.core.util.VersionUtil"); assertEquals(0, version.getMinorVersion()); assertEquals(0, version.getPatchLevel()); assertEquals(0, version.getMajorVersion()); assertFalse(version.isSnapshot()); assertFalse(version.isUnknownVersion()); } @Test public void testParseVersionWithEmptyStringAndEmptyString() { Version version = VersionUtil.parseVersion("", "", "\"g2AT"); assertTrue(version.isUnknownVersion()); } @Test public void testParseVersionWithNullAndEmptyString() { Version version = VersionUtil.parseVersion(null, "/nUmRN)3", ""); assertFalse(version.isSnapshot()); } }jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/failing/000077500000000000000000000000001356164247300270535ustar00rootroot00000000000000LocationOffsets455Test.java000066400000000000000000000032541356164247300340430ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/failingpackage com.fasterxml.jackson.failing; import com.fasterxml.jackson.core.JsonLocation; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; public class LocationOffsets455Test extends com.fasterxml.jackson.core.BaseTest { // for [jackson-core#455] public void testEOFLocationViaReader() throws Exception { JsonParser p = createParserUsingReader("42"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(42, p.getIntValue()); JsonLocation loc = p.getCurrentLocation(); assertEquals(1, loc.getLineNr()); assertEquals(3, loc.getColumnNr()); assertEquals(2, loc.getByteOffset()); assertNull(p.nextToken()); loc = p.getCurrentLocation(); System.err.println("LOC/r = "+loc); assertEquals(1, loc.getLineNr()); assertEquals(2, loc.getByteOffset()); assertEquals(3, loc.getColumnNr()); p.close(); } // for [jackson-core#455] public void testEOFLocationViaStream() throws Exception { JsonParser p = createParserUsingStream("42", "UTF-8"); assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); assertEquals(42, p.getIntValue()); JsonLocation loc = p.getCurrentLocation(); assertEquals(1, loc.getLineNr()); assertEquals(3, loc.getColumnNr()); assertEquals(2, loc.getByteOffset()); assertNull(p.nextToken()); loc = p.getCurrentLocation(); System.err.println("LOC/str = "+loc); assertEquals(1, loc.getLineNr()); assertEquals(2, loc.getCharOffset()); assertEquals(3, loc.getColumnNr()); p.close(); } } ParserErrorHandlingTest.java000066400000000000000000000043171356164247300344170ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/failingpackage com.fasterxml.jackson.failing; import com.fasterxml.jackson.core.*; // Failing tests for non-root-token problem public class ParserErrorHandlingTest extends com.fasterxml.jackson.core.BaseTest { // Tests for [core#105] ("eager number parsing misses errors") public void testMangledIntsBytes() throws Exception { _testMangledNonRootInts(MODE_INPUT_STREAM); _testMangledNonRootInts(MODE_INPUT_STREAM_THROTTLED); // 02-Jun-2017, tatu: Fails to fail; should check whether this is expected // (since DataInput can't do look-ahead) // _testMangledNonRootInts(MODE_DATA_INPUT); } public void testMangledFloatsBytes() throws Exception { _testMangledNonRootFloats(MODE_INPUT_STREAM); _testMangledNonRootFloats(MODE_INPUT_STREAM_THROTTLED); // 02-Jun-2017, tatu: Fails as expected, unlike int one. Bit puzzling... _testMangledNonRootFloats(MODE_DATA_INPUT); } public void testMangledIntsChars() throws Exception { _testMangledNonRootInts(MODE_READER); } public void testMangledFloatsChars() throws Exception { _testMangledNonRootFloats(MODE_READER); } /* /********************************************************** /* Helper methods /********************************************************** */ private void _testMangledNonRootInts(int mode) throws Exception { JsonParser p = createParser(mode, "[ 123true ]"); assertToken(JsonToken.START_ARRAY, p.nextToken()); try { JsonToken t = p.nextToken(); fail("Should have gotten an exception; instead got token: "+t); } catch (JsonParseException e) { verifyException(e, "expected space"); } p.close(); } private void _testMangledNonRootFloats(int mode) throws Exception { JsonParser p = createParser(mode, "[ 1.5false ]"); assertToken(JsonToken.START_ARRAY, p.nextToken()); try { JsonToken t = p.nextToken(); fail("Should have gotten an exception; instead got token: "+t); } catch (JsonParseException e) { verifyException(e, "expected space"); } p.close(); } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/failing/Surrogate223Test.java000066400000000000000000000063161356164247300327660ustar00rootroot00000000000000package com.fasterxml.jackson.failing; import java.io.ByteArrayOutputStream; import java.io.StringWriter; import java.io.Writer; 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; public class Surrogate223Test extends BaseTest { private final JsonFactory JSON_F = new JsonFactory(); // for [core#223] public void testSurrogatesByteBacked() throws Exception { ByteArrayOutputStream out; JsonGenerator g; final String toQuote = new String(Character.toChars(0x1F602)); assertEquals(2, toQuote.length()); // just sanity check // default should be disabled: // assertFalse(JSON_F.isEnabled(JsonGenerator.Feature.ESCAPE_UTF8_SURROGATES)); out = new ByteArrayOutputStream(); g = JSON_F.createGenerator(out); g.writeStartArray(); g.writeString(toQuote); g.writeEndArray(); g.close(); assertEquals(2 + 2 + 4, out.size()); // brackets, quotes, 4-byte encoding // Also parse back to ensure correctness JsonParser p = JSON_F.createParser(out.toByteArray()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); // but may revert back to original behavior out = new ByteArrayOutputStream(); g = JSON_F.createGenerator(out); // g.enable(JsonGenerator.Feature.ESCAPE_UTF8_SURROGATES); g.writeStartArray(); g.writeString(toQuote); g.writeEndArray(); g.close(); assertEquals(2 + 2 + 12, out.size()); // brackets, quotes, 2 x 6 byte JSON escape } // for [core#223] public void testSurrogatesCharBacked() throws Exception { Writer out; JsonGenerator g; final String toQuote = new String(Character.toChars(0x1F602)); assertEquals(2, toQuote.length()); // just sanity check // default should be disabled: // assertFalse(JSON_F.isEnabled(JsonGenerator.Feature.ESCAPE_UTF8_SURROGATES)); out = new StringWriter(); g = JSON_F.createGenerator(out); g.writeStartArray(); g.writeString(toQuote); g.writeEndArray(); g.close(); assertEquals(2 + 2 + 2, out.toString().length()); // brackets, quotes, 2 chars as is // Also parse back to ensure correctness JsonParser p = JSON_F.createParser(out.toString()); assertToken(JsonToken.START_ARRAY, p.nextToken()); assertToken(JsonToken.VALUE_STRING, p.nextToken()); assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); // but may revert back to original behavior out = new StringWriter(); g = JSON_F.createGenerator(out); // g.enable(JsonGenerator.Feature.ESCAPE_UTF8_SURROGATES); g.writeStartArray(); g.writeString(toQuote); g.writeEndArray(); g.close(); assertEquals(2 + 2 + 12, out.toString().length()); // brackets, quotes, 2 x 6 byte JSON escape } } jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/failing/async/000077500000000000000000000000001356164247300301705ustar00rootroot00000000000000AsyncTokenErrorTest.java000066400000000000000000000076671356164247300347240ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/com/fasterxml/jackson/failing/asyncpackage com.fasterxml.jackson.failing.async; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.testsupport.AsyncReaderWrapper; public class AsyncTokenErrorTest extends AsyncTestBase { private final JsonFactory JSON_F = new JsonFactory(); public void testInvalidKeywordsStartOk() throws Exception { _doTestInvalidKeyword("nul"); _doTestInvalidKeyword("nulla"); _doTestInvalidKeyword("fal"); _doTestInvalidKeyword("fals0"); _doTestInvalidKeyword("falsett0"); _doTestInvalidKeyword("tr"); _doTestInvalidKeyword("truE"); _doTestInvalidKeyword("treu"); _doTestInvalidKeyword("trueenough"); } public void testInvalidKeywordsStartFail() throws Exception { _doTestInvalidKeyword("Null"); _doTestInvalidKeyword("False"); _doTestInvalidKeyword("C"); } private void _doTestInvalidKeyword(String value) throws IOException { String doc = "{ \"key1\" : "+value+" }"; AsyncReaderWrapper p = _createParser(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(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(); } } public void testMangledRootInts() throws Exception { AsyncReaderWrapper p = _createParser("123true"); try { JsonToken t = p.nextToken(); fail("Should have gotten an exception; instead got token: "+t+"; number: "+p.getNumberValue()); } catch (JsonParseException e) { verifyException(e, "expected space"); } p.close(); } public void testMangledRootFloats() throws Exception { // Also test with floats AsyncReaderWrapper p = _createParser("1.5false"); try { JsonToken t = p.nextToken(); fail("Should have gotten an exception; instead got token: "+t+"; number: "+p.getNumberValue()); } catch (JsonParseException e) { verifyException(e, "expected space"); } p.close(); } public void testMangledNonRootInts() throws Exception { AsyncReaderWrapper p = _createParser("[ 123true ]"); assertToken(JsonToken.START_ARRAY, p.nextToken()); try { JsonToken t = p.nextToken(); fail("Should have gotten an exception; instead got token: "+t); } catch (JsonParseException e) { verifyException(e, "expected space"); } p.close(); } public void testMangledNonRootFloats() throws Exception { AsyncReaderWrapper p = _createParser("[ 1.5false ]"); assertToken(JsonToken.START_ARRAY, p.nextToken()); try { JsonToken t = p.nextToken(); fail("Should have gotten an exception; instead got token: "+t); } catch (JsonParseException e) { verifyException(e, "expected space"); } p.close(); } private AsyncReaderWrapper _createParser(String doc) throws IOException { return asyncForBytes(JSON_F, 1, _jsonDoc(doc), 1); } } jackson-core-jackson-core-2.10.1/src/test/java/perf/000077500000000000000000000000001356164247300221635ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/java/perf/ConcurrencyReadTest.java000066400000000000000000000042141356164247300267550ustar00rootroot00000000000000package perf; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; import com.fasterxml.jackson.core.*; /** * Manual performance test to try out various synchronization * methods for symbol tables. */ public class ConcurrencyReadTest { private final static int THREADS = 50; private void test() throws Exception { final JsonFactory jf = new JsonFactory(); final byte[] INPUT = "{\"a\":1}".getBytes("UTF-8"); final AtomicInteger count = new AtomicInteger(); for (int i = 0; i < THREADS; ++i) { new Thread(new Runnable() { @Override public void run() { try { while (true) { parse(jf, INPUT); count.addAndGet(1); } } catch (IOException e) { System.err.println("PROBLEM: "+e); } } }).start(); } // wait slightly.... Thread.sleep(200L); double totalTime = 0.0; double totalCount = 0.0; while (true) { long start = System.currentTimeMillis(); int startCount = count.get(); Thread.sleep(1000L); int done = count.get() - startCount; long time = System.currentTimeMillis() - start; totalTime += time; totalCount += done; double rate = (double) done / (double) time; System.out.printf("Rate: %.1f (avg: %.1f)\n", rate, totalCount/totalTime); } } protected void parse(JsonFactory jf, byte[] input) throws IOException { JsonParser jp = jf.createParser(input, 0, input.length); while (jp.nextToken() != null) { ; } jp.close(); } public static void main(String[] args) throws Exception { if (args.length != 0) { System.err.println("Usage: java ..."); System.exit(1); } new ConcurrencyReadTest().test(); } } jackson-core-jackson-core-2.10.1/src/test/java/perf/EnumByBytesLookup.java000066400000000000000000000116421356164247300264320ustar00rootroot00000000000000package perf; import java.nio.charset.Charset; import java.util.Arrays; /** * Trie container/wrapper, in this case implements Enum-value lookup. * Sample code to possibly use for streamlined-lookup by dictionary, using * UTF-8 bytes of {@link Enum#name()} as the key. */ public class EnumByBytesLookup> { private final static Charset UTF8 = Charset.forName("UTF-8"); private final Trie _root; private final int _size; private EnumByBytesLookup(Trie root, int size) { _root = root; _size = size; } public static > EnumByBytesLookup buildFor(Class enumClass) { Trie root = new Trie(null); int size = 0; for (EIN en : enumClass.getEnumConstants()) { byte[] key = en.name().getBytes(UTF8); root = root.with(en, key); ++size; } return new EnumByBytesLookup(root, size); } public E find(byte[] rawId) { return _root.find(rawId); } public int size() { return _size; } } /** * Trie nodes */ class Trie { private final static byte[] NO_BYTES = new byte[0]; private final static Trie[] NO_NODES = new Trie[0]; /** * For leaves, value matched by sequence */ private final T _match; private final byte[] _nextBytes; private final Trie[] nextNodes; private final int nextCount; @SuppressWarnings("unchecked") Trie(T match) { this(match, NO_BYTES, (Trie[]) NO_NODES); } private Trie(T match, byte[] nextBytes, Trie[] nextNodes) { this._match = match; this._nextBytes = nextBytes; this.nextNodes = nextNodes; nextCount = nextBytes.length; } private Trie(Trie base, T match) { // should we allow duplicate calls with same match? For now, let's not if (base._match != null) { throw new IllegalArgumentException("Trying to add same match multiple times"); } this._match = match; _nextBytes = base._nextBytes; nextNodes = base.nextNodes; nextCount = base.nextCount; } private Trie(Trie base, byte nextByte, Trie nextNode) { // should we allow duplicate calls with same match? For now, let's not if (base._match != null) { throw new IllegalArgumentException("Trying to add same match multiple times"); } _match = base._match; int size = base._nextBytes.length + 1; _nextBytes = Arrays.copyOf(base._nextBytes, size); _nextBytes[size-1] = nextByte; nextNodes = Arrays.copyOf(base.nextNodes, size); nextNodes[size-1] = nextNode; nextCount = size; } /** * Constructor used when an existing branch needs to be replaced due to addition */ private Trie(Trie base, int offset, Trie newNode) { _match = base._match; // can keep nextBytes, as they don't change _nextBytes = base._nextBytes; // but must create a copy of next nodes, to modify one entry nextNodes = Arrays.copyOf(base.nextNodes, base.nextNodes.length); nextNodes[offset] = newNode; nextCount = base.nextCount; } /** * "Mutant factory" method: constructs a modified Trie, with specified raw id * added. */ public Trie with(T match, byte[] rawId) { return with(match, rawId, 0, rawId.length); } private Trie with(T match, byte[] rawId, int start, int end) { if (start == end) { return new Trie(this, match); } // Ok: two choices; either we follow existing branch; or need to create new one final byte b = rawId[start++]; for (int i = 0; i < nextCount; ++i) { if (_nextBytes[i] == b) { // existing branch: good day for delegation... Trie old = nextNodes[i]; // to keep things truly immutable, copy underlying arrays, then return new Trie(this, i, old.with(match, rawId, start, end)); } } // simplest recursively, but for fun let's convert to iteration. Start with tail Trie curr = new Trie(match); for (int i = end-1; i >= start; --i) { curr = new Trie(this, rawId[i], curr); } return new Trie(this, b, curr); } public T find(byte[] id) { return find(id, 0, id.length); } public T find(byte[] id, int offset, int length) { Trie t = this; final int end = offset+length; for (; offset < end; ++offset) { byte b = id[offset]; t = t.next(b); if (t == null) { // NOTE: if using null-padding, would trim here /* if (b == (byte) 0) { break; } */ return null; } } return t._match; } private Trie next(int b) { for (int i = 0; i < nextCount; ++i) { if (_nextBytes[i] == b) { return nextNodes[i]; } } return null; } } jackson-core-jackson-core-2.10.1/src/test/java/perf/ManualCharAccessTest.java000066400000000000000000000141361356164247300270300ustar00rootroot00000000000000package perf; public class ManualCharAccessTest { protected int hash; protected final static byte[] SMALL_BYTE_CODES = new byte[256]; protected final static int[] SMALL_INT_CODES = new int[256]; protected final static int[] INT_CODES = new int[0x10000]; protected final static byte[] BYTE_CODES = new byte[0x10000]; static { for (int i = 0; i < 32; ++i) { if (!(i == '\r' || i == '\n' || i == '\t')) { INT_CODES[i] = 1; BYTE_CODES[i] = 1; SMALL_BYTE_CODES[i] = 1; SMALL_INT_CODES[i] = 1; } } INT_CODES['\\'] = 2; BYTE_CODES['\\'] = 2; SMALL_BYTE_CODES['\\'] = 2; SMALL_INT_CODES['\\'] = 2; } protected String generateString(int len) { int counter = 0; StringBuilder sb = new StringBuilder(len + 20); do { sb.append("Some stuff: ").append(len).append("\n"); if ((++counter % 31) == 0) { sb.append("\\"); } } while (sb.length() < len); return sb.toString(); } private void test() throws Exception { final String INPUT_STR = generateString(23000); final char[] INPUT_CHARS = INPUT_STR.toCharArray(); final char[] OUTPUT = new char[INPUT_CHARS.length]; // Let's try to guestimate suitable size, N megs of output final int REPS = (int) ((double) (80 * 1000 * 1000) / (double) INPUT_CHARS.length); System.out.printf("%d bytes to scan, will do %d repetitions\n", INPUT_CHARS.length, REPS); int i = 0; int roundsDone = 0; final int TYPES = 3; final int WARMUP_ROUNDS = 5; final long[] times = new long[TYPES]; while (true) { int round = (i++ % TYPES); String msg; boolean lf = (round == 0); long msecs; switch (round) { case 0: msg = "Read classic"; msecs = readClassic(REPS, INPUT_CHARS, OUTPUT); break; case 1: msg = "Read, byte[]"; msecs = readWithByte(REPS, INPUT_CHARS, OUTPUT); break; case 2: msg = "Read, int[]"; msecs = readWithInt(REPS, INPUT_CHARS, OUTPUT); break; default: throw new Error(); } // skip first 5 rounds to let results stabilize if (roundsDone >= WARMUP_ROUNDS) { times[round] += msecs; } System.out.printf("Test '%s' [hash: 0x%s] -> %d msecs\n", msg, this.hash, msecs); if (lf) { ++roundsDone; if ((roundsDone % 7) == 0 && roundsDone > WARMUP_ROUNDS) { double den = (double) (roundsDone - WARMUP_ROUNDS); System.out.printf("Averages after %d rounds (classic, byte[], int[]): " +"%.1f / %.1f / %.1f msecs\n", (int) den ,times[0] / den, times[1] / den, times[2] / den ); } System.out.println(); } if ((i % 17) == 0) { System.out.println("[GC]"); Thread.sleep(100L); System.gc(); Thread.sleep(100L); } } } private final long readClassic(int REPS, char[] input, char[] output) throws Exception { long start = System.currentTimeMillis(); final byte[] codes = SMALL_BYTE_CODES; final int MAX = 256; while (--REPS >= 0) { int outPtr = 0; for (int i = 0, end = input.length; i < end; ++i) { int ch = input[i]; if (ch < MAX && codes[ch] == NULL_BYTE) { output[outPtr++] = (char) ch; continue; } if (ch == '\\') { output[outPtr++] = '_'; } else if (ch == '\n') { output[outPtr++] = '_'; } } } long time = System.currentTimeMillis() - start; return time; } private final long readWithByte(int REPS, char[] input, char[] output) throws Exception { long start = System.currentTimeMillis(); final byte[] codes = BYTE_CODES; while (--REPS >= 0) { int outPtr = 0; for (int i = 0, end = input.length; i < end; ++i) { char ch = input[i]; if (codes[ch] == NULL_BYTE) { output[outPtr++] = ch; continue; } if (ch == '\\') { output[outPtr++] = '_'; } else if (ch == '\n') { output[outPtr++] = '_'; } } } long time = System.currentTimeMillis() - start; return time; } final static byte NULL_BYTE = (byte) 0; private final long readWithInt(int REPS, char[] input, char[] output) throws Exception { long start = System.currentTimeMillis(); final int[] codes = INT_CODES; while (--REPS >= 0) { int outPtr = 0; for (int i = 0, end = input.length; i < end; ++i) { char ch = input[i]; if (codes[ch] == 0) { output[outPtr++] = ch; continue; } if (ch == '\\') { output[outPtr++] = '_'; } else if (ch == '\n') { output[outPtr++] = '_'; } } } long time = System.currentTimeMillis() - start; return time; } public static void main(String[] args) throws Exception { if (args.length != 0) { System.err.println("Usage: java ..."); System.exit(1); } new ManualCharAccessTest().test(); } } jackson-core-jackson-core-2.10.1/src/test/java/perf/ManualIntRead.java000066400000000000000000000035201356164247300255120ustar00rootroot00000000000000package perf; import com.fasterxml.jackson.core.*; /** * Manually run micro-benchmark for checking performance of tokenizing * simple tokens (false, true, null). */ public class ManualIntRead extends ParserTestBase { protected final JsonFactory _factory; protected final byte[] _jsonBytes; protected final char[] _jsonChars; private ManualIntRead(JsonFactory f, String json) throws Exception { _factory = f; _jsonChars = json.toCharArray(); _jsonBytes = json.getBytes("UTF-8"); } public static void main(String[] args) throws Exception { if (args.length != 0) { System.err.println("Usage: java ..."); System.exit(1); } final JsonFactory f = new JsonFactory(); final String jsonStr = aposToQuotes( "{'data':[1,-2,138,-78,0,12435,-12,-9],'last':12345}" ); new ManualIntRead(f, jsonStr).test("char[]", "byte[]", jsonStr.length()); } @Override protected void testRead1(int reps) throws Exception { while (--reps >= 0) { JsonParser p = _factory.createParser(_jsonChars); _stream(p); p.close(); } } @Override protected void testRead2(int reps) throws Exception { while (--reps >= 0) { JsonParser p = _factory.createParser(_jsonBytes); _stream(p); p.close(); } } private final void _stream(JsonParser p) throws Exception { JsonToken t; while ((t = p.nextToken()) != null) { // force decoding/reading of scalar values too (booleans are fine, nulls too) if (t == JsonToken.VALUE_STRING) { p.getText(); } else if (t == JsonToken.VALUE_NUMBER_INT) { p.getIntValue(); } } } } jackson-core-jackson-core-2.10.1/src/test/java/perf/ManualReadPerfWithMedia.java000066400000000000000000000047541356164247300274620ustar00rootroot00000000000000package perf; import com.fasterxml.jackson.core.*; public class ManualReadPerfWithMedia extends ParserTestBase { protected final JsonFactory _factory; protected final String _json; private ManualReadPerfWithMedia(JsonFactory f, String json) throws Exception { _factory = f; _json = json; } public static void main(String[] args) throws Exception { if (args.length != 0) { System.err.println("Usage: java ..."); System.exit(1); } MediaItem.Content content = new MediaItem.Content(); content.setTitle("Performance micro-benchmark, to be run manually"); content.addPerson("William"); content.addPerson("Robert"); content.setWidth(900); content.setHeight(120); content.setBitrate(256000); content.setDuration(3600 * 1000L); content.setCopyright("none"); content.setPlayer(MediaItem.Player.FLASH); content.setUri("http://whatever.biz"); MediaItem input = new MediaItem(content); input.addPhoto(new MediaItem.Photo("http://a.com", "title1", 200, 100, MediaItem.Size.LARGE)); input.addPhoto(new MediaItem.Photo("http://b.org", "title2", 640, 480, MediaItem.Size.SMALL)); final JsonFactory f = new JsonFactory(); final String jsonStr = input.asJsonString(f); final byte[] json = jsonStr.getBytes("UTF-8"); new ManualReadPerfWithMedia(f, jsonStr).test("String", "char[]", json.length); } @Override protected void testRead1(int reps) throws Exception { while (--reps >= 0) { // JsonParser p = _factory.createParser(new StringReader(_json)); JsonParser p = _factory.createParser(_json); _stream(p); p.close(); } } @Override protected void testRead2(int reps) throws Exception { final char[] ch = _json.toCharArray(); while (--reps >= 0) { JsonParser p = _factory.createParser(ch, 0, ch.length); _stream(p); p.close(); } } private final void _stream(JsonParser p) throws Exception { JsonToken t; while ((t = p.nextToken()) != null) { // force decoding/reading of scalar values too (booleans are fine, nulls too) if (t == JsonToken.VALUE_STRING) { p.getText(); } else if (t == JsonToken.VALUE_NUMBER_INT) { p.getLongValue(); } } } } jackson-core-jackson-core-2.10.1/src/test/java/perf/ManualSmallTokenRead.java000066400000000000000000000035511356164247300270350ustar00rootroot00000000000000package perf; import com.fasterxml.jackson.core.*; /** * Manually run micro-benchmark for checking performance of tokenizing * simple tokens (false, true, null). */ public class ManualSmallTokenRead extends ParserTestBase { protected final JsonFactory _factory; protected final byte[] _jsonBytes; protected final char[] _jsonChars; private ManualSmallTokenRead(JsonFactory f, String json) throws Exception { _factory = f; _jsonChars = json.toCharArray(); _jsonBytes = json.getBytes("UTF-8"); } public static void main(String[] args) throws Exception { if (args.length != 0) { System.err.println("Usage: java ..."); System.exit(1); } final JsonFactory f = new JsonFactory(); final String jsonStr = aposToQuotes( "{'data':[true,false,null,false,null,true],'last':true}" ); new ManualSmallTokenRead(f, jsonStr).test("char[]", "byte[]", jsonStr.length()); } @Override protected void testRead1(int reps) throws Exception { while (--reps >= 0) { JsonParser p = _factory.createParser(_jsonChars); _stream(p); p.close(); } } @Override protected void testRead2(int reps) throws Exception { while (--reps >= 0) { JsonParser p = _factory.createParser(_jsonBytes); _stream(p); p.close(); } } private final void _stream(JsonParser p) throws Exception { JsonToken t; while ((t = p.nextToken()) != null) { // force decoding/reading of scalar values too (booleans are fine, nulls too) if (t == JsonToken.VALUE_STRING) { p.getText(); } else if (t == JsonToken.VALUE_NUMBER_INT) { p.getLongValue(); } } } } jackson-core-jackson-core-2.10.1/src/test/java/perf/MediaItem.java000066400000000000000000000142271356164247300246720ustar00rootroot00000000000000package perf; import java.io.IOException; import java.io.StringWriter; import java.util.*; import com.fasterxml.jackson.core.*; public class MediaItem { public enum Player { JAVA, FLASH; } public enum Size { SMALL, LARGE; } private List _photos; private Content _content; public MediaItem() { } public MediaItem(Content c) { _content = c; } public void addPhoto(Photo p) { if (_photos == null) { _photos = new ArrayList(); } _photos.add(p); } public List getImages() { return _photos; } public void setImages(List p) { _photos = p; } public Content getContent() { return _content; } public void setContent(Content c) { _content = c; } public String asJsonString(JsonFactory f) throws IOException { StringWriter w = new StringWriter(); JsonGenerator gen = f.createGenerator(w); write(gen); gen.close(); w.close(); return w.toString(); } public void write(JsonGenerator gen) throws IOException { gen.writeStartObject(); gen.writeFieldName("content"); if (_content == null) { gen.writeNull(); } else { _content.write(gen); } gen.writeFieldName("photos"); if (_photos == null) { gen.writeNull(); } else { gen.writeStartArray(); for (int i = 0, len = _photos.size(); i < len; ++i) { _photos.get(i).write(gen); } gen.writeEndArray(); } gen.writeEndObject(); } /* /********************************************************** /* Helper types /********************************************************** */ public static class Photo { private String _uri; private String _title; private int _width; private int _height; private Size _size; public Photo() {} public Photo(String uri, String title, int w, int h, Size s) { _uri = uri; _title = title; _width = w; _height = h; _size = s; } public String getUri() { return _uri; } public String getTitle() { return _title; } public int getWidth() { return _width; } public int getHeight() { return _height; } public Size getSize() { return _size; } public void setUri(String u) { _uri = u; } public void setTitle(String t) { _title = t; } public void setWidth(int w) { _width = w; } public void setHeight(int h) { _height = h; } public void setSize(Size s) { _size = s; } public void write(JsonGenerator gen) throws IOException { gen.writeStartObject(); gen.writeStringField("uri", _uri); gen.writeStringField("title", _title); gen.writeNumberField("width", _width); gen.writeNumberField("height", _height); if (_size == null) { gen.writeNullField("size"); } else { gen.writeStringField("size", _size.name()); } gen.writeEndObject(); } } public static class Content { private String _uri; private String _title; private int _width; private int _height; private String _format; private long _duration; private long _size; private int _bitrate; private String _copyright; private Player _player; private List _persons; public void write(JsonGenerator gen) throws IOException { gen.writeStartObject(); gen.writeStringField("uri", _uri); gen.writeStringField("title", _title); gen.writeNumberField("width", _width); gen.writeNumberField("height", _height); gen.writeStringField("format", _format); gen.writeNumberField("duration", _duration); gen.writeNumberField("size", _size); gen.writeNumberField("bitrate", _bitrate); gen.writeStringField("copyright", _copyright); if (_player == null) { gen.writeNullField("player"); } else { gen.writeStringField("player", _player.name()); } gen.writeFieldName("photos"); if (_persons == null) { gen.writeNull(); } else { gen.writeStartArray(); for (int i = 0, len = _persons.size(); i < len; ++i) { gen.writeString(_persons.get(i)); } gen.writeEndArray(); } gen.writeEndObject(); } public Content() { } public void addPerson(String p) { if (_persons == null) { _persons = new ArrayList(); } _persons.add(p); } public Player getPlayer() { return _player; } public String getUri() { return _uri; } public String getTitle() { return _title; } public int getWidth() { return _width; } public int getHeight() { return _height; } public String getFormat() { return _format; } public long getDuration() { return _duration; } public long getSize() { return _size; } public int getBitrate() { return _bitrate; } public List getPersons() { return _persons; } public String getCopyright() { return _copyright; } public void setPlayer(Player p) { _player = p; } public void setUri(String u) { _uri = u; } public void setTitle(String t) { _title = t; } public void setWidth(int w) { _width = w; } public void setHeight(int h) { _height = h; } public void setFormat(String f) { _format = f; } public void setDuration(long d) { _duration = d; } public void setSize(long s) { _size = s; } public void setBitrate(int b) { _bitrate = b; } public void setPersons(List p) { _persons = p; } public void setCopyright(String c) { _copyright = c; } } } jackson-core-jackson-core-2.10.1/src/test/java/perf/ParserTestBase.java000066400000000000000000000053241356164247300257210ustar00rootroot00000000000000package perf; abstract class ParserTestBase { protected int hash; protected void test(String desc1, String desc2, int expSize) throws Exception { // guessing we have 500 byte final int REPS = (int) ((double) (10 * 1000 * 1000) / (double) expSize); System.out.printf("Estimating %d bytes to read; will do %d repetitions\n", expSize, REPS); int i = 0; int roundsDone = 0; final int TYPES = 2; final int WARMUP_ROUNDS = 5; final long[] times = new long[TYPES]; while (true) { try { Thread.sleep(100L); } catch (InterruptedException ie) { } int round = (i++ % TYPES); String msg; boolean lf = (round == 0); long msecs; switch (round) { case 0: msg = desc1; msecs = _testRead1(REPS); break; case 1: msg = desc2; msecs = _testRead2(REPS); break; default: throw new Error(); } // skip first 5 rounds to let results stabilize if (roundsDone >= WARMUP_ROUNDS) { times[round] += msecs; } System.out.printf("Test '%s' [hash: 0x%s] -> %d msecs\n", msg, this.hash, msecs); if (lf) { ++roundsDone; if ((roundsDone % 3) == 0 && roundsDone > WARMUP_ROUNDS) { double den = (double) (roundsDone - WARMUP_ROUNDS); System.out.printf("Averages after %d rounds ("+desc1+" / "+desc2+"): %.1f / %.1f msecs\n", (int) den, times[0] / den, times[1] / den); } System.out.println(); } if ((i % 17) == 0) { System.out.println("[GC]"); Thread.sleep(100L); System.gc(); Thread.sleep(100L); } } } protected long _testRead1(int reps) throws Exception { final long start = System.currentTimeMillis(); testRead1(reps); return System.currentTimeMillis() - start; } protected long _testRead2(int reps) throws Exception { final long start = System.currentTimeMillis(); testRead2(reps); return System.currentTimeMillis() - start; } protected abstract void testRead1(int reps) throws Exception; protected abstract void testRead2(int reps) throws Exception; protected static String aposToQuotes(String json) { return json.replace("'", "\""); } } jackson-core-jackson-core-2.10.1/src/test/resources/000077500000000000000000000000001356164247300223205ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/resources/META-INF/000077500000000000000000000000001356164247300234605ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/resources/META-INF/maven/000077500000000000000000000000001356164247300245665ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/resources/META-INF/maven/foo/000077500000000000000000000000001356164247300253515ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/resources/META-INF/maven/foo/bar/000077500000000000000000000000001356164247300261155ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/resources/META-INF/maven/foo/bar/foo-bar/000077500000000000000000000000001356164247300274425ustar00rootroot00000000000000jackson-core-jackson-core-2.10.1/src/test/resources/META-INF/maven/foo/bar/foo-bar/pom.properties000066400000000000000000000000711356164247300323510ustar00rootroot00000000000000groupId=foo.bar artifactId=foo-bar version=1.2.3-SNAPSHOTjackson-core-jackson-core-2.10.1/src/test/resources/test_0xA0.json000066400000000000000000000003171356164247300247630ustar00rootroot00000000000000{ "request":{ "mac": "000000BAD004",     "data": {         "invoice": "54321",         "tag": "TAG2",         "car_number":"CA492CF"     } } }