pax_global_header00006660000000000000000000000064121505662220014513gustar00rootroot0000000000000052 comment=38ffbfd6da68e913811d60698277e2bce96ce4ed jackson-core-jackson-core-2.2.2/000077500000000000000000000000001215056622200164505ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/.gitignore000066400000000000000000000002351215056622200204400ustar00rootroot00000000000000# use glob syntax. syntax: glob *.class *~ *.bak *.off *.old .DS_Store # building target # Eclipse .classpath .project .settings # IDEA *.iml *.ipr *.iws jackson-core-jackson-core-2.2.2/README.md000066400000000000000000000101611215056622200177260ustar00rootroot00000000000000# 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. Alternate data format implementations (like [Smile (binary JSON)](https://github.com/FasterXML/jackson-dataformat-smile), [XML](https://github.com/FasterXML/jackson-dataformat-xml) and [CSV](https://github.com/FasterXML/jackson-dataformat-csv)) 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://fasterxml.ci.cloudbees.com/job/jackson-core-master/badge/icon)](https://fasterxml.ci.cloudbees.com/job/jackson-core-master/) ### 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`) ---- # 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 2.2.0 ``` or download jars from Maven repository or [Download page](http://wiki.fasterxml.com/JacksonDownload). 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 [Download page](https://github.com/FasterXML/jackson-core/wiki/Downloads). 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.getJsonFactory(); ``` More information can be found from [Streaming API](http://wiki.fasterxml.com/JacksonStreamingApi ) at Jackson Wiki. ## 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 * [Documentation](https://github.com/FasterXML/jackson-core/wiki/Documentation) has other project documentation * [Jackson Project Home](http://wiki.fasterxml.com/JacksonHome) jackson-core-jackson-core-2.2.2/create-test-report.sh000077500000000000000000000000501215056622200225330ustar00rootroot00000000000000#!/bin/sh mvn surefire-report:report jackson-core-jackson-core-2.2.2/pom.xml000066400000000000000000000135301215056622200177670ustar00rootroot00000000000000 4.0.0 com.fasterxml oss-parent 10 com.fasterxml.jackson.core jackson-core Jackson-core 2.2.2 Core Jackson abstractions, basic JSON streaming API implementation http://wiki.fasterxml.com/JacksonHome 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.2.2 com.fasterxml.jackson.core;version=${project.version}, com.fasterxml.jackson.core.*;version=${project.version} com/fasterxml/jackson/core/json com.fasterxml.jackson.core.json junit junit 4.8.2 test org.apache.maven.plugins maven-javadoc-plugin 2.8.1 1.6 1.6 UTF-8 512m http://docs.oracle.com/javase/6/docs/api/ attach-javadocs verify jar org.apache.maven.plugins maven-site-plugin 3.1 org.apache.maven.plugins maven-surefire-plugin ${surefire.version} ${surefire.redirectTestOutputToFile} com.google.code.maven-replacer-plugin replacer process-packageVersion generate-sources org.apache.maven.wagon wagon-ssh-external 1.0-beta-6 org.apache.maven.scm maven-scm-provider-gitexe 1.6 org.apache.maven.scm maven-scm-manager-plexus 1.6 org.kathrynhuxtable.maven.wagon wagon-gitsite 0.3.1 org.apache.maven.plugins maven-javadoc-plugin 2.8.1 true 1.6 UTF-8 1g http://docs.oracle.com/javase/6/docs/api/ http://fasterxml.github.com/jackson-core/javadoc/2.2.0/ ${javadoc.package.exclude} ${sun.boot.class.path} com.google.doclava.Doclava false -J-Xmx1024m com.google.doclava doclava 1.0.3 -hdf project.name "${project.name} ${project.version}" -d ${project.reporting.outputDirectory}/apidocs default javadoc jackson-core-jackson-core-2.2.2/release-notes/000077500000000000000000000000001215056622200212165ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/release-notes/CREDITS000066400000000000000000000013331215056622200222360ustar00rootroot00000000000000Here are people who have contributed to development Jackson JSON process 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 jackson-core-jackson-core-2.2.2/release-notes/VERSION000066400000000000000000000071431215056622200222730ustar00rootroot00000000000000Project: jackson-core Version: 2.2.2 (26-May-2013) No changes since previous version. ------------------------------------------------------------------------ === History: === ------------------------------------------------------------------------ 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.2.2/src/000077500000000000000000000000001215056622200172375ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/000077500000000000000000000000001215056622200201635ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/java/000077500000000000000000000000001215056622200211045ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/java/com/000077500000000000000000000000001215056622200216625ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/000077500000000000000000000000001215056622200236675ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/000077500000000000000000000000001215056622200253175ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/000077500000000000000000000000001215056622200262475ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/Base64Variant.java000066400000000000000000000331071215056622200314670ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; import java.util.Arrays; /** * Abstract base 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 { // 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 */ protected final String _name; /** * Whether this variant uses padding or not. */ protected final transient boolean _usesPadding; /** * Characted used for padding, if any ({@link #PADDING_CHAR_NONE} if not). */ protected 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. */ protected 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; return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID; } /* /********************************************************** /* 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 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. * * @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) { int inputEnd = input.length; StringBuilder sb; { // let's approximate... 33% overhead, ~= 3/8 (0.375) int outputLen = inputEnd + (inputEnd >> 2) + (inputEnd >> 3); sb = new StringBuilder(outputLen); } 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(); } /* /********************************************************** /* other methods /********************************************************** */ @Override public String toString() { return _name; } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/Base64Variants.java000066400000000000000000000077321215056622200316570ustar00rootroot00000000000000/* 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 { StringBuffer sb = new StringBuffer(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.2.2/src/main/java/com/fasterxml/jackson/core/FormatSchema.java000066400000000000000000000025251215056622200314670ustar00rootroot00000000000000/* 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.2.2/src/main/java/com/fasterxml/jackson/core/JsonEncoding.java000066400000000000000000000030741215056622200314760ustar00rootroot00000000000000/* 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), // N/A for big-endian, really UTF16_BE("UTF-16BE", true), UTF16_LE("UTF-16LE", false), UTF32_BE("UTF-32BE", true), UTF32_LE("UTF-32LE", false) ; protected final String _javaName; protected final boolean _bigEndian; JsonEncoding(String javaName, boolean bigEndian) { _javaName = javaName; _bigEndian = bigEndian; } /** * 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; } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/JsonFactory.java000066400000000000000000001417431215056622200313650ustar00rootroot00000000000000/* 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.sym.BytesToNameCanonicalizer; import com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer; import com.fasterxml.jackson.core.util.BufferRecycler; 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 */ public class JsonFactory implements Versioned, java.io.Serializable // since 2.1 (for Android, mostly) { /** * Computed for Jackson 2.2.0 release */ private static final long serialVersionUID = 8726401676402117450L; /* /********************************************************** /* 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) ; /** * 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(); private final static SerializableString DEFAULT_ROOT_VALUE_SEPARATOR = DefaultPrettyPrinter.DEFAULT_ROOT_VALUE_SEPARATOR; /* /********************************************************** /* Buffer, symbol table management /********************************************************** */ /** * 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>(); /** * 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. */ protected final transient BytesToNameCanonicalizer _rootByteSymbols = BytesToNameCanonicalizer.createRoot(); /* /********************************************************** /* Configuration /********************************************************** */ /** * 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; /** * 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; /** * 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; /* /********************************************************** /* 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; } /** * Constructor used when copy()ing a factory instance. * * @since 2.2.1 */ protected JsonFactory(JsonFactory src, ObjectCodec codec) { _objectCodec = null; _factoryFeatures = src._factoryFeatures; _parserFeatures = src._parserFeatures; _generatorFeatures = src._generatorFeatures; _characterEscapes = src._characterEscapes; _inputDecorator = src._inputDecorator; _outputDecorator = src._outputDecorator; _rootValueSeparator = src._rootValueSeparator; /* 27-Apr-2013, tatu: How about symbol table; should we try to * reuse shared symbol tables? Could be more efficient that way; * although can slightly add to concurrency overhead. */ } /** * 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 * @param exp */ 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); } /* /********************************************************** /* Format detection functionality (since 1.8) /********************************************************** */ /** * 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 boolean canUseSchema(FormatSchema schema) { 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 */ 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; } 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) */ 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) */ 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) */ 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; } /* /********************************************************** /* 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. */ public final boolean isEnabled(JsonParser.Feature f) { return (_parserFeatures & f.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 */ 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. */ public final boolean isEnabled(JsonGenerator.Feature f) { return (_generatorFeatures & f.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 */ 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 (new ones, as per [Issue-25]) /********************************************************** */ /** * Method for constructing JSON parser instance to parse * contents of specified file. Encoding is auto-detected * from contents according to JSON specification recommended * mechanism. *

* 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 */ @SuppressWarnings("resource") 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); // [JACKSON-512]: allow wrapping with InputDecorator if (_inputDecorator != null) { in = _inputDecorator.decorate(ctxt, in); } return _createParser(in, 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. *

* 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 */ public JsonParser createParser(URL url) throws IOException, JsonParseException { // true, since we create InputStream from URL IOContext ctxt = _createContext(url, true); InputStream in = _optimizedStreamFromURL(url); // [JACKSON-512]: allow wrapping with InputDecorator if (_inputDecorator != null) { in = _inputDecorator.decorate(ctxt, in); } return _createParser(in, 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.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. * * @param in InputStream to use for reading JSON content to parse * * @since 2.1 */ public JsonParser createParser(InputStream in) throws IOException, JsonParseException { IOContext ctxt = _createContext(in, false); // [JACKSON-512]: allow wrapping with InputDecorator if (_inputDecorator != null) { in = _inputDecorator.decorate(ctxt, in); } return _createParser(in, 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.JsonParser.Feature#AUTO_CLOSE_SOURCE} * is enabled. * * @param r Reader to use for reading JSON content to parse * * @since 2.1 */ public JsonParser createParser(Reader r) throws IOException, JsonParseException { // false -> we do NOT own Reader (did not create it) IOContext ctxt = _createContext(r, false); // [JACKSON-512]: allow wrapping with InputDecorator if (_inputDecorator != null) { r = _inputDecorator.decorate(ctxt, r); } return _createParser(r, ctxt); } /** * Method for constructing parser for parsing * the contents of given byte array. * * @since 2.1 */ public JsonParser createParser(byte[] data) throws IOException, JsonParseException { IOContext ctxt = _createContext(data, true); // [JACKSON-512]: allow wrapping with InputDecorator 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 */ 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 */ public JsonParser createParser(String content) throws IOException, JsonParseException { Reader r = new StringReader(content); // true -> we own the Reader (and must close); not a big deal IOContext ctxt = _createContext(r, true); // [JACKSON-512]: allow wrapping with InputDecorator if (_inputDecorator != null) { r = _inputDecorator.decorate(ctxt, r); } return _createParser(r, ctxt); } /* /********************************************************** /* Parser factories (old ones, as per [Issue-25]) /********************************************************** */ /** * Method for constructing JSON parser instance to parse * contents of specified file. Encoding is auto-detected * from contents according to JSON specification recommended * mechanism. *

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

* NOTE: as of 2.1, should not be used (will be deprecated in 2.2); * instead, should call createParser. * * @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. *

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

* NOTE: as of 2.1, should not be used (will be deprecated in 2.2); * instead, should call createParser. * * @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. *

* NOTE: as of 2.1, should not be used (will be deprecated in 2.2); * instead, should call createParser. * * @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. *

* NOTE: as of 2.1, should not be used (will be deprecated in 2.2); * instead, should call createParser. * * @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. *

* NOTE: as of 2.1, should not be used (will be deprecated in 2.2); * instead, should call createParser. * * @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. *

* NOTE: as of 2.1, should not be used (will be deprecated in 2.2); * instead, should call createParser. * * @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); } /* /********************************************************** /* Generator factories, new (as per [Issue-25] /********************************************************** */ /** * 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 */ 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) { // [JACKSON-512]: allow wrapping with _outputDecorator if (_outputDecorator != null) { out = _outputDecorator.decorate(ctxt, out); } return _createUTF8Generator(out, ctxt); } Writer w = _createWriter(out, enc, ctxt); // [JACKSON-512]: allow wrapping with _outputDecorator if (_outputDecorator != null) { w = _outputDecorator.decorate(ctxt, w); } return _createGenerator(w, 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 */ 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 out Writer to use for writing JSON content */ public JsonGenerator createGenerator(Writer out) throws IOException { IOContext ctxt = _createContext(out, false); // [JACKSON-512]: allow wrapping with _outputDecorator if (_outputDecorator != null) { out = _outputDecorator.decorate(ctxt, out); } return _createGenerator(out, 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 */ 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) { // [JACKSON-512]: allow wrapping with _outputDecorator if (_outputDecorator != null) { out = _outputDecorator.decorate(ctxt, out); } return _createUTF8Generator(out, ctxt); } Writer w = _createWriter(out, enc, ctxt); // [JACKSON-512]: allow wrapping with _outputDecorator if (_outputDecorator != null) { w = _outputDecorator.decorate(ctxt, w); } return _createGenerator(w, ctxt); } /* /********************************************************** /* Generator factories, old (as per [Issue-25] /********************************************************** */ /** * 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); } /** * 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 * * * @deprecated Since 2.2, use {@link #createGenerator(File,JsonEncoding)} instead. */ @Deprecated public JsonGenerator createJsonGenerator(File f, JsonEncoding enc) throws IOException { return createGenerator(f, enc); } /* /********************************************************** /* 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, JsonParseException { // As per [JACKSON-259], may want to fully disable canonicalization: return new ByteSourceJsonBootstrapper(ctxt, in).constructParser(_parserFeatures, _objectCodec, _rootByteSymbols, _rootCharSymbols, isEnabled(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES), isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)); } /** * @deprecated since 2.1 -- use {@link #_createParser(InputStream, IOContext)} instead */ @Deprecated protected JsonParser _createJsonParser(InputStream in, IOContext ctxt) throws IOException, JsonParseException { return _createParser(in, ctxt); } /** * 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, JsonParseException { return new ReaderBasedJsonParser(ctxt, _parserFeatures, r, _objectCodec, _rootCharSymbols.makeChild(isEnabled(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES), isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES))); } /** * @deprecated since 2.1 -- use {@link #_createParser(Reader, IOContext)} instead */ @Deprecated protected JsonParser _createJsonParser(Reader r, IOContext ctxt) throws IOException, JsonParseException { return _createParser(r, ctxt); } /** * 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, JsonParseException { return new ByteSourceJsonBootstrapper(ctxt, data, offset, len).constructParser(_parserFeatures, _objectCodec, _rootByteSymbols, _rootCharSymbols, isEnabled(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES), isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)); } /** * @deprecated since 2.1 -- use {@link #_createParser(byte[], int, int, IOContext)} instead */ @Deprecated protected JsonParser _createJsonParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException, JsonParseException { return _createParser(data, offset, len, ctxt); } /* /********************************************************** /* 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); if (_characterEscapes != null) { gen.setCharacterEscapes(_characterEscapes); } SerializableString rootSep = _rootValueSeparator; if (rootSep != DEFAULT_ROOT_VALUE_SEPARATOR) { gen.setRootValueSeparator(rootSep); } return gen; } /** * @deprecated since 2.1 -- use {@link #_createGenerator(Writer, IOContext)} instead */ @Deprecated protected JsonGenerator _createJsonGenerator(Writer out, IOContext ctxt) throws IOException { /* NOTE: MUST call the deprecated method until it is deleted, just so * that override still works as expected, for now. */ return _createGenerator(out, ctxt); } /** * 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); if (_characterEscapes != null) { gen.setCharacterEscapes(_characterEscapes); } SerializableString rootSep = _rootValueSeparator; if (rootSep != DEFAULT_ROOT_VALUE_SEPARATOR) { gen.setRootValueSeparator(rootSep); } return gen; } /** * @deprecated since 2.1 */ @Deprecated protected JsonGenerator _createUTF8JsonGenerator(OutputStream out, IOContext ctxt) throws IOException { return _createUTF8Generator(out, ctxt); } 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, other /********************************************************** */ /** * Overridable factory method that actually instantiates desired * context object. */ protected IOContext _createContext(Object srcRef, boolean resourceManaged) { return new IOContext(_getBufferRecycler(), srcRef, resourceManaged); } /** * Method used by factory to create buffer recycler instances * for parsers and generators. *

* Note: only public to give access for ObjectMapper */ public BufferRecycler _getBufferRecycler() { SoftReference ref = _recyclerRef.get(); BufferRecycler br = (ref == null) ? null : ref.get(); if (br == null) { br = new BufferRecycler(); _recyclerRef.set(new SoftReference(br)); } return br; } /** * 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) { // [Issue#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(); } } JsonGenerationException.java000066400000000000000000000014241215056622200336400ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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... public JsonGenerationException(Throwable rootCause) { super(rootCause); } public JsonGenerationException(String msg) { super(msg, (JsonLocation)null); } public JsonGenerationException(String msg, Throwable rootCause) { super(msg, (JsonLocation)null, rootCause); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java000066400000000000000000001312621215056622200316770ustar00rootroot00000000000000/* 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 com.fasterxml.jackson.core.io.CharacterEscapes; /** * 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, // as of 2.1 Versioned { /** * Enumeration that defines all togglable features for generators. */ public enum Feature { /** * 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 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). */ 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. */ QUOTE_NON_NUMERIC_NUMBERS(true), /** * Feature that forces all Java numbers to be written as JSON strings. * Default state is 'false', meaning that Java numbers are to * be serialized using basic numeric serialization (as JSON * numbers, integral or floating point). If enabled, all such * numeric values are instead written out as JSON Strings. *

* 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. */ WRITE_NUMBERS_AS_STRINGS(false), /** * 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), /** * 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. */ ESCAPE_NON_ASCII(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) { _mask = (1 << ordinal()); _defaultState = defaultState; } public boolean enabledByDefault() { return _defaultState; } 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(); /** * 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; } /** * 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; } /** * Accessor for finding out version of the bundle that provided this generator instance. */ @Override public abstract Version version(); /** * 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 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 JsonGenerator setRootValueSeparator(SerializableString sep) { throw new UnsupportedOperationException(); } /* /********************************************************** /* Public API, 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); /* /********************************************************** /* Configuring generator /********************************************************** */ /** * 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 -1 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. */ public JsonGenerator setCharacterEscapes(CharacterEscapes esc) { return this; } /* /********************************************************** /* Public API, write methods, structural /********************************************************** */ /** * Method for writing starting marker of a JSON Array value * (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, JsonGenerationException; /** * 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, JsonGenerationException; /** * Method for writing starting marker of a JSON 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, JsonGenerationException; /** * Method for writing closing marker of a JSON 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, JsonGenerationException; /** * 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, JsonGenerationException; /** * 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, JsonGenerationException; /* /********************************************************** /* 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, JsonGenerationException; /** * 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, JsonGenerationException; /** * 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, JsonGenerationException; /** * 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, JsonGenerationException; /** * 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, JsonGenerationException; /* /********************************************************** /* 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, JsonGenerationException; /** * 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, JsonGenerationException; /** * 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, JsonGenerationException; /** * 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, JsonGenerationException; /** * 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 void writeRaw(SerializableString raw) throws IOException, JsonGenerationException { 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, JsonGenerationException; public abstract void writeRawValue(String text, int offset, int len) throws IOException, JsonGenerationException; public abstract void writeRawValue(char[] text, int offset, int len) throws IOException, JsonGenerationException; /** * 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 b64variant 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 b64variant, byte[] data, int offset, int len) throws IOException, JsonGenerationException; /** * 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, JsonGenerationException { 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, JsonGenerationException { 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, JsonGenerationException { 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 b64variant 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 b64variant, InputStream data, int dataLength) throws IOException, JsonGenerationException; /* /********************************************************** /* Public API, write methods, other value types /********************************************************** */ /** * 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. * * @since 2.2 */ public void writeNumber(short v) throws IOException, JsonGenerationException { 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. */ public abstract void writeNumber(int v) throws IOException, JsonGenerationException; /** * 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. */ public abstract void writeNumber(long v) throws IOException, JsonGenerationException; /** * 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. */ public abstract void writeNumber(BigInteger v) throws IOException, JsonGenerationException; /** * 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. */ public abstract void writeNumber(double d) throws IOException, JsonGenerationException; /** * 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. */ public abstract void writeNumber(float f) throws IOException, JsonGenerationException; /** * 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. */ public abstract void writeNumber(BigDecimal dec) throws IOException, JsonGenerationException; /** * 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}. */ public abstract void writeNumber(String encodedValue) throws IOException, JsonGenerationException, UnsupportedOperationException; /** * 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, JsonGenerationException; /** * 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, JsonGenerationException; /* /********************************************************** /* 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, JsonProcessingException; /** * 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, JsonProcessingException; /* /********************************************************** /* 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, JsonGenerationException { 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, JsonGenerationException { 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, JsonGenerationException { 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, JsonGenerationException { 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, JsonGenerationException { 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, JsonGenerationException { 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, JsonGenerationException { 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, JsonGenerationException { 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, JsonGenerationException { 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, JsonGenerationException { writeFieldName(fieldName); writeStartArray(); } /** * Convenience method for outputting a field entry ("member") * (that will contain a JSON 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, JsonGenerationException { 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, JsonProcessingException { writeFieldName(fieldName); writeObject(pojo); } /* /********************************************************** /* 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 abstract void copyCurrentEvent(JsonParser jp) throws IOException, JsonProcessingException; /** * 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 abstract void copyCurrentStructure(JsonParser jp) throws IOException, JsonProcessingException; /* /********************************************************** /* 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; } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/JsonLocation.java000066400000000000000000000100441215056622200315130ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; /** * 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 // as per [JACKSON-168] { private static final long serialVersionUID = 1L; /** * Shared immutable "N/A location" that can be returned to indicate * that no location information is available */ public final static JsonLocation NA = new JsonLocation("N/A", -1L, -1L, -1, -1); final long _totalBytes; final long _totalChars; final int _lineNr; 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; } @Override public String toString() { StringBuilder sb = new StringBuilder(80); sb.append("[Source: "); if (_sourceRef == null) { sb.append("UNKNOWN"); } else { sb.append(_sourceRef.toString()); } sb.append("; line: "); sb.append(_lineNr); sb.append(", column: "); sb.append(_columnNr); sb.append(']'); return sb.toString(); } @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()) ; } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/JsonParseException.java000066400000000000000000000012261215056622200326760ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; /** * 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 JsonProcessingException { final static long serialVersionUID = 123; // Stupid eclipse... public JsonParseException(String msg, JsonLocation loc) { super(msg, loc); } public JsonParseException(String msg, JsonLocation loc, Throwable root) { super(msg, loc, root); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/JsonParser.java000066400000000000000000001502751215056622200312120ustar00rootroot00000000000000/* 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.type.TypeReference; /** * 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. */ ALLOW_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. */ 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 acceptabl 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. */ 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. */ ALLOW_UNQUOTED_CONTROL_CHARS(false), /** * Feature that can be enabled to accept quoting of all character * using backslash qooting 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. */ 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. */ 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. */ ALLOW_NON_NUMERIC_NUMBERS(false), ; /** * 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()); } } /* /********************************************************** /* Minimal configuration state /********************************************************** */ /** * Bit flag composed of bits that indicate which * {@link com.fasterxml.jackson.core.JsonParser.Feature}s * are enabled. */ protected int _features; /* /********************************************************** /* 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; } /* /********************************************************** /* 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; } /** * 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; } /* /********************************************************** /* 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; /* /********************************************************** /* 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 (_features & f.getMask()) != 0; } /* /********************************************************** /* 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, JsonParseException; /** * 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, JsonParseException; /** * 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, JsonParseException { return (nextToken() == JsonToken.FIELD_NAME) && str.getValue().equals(getCurrentName()); } /** * 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, JsonParseException { 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 a String value next from input. */ public int nextIntValue(int defaultValue) throws IOException, JsonParseException { 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 String value next from input. */ public long nextLongValue(long defaultValue) throws IOException, JsonParseException { 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 String value next from input. */ public Boolean nextBooleanValue() throws IOException, JsonParseException { switch (nextToken()) { case VALUE_TRUE: return Boolean.TRUE; case VALUE_FALSE: return Boolean.FALSE; default: 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, JsonParseException; /** * 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, token accessors /********************************************************** */ /** * 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. */ public abstract JsonToken getCurrentToken(); /** * 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 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, JsonParseException; /** * 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(); /** * 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. *

* Default implementation is equivalent to: *

     *   getCurrentToken() == 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 getCurrentToken() == JsonToken.START_ARRAY; } /* /********************************************************** /* 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. * * @since 2.0 */ public abstract void overrideCurrentName(String name); /* /********************************************************** /* Public API, access to token information, text /********************************************************** */ /** * 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, JsonParseException; /** * 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, JsonParseException; /** * 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, JsonParseException; /** * 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, JsonParseException; /** * 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, JsonParseException; /** * 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, JsonParseException; /** * 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, JsonParseException { 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, JsonParseException { 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, JsonParseException; /** * 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, JsonParseException; /** * 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, JsonParseException; /** * 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, JsonParseException; /** * 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, JsonParseException; /** * 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, JsonParseException; /* /********************************************************** /* 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, JsonParseException { JsonToken t = getCurrentToken(); if (t == JsonToken.VALUE_TRUE) return true; if (t == JsonToken.VALUE_FALSE) return false; throw new JsonParseException("Current token ("+t+") not of boolean type", getCurrentLocation()); } /** * 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). */ public abstract Object getEmbeddedObject() throws IOException, JsonParseException; /* /********************************************************** /* 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 b64variant Expected variant of base64 encoded * content (see {@link Base64Variants} for definitions * of "standard" variants). * * @return Decoded binary data */ public abstract byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException; /** * Convenience alternative to {@link #getBinaryValue(Base64Variant)} * that defaults to using * {@link Base64Variants#getDefaultVariant} as the default encoding. */ public byte[] getBinaryValue() throws IOException, JsonParseException { 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, JsonParseException { return readBinaryValue(Base64Variants.getDefaultVariant(), out); } /** * Similar to {@link #readBinaryValue(OutputStream)} but allows explicitly * specifying base64 variant to use. * * @param b64variant 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 b64variant, OutputStream out) throws IOException, JsonParseException { _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, JsonParseException { 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 defaultValue will be returned; no exceptions are thrown. */ public int getValueAsInt(int defaultValue) throws IOException, JsonParseException { return defaultValue; } /** * 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 an int (including structured type * markers like start/end Object/Array) * default value of 0 will be returned; no exceptions are thrown. */ public long getValueAsLong() throws IOException, JsonParseException { 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 an int (including structured type * markers like start/end Object/Array) * specified defaultValue will be returned; no exceptions are thrown. */ public long getValueAsLong(long defaultValue) throws IOException, JsonParseException { return defaultValue; } /** * 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 integer * parsing rules. *

* If representation can not be converted to an int (including structured types * like Objects and Arrays), * default value of 0.0 will be returned; no exceptions are thrown. */ public double getValueAsDouble() throws IOException, JsonParseException { 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 integer * parsing rules. *

* If representation can not be converted to an int (including structured types * like Objects and Arrays), * specified defaultValue will be returned; no exceptions are thrown. */ public double getValueAsDouble(double defaultValue) throws IOException, JsonParseException { return defaultValue; } /** * 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, JsonParseException { 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 defaultValue will be returned; no exceptions are thrown. */ public boolean getValueAsBoolean(boolean defaultValue) throws IOException, JsonParseException { return defaultValue; } /** * 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, JsonParseException { 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 defaultValue) throws IOException, JsonParseException; /* /********************************************************** /* 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, JsonProcessingException { ObjectCodec codec = getCodec(); if (codec == null) { throw new IllegalStateException("No ObjectCodec defined for the parser, can not deserialize JSON into Java objects"); } 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, JsonProcessingException { ObjectCodec codec = getCodec(); if (codec == null) { throw new IllegalStateException("No ObjectCodec defined for the parser, can not deserialize JSON into Java objects"); } /* Ugh. Stupid Java type erasure... can't just chain call,s * must cast here also. */ 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, JsonProcessingException { ObjectCodec codec = getCodec(); if (codec == null) { throw new IllegalStateException("No ObjectCodec defined for the parser, can not deserialize JSON into Java objects"); } 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, JsonProcessingException { ObjectCodec codec = getCodec(); if (codec == null) { throw new IllegalStateException("No ObjectCodec defined for the parser, can not deserialize JSON into Java objects"); } 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 */ @SuppressWarnings("unchecked") public T readValueAsTree() throws IOException, JsonProcessingException { ObjectCodec codec = getCodec(); if (codec == null) { throw new IllegalStateException("No ObjectCodec defined for the parser, can not deserialize JSON into JsonNode tree"); } return (T) codec.readTree(this); } /* /********************************************************** /* Internal methods /********************************************************** */ /** * Helper method for constructing {@link JsonParseException}s * based on current state of the parser */ protected JsonParseException _constructError(String msg) { return new JsonParseException(msg, getCurrentLocation()); } /** * 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()); } } JsonProcessingException.java000066400000000000000000000066141215056622200336670ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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) { /* Argh. IOException(Throwable,String) is only available starting * with JDK 1.6... */ 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); } public JsonLocation getLocation() { return _location; } /* /********************************************************** /* Extended API /********************************************************** */ /** * 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(); } /* /********************************************************** /* 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.2.2/src/main/java/com/fasterxml/jackson/core/JsonStreamContext.java000066400000000000000000000066741215056622200325610ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; /** * 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() { } /* /********************************************************** /* 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) */ public final String getTypeDesc() { 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 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(); } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/JsonToken.java000066400000000000000000000114721215056622200310310ustar00rootroot00000000000000/* 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), /** * START_OBJECT is returned when encountering '{' * which signals starting of an Object value. */ START_OBJECT("{"), /** * END_OBJECT is returned when encountering '}' * which signals ending of an Object value */ END_OBJECT("}"), /** * START_ARRAY is returned when encountering '[' * which signals starting of an Array value */ START_ARRAY("["), /** * END_ARRAY is returned when encountering ']' * which signals ending of an Array value */ END_ARRAY("]"), /** * FIELD_NAME is returned when a String token is encountered * as a field name (same lexical value, different function) */ FIELD_NAME(null), /** * 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), /** * 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), /** * 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) */ VALUE_NUMBER_INT(null), /** * VALUE_NUMBER_INT is returned when a numeric token other * that is not an integer is encountered: that is, a number that does * have floating point or exponent marker in it, in addition * to one or more digits. */ VALUE_NUMBER_FLOAT(null), /** * VALUE_TRUE is returned when encountering literal "true" in * value context */ VALUE_TRUE("true"), /** * VALUE_FALSE is returned when encountering literal "false" in * value context */ VALUE_FALSE("false"), /** * VALUE_NULL is returned when encountering literal "null" in * value context */ VALUE_NULL("null") ; final String _serialized; final char[] _serializedChars; final byte[] _serializedBytes; /** * @param token representation for this token, if there is a * single static representation; null otherwise */ JsonToken(String token) { 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]; } } } public String asString() { return _serialized; } public char[] asCharArray() { return _serializedChars; } public byte[] asByteArray() { return _serializedBytes; } public boolean isNumeric() { return (this == VALUE_NUMBER_INT) || (this == VALUE_NUMBER_FLOAT); } /** * 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 boolean isScalarValue() { // note: up to 1.5, VALUE_EMBEDDED_OBJECT was incorrectly considered non-scalar! return ordinal() >= VALUE_EMBEDDED_OBJECT.ordinal(); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/ObjectCodec.java000066400000000000000000000126751215056622200312710ustar00rootroot00000000000000/* 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 { protected ObjectCodec() { } /* /********************************************************** /* 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 jp, Class valueType) throws IOException, JsonProcessingException; /** * 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 jp, TypeReference valueTypeRef) throws IOException, JsonProcessingException; /** * 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 jp, ResolvedType valueType) throws IOException, JsonProcessingException; /** * 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). */ public abstract T readTree(JsonParser jp) throws IOException, JsonProcessingException; /** * Method for reading sequence of Objects from parser stream, * all with same specified value type. */ public abstract Iterator readValues(JsonParser jp, Class valueType) throws IOException, JsonProcessingException; /** * Method for reading sequence of Objects from parser stream, * all with same specified value type. */ public abstract Iterator readValues(JsonParser jp, TypeReference valueTypeRef) throws IOException, JsonProcessingException; /** * Method for reading sequence of Objects from parser stream, * all with same specified value type. */ public abstract Iterator readValues(JsonParser jp, ResolvedType valueType) throws IOException, JsonProcessingException; /* /********************************************************** /* API for serialization (Object-to-JSON) /********************************************************** */ /** * Method to serialize given Java Object, using generator * provided. */ public abstract void writeValue(JsonGenerator jgen, Object value) throws IOException, JsonProcessingException; /* /********************************************************** /* API for Tree Model handling /********************************************************** */ /** * Method for construct root level Object nodes * for Tree Model instances. */ public abstract TreeNode createObjectNode(); /** * Method for construct root level Array nodes * for Tree Model instances. */ 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. */ public abstract JsonParser treeAsTokens(TreeNode n); /** * 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 abstract JsonFactory getJsonFactory(); /** * 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.2.2/src/main/java/com/fasterxml/jackson/core/PrettyPrinter.java000066400000000000000000000143251215056622200317520ustar00rootroot00000000000000/* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; import java.io.IOException; /** * 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 (see [JACKSON-851] for details). * Stateless implementations need not do this; but those are less common. */ public interface PrettyPrinter { /* /********************************************************** /* 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 jg) throws IOException, JsonGenerationException; // // 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 jg) throws IOException, JsonGenerationException; /** * 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 jg, int nrOfEntries) throws IOException, JsonGenerationException; /** * 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 jg) throws IOException, JsonGenerationException; /** * 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 jg) throws IOException, JsonGenerationException; // // // 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 jg) throws IOException, JsonGenerationException; /** * 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 jg, int nrOfValues) throws IOException, JsonGenerationException; /** * 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 jg) throws IOException, JsonGenerationException; /* /********************************************************** /* 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 jg) throws IOException, JsonGenerationException; /** * 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 jg) throws IOException, JsonGenerationException; } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/SerializableString.java000066400000000000000000000106711215056622200327140ustar00rootroot00000000000000/* 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. * * @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 equvalent 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.2.2/src/main/java/com/fasterxml/jackson/core/TreeNode.java000066400000000000000000000201141215056622200306150ustar00rootroot00000000000000/* 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?). */ 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(); /* /********************************************************** /* 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. */ 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). * * @since 2.1 */ JsonParser traverse(ObjectCodec codec); } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/Version.java000066400000000000000000000103741215056622200305440ustar00rootroot00000000000000/* 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; } public boolean isUknownVersion() { return (this == UNKNOWN_VERSION); } public boolean isSnapshot() { return (_snapshotInfo != null && _snapshotInfo.length() > 0); } 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 new StringBuilder() .append(_groupId) .append('/') .append(_artifactId) .append('/') .append(toString()) .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.2.2/src/main/java/com/fasterxml/jackson/core/Versioned.java000066400000000000000000000015501215056622200310510ustar00rootroot00000000000000/* 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.2.2/src/main/java/com/fasterxml/jackson/core/base/000077500000000000000000000000001215056622200271615ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java000066400000000000000000000405411215056622200325510ustar00rootroot00000000000000package com.fasterxml.jackson.core.base; 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.*; import com.fasterxml.jackson.core.json.JsonWriteContext; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.core.util.VersionUtil; /** * This base class implements part of API that a JSON generator exposes * 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 { /* /********************************************************** /* 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 /********************************************************** */ protected GeneratorBase(int features, ObjectCodec codec) { super(); _features = features; _writeContext = JsonWriteContext.createRootContext(); _objectCodec = codec; _cfgNumbersAsStrings = isEnabled(Feature.WRITE_NUMBERS_AS_STRINGS); } /** * Implemented with detection that tries to find "VERSION.txt" in same * package as the implementation class. */ @Override public Version version() { return VersionUtil.versionFor(getClass()); } /* /********************************************************** /* Configuration /********************************************************** */ @Override public JsonGenerator enable(Feature f) { _features |= f.getMask(); if (f == Feature.WRITE_NUMBERS_AS_STRINGS) { _cfgNumbersAsStrings = true; } else if (f == Feature.ESCAPE_NON_ASCII) { setHighestNonEscapedChar(127); } return this; } @Override public JsonGenerator disable(Feature f) { _features &= ~f.getMask(); if (f == Feature.WRITE_NUMBERS_AS_STRINGS) { _cfgNumbersAsStrings = false; } else if (f == Feature.ESCAPE_NON_ASCII) { setHighestNonEscapedChar(0); } return this; } //public JsonGenerator configure(Feature f, boolean state) { } @Override public final boolean isEnabled(Feature f) { return (_features & f.getMask()) != 0; } @Override public JsonGenerator useDefaultPrettyPrinter() { /* 28-Sep-2012, tatu: As per [Issue#84], should not override a * pretty printer if one already assigned. */ if (getPrettyPrinter() != null) { return this; } return setPrettyPrinter(new DefaultPrettyPrinter()); } @Override public JsonGenerator setCodec(ObjectCodec oc) { _objectCodec = oc; return this; } @Override public final ObjectCodec getCodec() { return _objectCodec; } /* /********************************************************** /* Public API, accessors /********************************************************** */ /** * Note: co-variant return type. */ @Override public final JsonWriteContext getOutputContext() { return _writeContext; } /* /********************************************************** /* Public API, write methods, structural /********************************************************** */ //public void writeStartArray() throws IOException, JsonGenerationException //public void writeEndArray() throws IOException, JsonGenerationException //public void writeStartObject() throws IOException, JsonGenerationException //public void writeEndObject() throws IOException, JsonGenerationException /* /********************************************************** /* Public API, write methods, textual /********************************************************** */ @Override public void writeFieldName(SerializableString name) throws IOException, JsonGenerationException { writeFieldName(name.getValue()); } //public abstract void writeString(String text) throws IOException, JsonGenerationException; //public abstract void writeString(char[] text, int offset, int len) throws IOException, JsonGenerationException; //public abstract void writeRaw(String text) throws IOException, JsonGenerationException; //public abstract void writeRaw(char[] text, int offset, int len) throws IOException, JsonGenerationException; @Override public void writeString(SerializableString text) throws IOException, JsonGenerationException { writeString(text.getValue()); } @Override public void writeRawValue(String text) throws IOException, JsonGenerationException { _verifyValueWrite("write raw value"); writeRaw(text); } @Override public void writeRawValue(String text, int offset, int len) throws IOException, JsonGenerationException { _verifyValueWrite("write raw value"); writeRaw(text, offset, len); } @Override public void writeRawValue(char[] text, int offset, int len) throws IOException, JsonGenerationException { _verifyValueWrite("write raw value"); writeRaw(text, offset, len); } @Override public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) throws IOException, JsonGenerationException { // 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, JsonProcessingException { 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, JsonProcessingException { // 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; } /* /********************************************************** /* Public API, copy-through methods /********************************************************** */ @Override public final void copyCurrentEvent(JsonParser jp) throws IOException, JsonProcessingException { JsonToken t = jp.getCurrentToken(); // sanity check; what to do? if (t == null) { _reportError("No current event to copy"); } switch(t) { case START_OBJECT: writeStartObject(); break; case END_OBJECT: writeEndObject(); break; case START_ARRAY: writeStartArray(); break; case END_ARRAY: writeEndArray(); break; case FIELD_NAME: writeFieldName(jp.getCurrentName()); break; case VALUE_STRING: if (jp.hasTextCharacters()) { writeString(jp.getTextCharacters(), jp.getTextOffset(), jp.getTextLength()); } else { writeString(jp.getText()); } break; case VALUE_NUMBER_INT: switch (jp.getNumberType()) { case INT: writeNumber(jp.getIntValue()); break; case BIG_INTEGER: writeNumber(jp.getBigIntegerValue()); break; default: writeNumber(jp.getLongValue()); } break; case VALUE_NUMBER_FLOAT: switch (jp.getNumberType()) { case BIG_DECIMAL: writeNumber(jp.getDecimalValue()); break; case FLOAT: writeNumber(jp.getFloatValue()); break; default: writeNumber(jp.getDoubleValue()); } break; case VALUE_TRUE: writeBoolean(true); break; case VALUE_FALSE: writeBoolean(false); break; case VALUE_NULL: writeNull(); break; case VALUE_EMBEDDED_OBJECT: writeObject(jp.getEmbeddedObject()); break; default: _throwInternal(); } } @Override public final void copyCurrentStructure(JsonParser jp) throws IOException, JsonProcessingException { JsonToken t = jp.getCurrentToken(); // Let's handle field-name separately first if (t == JsonToken.FIELD_NAME) { writeFieldName(jp.getCurrentName()); t = jp.nextToken(); // fall-through to copy the associated value } switch (t) { case START_ARRAY: writeStartArray(); while (jp.nextToken() != JsonToken.END_ARRAY) { copyCurrentStructure(jp); } writeEndArray(); break; case START_OBJECT: writeStartObject(); while (jp.nextToken() != JsonToken.END_OBJECT) { copyCurrentStructure(jp); } writeEndObject(); break; default: // others are simple: copyCurrentEvent(jp); } } /* /********************************************************** /* 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, JsonGenerationException; /** * 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); } /** * 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, JsonGenerationException { /* 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()+")"); } protected final void _throwInternal() { VersionUtil.throwInternal(); } protected void _reportUnsupportedOperation() { throw new UnsupportedOperationException("Operation not supported by generator of type "+getClass().getName()); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java000066400000000000000000001116001215056622200320520ustar00rootroot00000000000000package com.fasterxml.jackson.core.base; import java.io.*; import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.IOContext; import com.fasterxml.jackson.core.io.NumberInput; 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 * * @author Tatu Saloranta */ 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 = 0; /** * Index of character after last available one in the buffer. */ protected int _inputEnd = 0; /* /********************************************************** /* 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 = 0L; /** * 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 = 0; /* /********************************************************** /* 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 = 0; /** * 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 = 0; /* /********************************************************** /* 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 = null; /** * 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 = false; /** * ByteArrayBuilder is needed if 'getBinaryValue' is called. If so, * we better reuse it for remainder of content. */ protected ByteArrayBuilder _byteArrayBuilder = null; /** * 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; /* /********************************************************** /* Constants and fields of former 'JsonNumericParserBase' /********************************************************** */ final protected static int NR_UNKNOWN = 0; // First, integer types final protected static int NR_INT = 0x0001; final protected static int NR_LONG = 0x0002; final protected static int NR_BIGINT = 0x0004; // And then floating point types final protected static int NR_DOUBLE = 0x008; final protected static int NR_BIGDECIMAL = 0x0010; // Also, we need some numeric constants final static BigInteger BI_MIN_INT = BigInteger.valueOf(Integer.MIN_VALUE); final static BigInteger BI_MAX_INT = BigInteger.valueOf(Integer.MAX_VALUE); final static BigInteger BI_MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE); final static BigInteger BI_MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE); final static BigDecimal BD_MIN_LONG = new BigDecimal(BI_MIN_LONG); final static BigDecimal BD_MAX_LONG = new BigDecimal(BI_MAX_LONG); final static BigDecimal BD_MIN_INT = new BigDecimal(BI_MIN_INT); final static BigDecimal BD_MAX_INT = new BigDecimal(BI_MAX_INT); final static long MIN_INT_L = (long) Integer.MIN_VALUE; final static long MAX_INT_L = (long) Integer.MAX_VALUE; // These are not very accurate, but have to do... (for bounds checks) final static double MIN_LONG_D = (double) Long.MIN_VALUE; final static double MAX_LONG_D = (double) Long.MAX_VALUE; final static double MIN_INT_D = (double) Integer.MIN_VALUE; final static double MAX_INT_D = (double) Integer.MAX_VALUE; // Digits, numeric final protected static int INT_0 = '0'; final protected static int INT_1 = '1'; final protected static int INT_2 = '2'; final protected static int INT_3 = '3'; final protected static int INT_4 = '4'; final protected static int INT_5 = '5'; final protected static int INT_6 = '6'; final protected static int INT_7 = '7'; final protected static int INT_8 = '8'; final protected static int INT_9 = '9'; final protected static int INT_MINUS = '-'; final protected static int INT_PLUS = '+'; final protected static int INT_DECIMAL_POINT = '.'; final protected static int INT_e = 'e'; final protected static int INT_E = 'E'; final protected static char CHAR_NULL = '\0'; // 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 = features; _ioContext = ctxt; _textBuffer = ctxt.constructTextBuffer(); _parsingContext = JsonReadContext.createRootContext(); } @Override public Version version() { return PackageVersion.VERSION; } /* /********************************************************** /* JsonParser impl /********************************************************** */ /** * Method that can be called to get the name associated with * the current event. */ @Override public String getCurrentName() throws IOException, JsonParseException { // [JACKSON-395]: start markers require information from parent if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) { JsonReadContext parent = _parsingContext.getParent(); 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(); } ctxt.setCurrentName(name); } @Override public void close() throws IOException { if (!_closed) { _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(_ioContext.getSourceReference(), getTokenCharacterOffset(), 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(_ioContext.getSourceReference(), _currInputProcessed + _inputPtr - 1, _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; } // No embedded objects with base impl... @Override public Object getEmbeddedObject() throws IOException, JsonParseException { return null; } /* /********************************************************** /* 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); } /* /********************************************************** /* Low-level reading, other /********************************************************** */ protected final void loadMoreGuaranteed() throws IOException { if (!loadMore()) { _reportInvalidEOF(); } } /* /********************************************************** /* Abstract methods needed from sub-classes /********************************************************** */ protected abstract boolean loadMore() throws IOException; protected abstract void _finishString() throws IOException, JsonParseException; 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()) { _reportInvalidEOF(": expected close marker for "+_parsingContext.getTypeDesc()+" (from "+_parsingContext.getStartLocation(_ioContext.getSourceReference())+")"); } } /* /********************************************************** /* Internal/package methods: Error reporting /********************************************************** */ protected void _reportMismatchedEndMarker(int actCh, char expCh) throws JsonParseException { String startDesc = ""+_parsingContext.getStartLocation(_ioContext.getSourceReference()); _reportError("Unexpected close marker '"+((char) actCh)+"': expected '"+expCh+"' (for "+_parsingContext.getTypeDesc()+" starting at "+startDesc+")"); } /* /********************************************************** /* 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; } /* /********************************************************** /* Numeric accessors of public API /********************************************************** */ @Override public Number getNumberValue() throws IOException, JsonParseException { 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, JsonParseException { 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, JsonParseException { if ((_numTypesValid & NR_INT) == 0) { if (_numTypesValid == NR_UNKNOWN) { // not parsed at all _parseNumericValue(NR_INT); // will also check event type } 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, JsonParseException { 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, JsonParseException { 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, JsonParseException { 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, JsonParseException { 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, JsonParseException { 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, JsonParseException { // Int or float? if (_currToken == JsonToken.VALUE_NUMBER_INT) { char[] buf = _textBuffer.getTextBuffer(); int offset = _textBuffer.getTextOffset(); int len = _intLength; if (_numberNegative) { ++offset; } if (len <= 9) { // definitely fits in int int i = NumberInput.parseInt(buf, offset, len); _numberInt = _numberNegative ? -i : i; _numTypesValid = NR_INT; return; } if (len <= 18) { // definitely fits AND is easy to parse using 2 int parse calls long l = NumberInput.parseLong(buf, offset, len); if (_numberNegative) { l = -l; } // [JACKSON-230] Could 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; } _parseSlowIntValue(expType, buf, offset, len); return; } if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) { _parseSlowFloatValue(expType); return; } _reportError("Current token ("+_currToken+") not numeric, can not use numeric value accessors"); } private void _parseSlowFloatValue(int expType) throws IOException, JsonParseException { /* 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 '"+_textBuffer.contentsAsString()+"'", nex); } } private void _parseSlowIntValue(int expType, char[] buf, int offset, int len) throws IOException, JsonParseException { String numStr = _textBuffer.contentsAsString(); try { // [JACKSON-230] 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 { // 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 '"+numStr+"'", nex); } } /* /********************************************************** /* Numeric conversions /********************************************************** */ protected void convertNumberToInt() throws IOException, JsonParseException { // 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) { _reportError("Numeric value ("+getText()+") out of range of int"); } _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, JsonParseException { 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, JsonParseException { 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, JsonParseException { /* 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, JsonParseException { /* 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 * would incur */ _numberBigDecimal = new BigDecimal(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((long) _numberInt); } else { _throwInternal(); } _numTypesValid |= NR_BIGDECIMAL; } /* /********************************************************** /* Number handling exceptions /********************************************************** */ protected void reportUnexpectedNumberChar(int ch, String comment) throws JsonParseException { String msg = "Unexpected character ("+_getCharDesc(ch)+") in numeric value"; if (comment != null) { msg += ": "+comment; } _reportError(msg); } protected void reportInvalidNumber(String msg) throws JsonParseException { _reportError("Invalid numeric value: "+msg); } protected void reportOverflowInt() throws IOException, JsonParseException { _reportError("Numeric value ("+getText()+") out of range of int ("+Integer.MIN_VALUE+" - "+Integer.MAX_VALUE+")"); } protected void reportOverflowLong() throws IOException, JsonParseException { _reportError("Numeric value ("+getText()+") out of range of long ("+Long.MIN_VALUE+" - "+Long.MAX_VALUE+")"); } /* /********************************************************** /* 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, JsonParseException { throw new UnsupportedOperationException(); } protected final int _decodeBase64Escape(Base64Variant b64variant, int ch, int index) throws IOException, JsonParseException { // 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) { throw reportInvalidBase64Char(b64variant, unescaped, index); } return bits; } protected final int _decodeBase64Escape(Base64Variant b64variant, char ch, int index) throws IOException, JsonParseException { // 17-May-2011, tatu: As per [JACKSON-xxx], need to handle escaped chars 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) { 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 = "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 (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); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/base/ParserMinimalBase.java000066400000000000000000000472701215056622200333740ustar00rootroot00000000000000package com.fasterxml.jackson.core.base; import java.io.IOException; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.JsonParser.Feature; import com.fasterxml.jackson.core.io.NumberInput; import com.fasterxml.jackson.core.util.ByteArrayBuilder; import com.fasterxml.jackson.core.util.VersionUtil; /** * 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. * * @author Tatu Saloranta */ 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_BACKSLASH = '\\'; protected final static int INT_SLASH = '/'; protected final static int INT_COLON = ':'; protected final static int INT_COMMA = ','; protected final static int INT_ASTERISK = '*'; protected final static int INT_APOSTROPHE = '\''; // Letters we need protected final static int INT_b = 'b'; protected final static int INT_f = 'f'; protected final static int INT_n = 'n'; protected final static int INT_r = 'r'; protected final static int INT_t = 't'; protected final static int INT_u = 'u'; /* /********************************************************** /* 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 * (by call to {@link #clearCurrentToken}) */ 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); } @Override public Version version() { return VersionUtil.versionFor(getClass()); } /* /********************************************************** /* 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, JsonParseException; @Override public JsonToken getCurrentToken() { return _currToken; } @Override public boolean hasCurrentToken() { return _currToken != null; } @Override public JsonToken nextValue() throws IOException, JsonParseException { /* 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, JsonParseException { 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; } switch (t) { case START_OBJECT: case START_ARRAY: ++open; break; case END_OBJECT: case END_ARRAY: if (--open == 0) { return this; } break; } } } /** * 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, JsonParseException; @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, JsonParseException; @Override public abstract char[] getTextCharacters() throws IOException, JsonParseException; @Override public abstract boolean hasTextCharacters(); @Override public abstract int getTextLength() throws IOException, JsonParseException; @Override public abstract int getTextOffset() throws IOException, JsonParseException; /* /********************************************************** /* Public API, access to token information, binary /********************************************************** */ @Override public abstract byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException; /* /********************************************************** /* Public API, access with conversion/coercion /********************************************************** */ @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException, JsonParseException { if (_currToken != null) { switch (_currToken) { case VALUE_NUMBER_INT: return getIntValue() != 0; case VALUE_TRUE: return true; case VALUE_FALSE: case VALUE_NULL: return false; case VALUE_EMBEDDED_OBJECT: { Object value = this.getEmbeddedObject(); if (value instanceof Boolean) { return (Boolean) value; } } case VALUE_STRING: String str = getText().trim(); if ("true".equals(str)) { return true; } break; } } return defaultValue; } @Override public int getValueAsInt(int defaultValue) throws IOException, JsonParseException { if (_currToken != null) { switch (_currToken) { case VALUE_NUMBER_INT: case VALUE_NUMBER_FLOAT: return getIntValue(); case VALUE_TRUE: return 1; case VALUE_FALSE: case VALUE_NULL: return 0; case VALUE_STRING: return NumberInput.parseAsInt(getText(), defaultValue); case VALUE_EMBEDDED_OBJECT: { Object value = this.getEmbeddedObject(); if (value instanceof Number) { return ((Number) value).intValue(); } } } } return defaultValue; } @Override public long getValueAsLong(long defaultValue) throws IOException, JsonParseException { if (_currToken != null) { switch (_currToken) { case VALUE_NUMBER_INT: case VALUE_NUMBER_FLOAT: return getLongValue(); case VALUE_TRUE: return 1; case VALUE_FALSE: case VALUE_NULL: return 0; case VALUE_STRING: return NumberInput.parseAsLong(getText(), defaultValue); case VALUE_EMBEDDED_OBJECT: { Object value = this.getEmbeddedObject(); if (value instanceof Number) { return ((Number) value).longValue(); } } } } return defaultValue; } @Override public double getValueAsDouble(double defaultValue) throws IOException, JsonParseException { if (_currToken != null) { switch (_currToken) { case VALUE_NUMBER_INT: case VALUE_NUMBER_FLOAT: return getDoubleValue(); case VALUE_TRUE: return 1; case VALUE_FALSE: case VALUE_NULL: return 0; case VALUE_STRING: return NumberInput.parseAsDouble(getText(), defaultValue); case VALUE_EMBEDDED_OBJECT: { Object value = this.getEmbeddedObject(); if (value instanceof Number) { return ((Number) value).doubleValue(); } } } } return defaultValue; } @Override public String getValueAsString(String defaultValue) throws IOException, JsonParseException { if (_currToken != JsonToken.VALUE_STRING) { 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, JsonParseException { int ptr = 0; int len = str.length(); main_loop: while (ptr < len) { // first, we'll skip preceding white space, if any char ch; do { ch = str.charAt(ptr++); if (ptr >= len) { break main_loop; } } while (ch <= INT_SPACE); int bits = b64variant.decodeBase64Char(ch); if (bits < 0) { _reportInvalidBase64(b64variant, 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 = b64variant.decodeBase64Char(ch); if (bits < 0) { _reportInvalidBase64(b64variant, 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 (!b64variant.usesPadding()) { decodedData >>= 4; builder.append(decodedData); break; } _reportBase64EOF(); } ch = str.charAt(ptr++); bits = b64variant.decodeBase64Char(ch); // First branch: can get padding (-> 1 byte) if (bits < 0) { if (bits != Base64Variant.BASE64_VALUE_PADDING) { _reportInvalidBase64(b64variant, ch, 2, null); } // Ok, must get padding if (ptr >= len) { _reportBase64EOF(); } ch = str.charAt(ptr++); if (!b64variant.usesPaddingChar(ch)) { _reportInvalidBase64(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 (ptr >= len) { // but as per [JACKSON-631] can be end-of-input, iff not using padding if (!b64variant.usesPadding()) { decodedData >>= 2; builder.appendTwoBytes(decodedData); break; } _reportBase64EOF(); } ch = str.charAt(ptr++); bits = b64variant.decodeBase64Char(ch); if (bits < 0) { if (bits != Base64Variant.BASE64_VALUE_PADDING) { _reportInvalidBase64(b64variant, ch, 3, null); } decodedData >>= 2; builder.appendTwoBytes(decodedData); } else { // otherwise, our triple is now complete decodedData = (decodedData << 6) | bits; builder.appendThreeBytes(decodedData); } } } /** * @param bindex Relative index within base64 character unit; between 0 * and 3 (as unit has exactly 4 characters) */ protected void _reportInvalidBase64(Base64Variant b64variant, char ch, int bindex, String msg) throws JsonParseException { 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 (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 '"+ch+"' (code 0x"+Integer.toHexString(ch)+") in base64 content"; } if (msg != null) { base = base + ": " + msg; } throw _constructError(base); } protected void _reportBase64EOF() throws JsonParseException { throw _constructError("Unexpected end-of-String in base64 content"); } /* /********************************************************** /* Error reporting /********************************************************** */ protected void _reportUnexpectedChar(int ch, String comment) throws JsonParseException { String msg = "Unexpected character ("+_getCharDesc(ch)+")"; if (comment != null) { msg += ": "+comment; } _reportError(msg); } protected void _reportInvalidEOF() throws JsonParseException { _reportInvalidEOF(" in "+_currToken); } protected void _reportInvalidEOF(String msg) throws JsonParseException { _reportError("Unexpected end-of-input"+msg); } protected void _reportInvalidEOFInValue() throws JsonParseException { _reportInvalidEOF(" in a value"); } 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); } /** * Method called to report a problem with unquoted control character. * Note: starting with version 1.4, it is possible to suppress * exception by enabling {@link Feature#ALLOW_UNQUOTED_CONTROL_CHARS}. */ 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); } } 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; } /* /********************************************************** /* 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); } 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(msg, getCurrentLocation(), t); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/base/package-info.java000066400000000000000000000006231215056622200323510ustar00rootroot00000000000000/** * 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.2.2/src/main/java/com/fasterxml/jackson/core/format/000077500000000000000000000000001215056622200275375ustar00rootroot00000000000000DataFormatDetector.java000066400000000000000000000162011215056622200340370ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.java000066400000000000000000000074031215056622200336550ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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; } /* /********************************************************** /* 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.2.2/src/main/java/com/fasterxml/jackson/core/format/InputAccessor.java000066400000000000000000000104141215056622200331640ustar00rootroot00000000000000package 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.2.2/src/main/java/com/fasterxml/jackson/core/format/MatchStrength.java000066400000000000000000000050261215056622200331600ustar00rootroot00000000000000package 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.2.2/src/main/java/com/fasterxml/jackson/core/format/package-info.java000066400000000000000000000003371215056622200327310ustar00rootroot00000000000000/** * 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.2.2/src/main/java/com/fasterxml/jackson/core/io/000077500000000000000000000000001215056622200266565ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/io/BaseReader.java000066400000000000000000000056211215056622200315220ustar00rootroot00000000000000 package com.fasterxml.jackson.core.io; import java.io.*; /** * Simple basic class for optimized readers in this package; implements * "cookie-cutter" methods that are used by all actual implementations. */ abstract class BaseReader 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 NULL_CHAR = (char) 0; final protected static char NULL_BYTE = (byte) 0; final protected IOContext _context; protected InputStream _in; protected byte[] _buffer; protected int _ptr; protected int _length; /* /********************************************************** /* Life-cycle /********************************************************** */ protected BaseReader(IOContext context, InputStream in, byte[] buf, int ptr, int len) { _context = context; _in = in; _buffer = buf; _ptr = ptr; _length = len; } /* /********************************************************** /* Reader API /********************************************************** */ @Override public void close() throws IOException { InputStream in = _in; if (in != null) { _in = null; freeBuffers(); in.close(); } } protected char[] _tmpBuf = null; /** * 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]; } /* /********************************************************** /* Internal/package methods: /********************************************************** */ /** * 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). */ public final void freeBuffers() { byte[] buf = _buffer; if (buf != null) { _buffer = null; _context.releaseReadIOBuffer(buf); } } protected void reportBounds(char[] cbuf, int start, int len) throws IOException { throw new ArrayIndexOutOfBoundsException("read(buf,"+start+","+len+"), cbuf["+cbuf.length+"]"); } protected void reportStrangeStream() throws IOException { throw new IOException("Strange I/O stream, returned 0 bytes on read"); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/io/CharTypes.java000066400000000000000000000170471215056622200314340ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.util.Arrays; public final class CharTypes { private final static char[] HEX_CHARS = "0123456789ABCDEF".toCharArray(); private final static byte[] HEX_BYTES; static { int len = HEX_CHARS.length; HEX_BYTES = new byte[len]; for (int i = 0; i < len; ++i) { HEX_BYTES[i] = (byte) HEX_CHARS[i]; } } /** * Lookup table used for determining which input characters * need special handling when contained in text segment. */ 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 */ 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. */ final static int[] sInputCodesUtf8; static { int[] table = new int[sInputCodes.length]; System.arraycopy(sInputCodes, 0, table, 0, sInputCodes.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 */ final static int[] sInputCodesJsNames; static { 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 */ final static int[] sInputCodesUtf8JsNames; static { int[] table = new int[256]; // start with 8-bit JS names System.arraycopy(sInputCodesJsNames, 0, table, 0, sInputCodesJsNames.length); Arrays.fill(table, 128, 128, 0); sInputCodesUtf8JsNames = table; } /** * Decoding table used to quickly determine characters that are * relevant within comment content */ final static int[] sInputCodesComment = new int[256]; static { // but first: let's start with UTF-8 multi-byte markers: System.arraycopy(sInputCodesUtf8, 128, sInputCodesComment, 128, 128); // default (0) means "ok" (skip); -1 invalid, others marked by char itself Arrays.fill(sInputCodesComment, 0, 32, -1); // invalid white space sInputCodesComment['\t'] = 0; // tab is still fine sInputCodesComment['\n'] = '\n'; // lf/cr need to be observed, ends cpp comment sInputCodesComment['\r'] = '\r'; sInputCodesComment['*'] = '*'; // end marker for c-style comments } /** * Lookup table used for determining which output characters in * 7-bit ASCII range need to be quoted. */ 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 constants 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 128 Unicode characters (7-bit ASCII) * range. For actual hex digits, contains corresponding value; * for others -1. */ final static int[] sHexValues = new int[128]; 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; } /** * 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; } public static int charToHex(int ch) { return (ch > 127) ? -1 : sHexValues[ch]; } 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) // We know that it has to fit in just 2 hex chars sb.append('u'); sb.append('0'); sb.append('0'); int value = -(escCode + 1); sb.append(HEX_CHARS[value >> 4]); sb.append(HEX_CHARS[value & 0xF]); } else { // "named", i.e. prepend with slash sb.append((char) escCode); } } } public static char[] copyHexChars() { return (char[]) HEX_CHARS.clone(); } public static byte[] copyHexBytes() { return (byte[]) HEX_BYTES.clone(); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/io/CharacterEscapes.java000066400000000000000000000055121215056622200327240ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; 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. */ public abstract class CharacterEscapes implements java.io.Serializable // since 2.1 { private static final long serialVersionUID = 1L; /** * 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(); int len = esc.length; int[] result = new int[len]; System.arraycopy(esc, 0, result, 0, esc.length); return result; } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/io/IOContext.java000066400000000000000000000201451215056622200313770ustar00rootroot00000000000000package 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. */ public final 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 = null; /** * Reference to the allocated I/O buffer used for low-level * encoding-related buffering. */ protected byte[] _writeEncodingBuffer = null; /** * Reference to the buffer allocated for temporary use with * base64 encoding or decoding. */ protected byte[] _base64Buffer = null; /** * 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 = null; /** * 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 = null; /** * 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 = null; /* /********************************************************** /* Life-cycle /********************************************************** */ public IOContext(BufferRecycler br, Object sourceRef, boolean managedResource) { _bufferRecycler = br; _sourceRef = sourceRef; _managedResource = managedResource; } public void setEncoding(JsonEncoding enc) { _encoding = enc; } /* /********************************************************** /* 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.ByteBufferType.READ_IO_BUFFER)); } public byte[] allocWriteEncodingBuffer() { _verifyAlloc(_writeEncodingBuffer); return (_writeEncodingBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.ByteBufferType.WRITE_ENCODING_BUFFER)); } /** * @since 2.1 */ public byte[] allocBase64Buffer() { _verifyAlloc(_base64Buffer); return (_base64Buffer = _bufferRecycler.allocByteBuffer(BufferRecycler.ByteBufferType.BASE64_CODEC_BUFFER)); } public char[] allocTokenBuffer() { _verifyAlloc(_tokenCBuffer); return (_tokenCBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CharBufferType.TOKEN_BUFFER)); } public char[] allocConcatBuffer() { _verifyAlloc(_concatCBuffer); return (_concatCBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CharBufferType.CONCAT_BUFFER)); } public char[] allocNameCopyBuffer(int minSize) { _verifyAlloc(_nameCopyBuffer); return (_nameCopyBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CharBufferType.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.ByteBufferType.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.ByteBufferType.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.ByteBufferType.BASE64_CODEC_BUFFER, buf); } } public void releaseTokenBuffer(char[] buf) { if (buf != null) { _verifyRelease(buf, _tokenCBuffer); _tokenCBuffer = null; _bufferRecycler.releaseCharBuffer(BufferRecycler.CharBufferType.TOKEN_BUFFER, buf); } } public void releaseConcatBuffer(char[] buf) { if (buf != null) { _verifyRelease(buf, _concatCBuffer); _concatCBuffer = null; _bufferRecycler.releaseCharBuffer(BufferRecycler.CharBufferType.CONCAT_BUFFER, buf); } } public void releaseNameCopyBuffer(char[] buf) { if (buf != null) { _verifyRelease(buf, _nameCopyBuffer); _nameCopyBuffer = null; _bufferRecycler.releaseCharBuffer(BufferRecycler.CharBufferType.NAME_COPY_BUFFER, buf); } } /* /********************************************************** /* Internal helpers /********************************************************** */ private final void _verifyAlloc(Object buffer) { if (buffer != null) { throw new IllegalStateException("Trying to call same allocXxx() method second time"); } } private final void _verifyRelease(Object toRelease, Object src) { if (toRelease != src) { throw new IllegalArgumentException("Trying to release buffer not owned by the context"); } } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/io/InputDecorator.java000066400000000000000000000057401215056622200324710ustar00rootroot00000000000000package 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 passed in argument, or something that * calls it */ 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 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 src Original input source * * @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 src) throws IOException; } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/io/JsonStringEncoder.java000066400000000000000000000362531215056622200331320ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.lang.ref.SoftReference; import com.fasterxml.jackson.core.util.BufferRecycler; 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 { private final static char[] HEX_CHARS = CharTypes.copyHexChars(); private final static byte[] HEX_BYTES = 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 INT_BACKSLASH = '\\'; private final static int INT_U = 'u'; private final static int INT_0 = '0'; /** * 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> _threadEncoder = new ThreadLocal>(); /** * Lazily constructed text buffer used to produce JSON encoded Strings * as characters (without UTF-8 encoding) */ protected TextBuffer _textBuffer; /** * Lazily-constructed builder used for UTF-8 encoding of text values * (quoted and unquoted) */ protected ByteArrayBuilder _byteBuilder; /** * Temporary buffer used for composing quote/escape sequences */ protected final char[] _quoteBuffer; /* /********************************************************** /* Construction, instance access /********************************************************** */ public JsonStringEncoder() { _quoteBuffer = new char[6]; _quoteBuffer[0] = '\\'; _quoteBuffer[2] = '0'; _quoteBuffer[3] = '0'; } /** * Factory method for getting an instance; this is either recycled per-thread instance, * or a newly constructed one. */ public static JsonStringEncoder getInstance() { SoftReference ref = _threadEncoder.get(); JsonStringEncoder enc = (ref == null) ? null : ref.get(); if (enc == null) { enc = new JsonStringEncoder(); _threadEncoder.set(new SoftReference(enc)); } return enc; } /* /********************************************************** /* Public API /********************************************************** */ /** * Method that will quote text contents using JSON standard quoting, * and return results as a character array */ public char[] quoteAsString(String input) { TextBuffer textBuffer = _textBuffer; if (textBuffer == null) { // no allocator; can add if we must, shouldn't need to _textBuffer = textBuffer = new TextBuffer(null); } char[] outputBuffer = textBuffer.emptyAndGetCurrentSegment(); final int[] escCodes = CharTypes.get7BitOutputEscapes(); final int escCodeCount = escCodes.length; int inPtr = 0; final int inputLen = input.length(); int outPtr = 0; outer_loop: 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) { outputBuffer = textBuffer.finishCurrentSegment(); outPtr = 0; } outputBuffer[outPtr++] = c; if (++inPtr >= inputLen) { break outer_loop; } } // something to escape; 2 or 6-char variant? char d = input.charAt(inPtr++); int escCode = escCodes[d]; int length = (escCode < 0) ? _appendNumericEscape(d, _quoteBuffer) : _appendNamedEscape(escCode, _quoteBuffer); ; if ((outPtr + length) > outputBuffer.length) { int first = outputBuffer.length - outPtr; if (first > 0) { System.arraycopy(_quoteBuffer, 0, outputBuffer, outPtr, first); } outputBuffer = textBuffer.finishCurrentSegment(); int second = length - first; System.arraycopy(_quoteBuffer, first, outputBuffer, 0, second); outPtr = second; } else { System.arraycopy(_quoteBuffer, 0, outputBuffer, outPtr, length); outPtr += length; } } textBuffer.setCurrentLength(outPtr); return textBuffer.contentsAsArray(); } /** * Will quote given JSON String value using standard quoting, encode * results as UTF-8, and return result as a byte array. */ public byte[] quoteAsUTF8(String text) { ByteArrayBuilder byteBuilder = _byteBuilder; if (byteBuilder == null) { // no allocator; can add if we must, shouldn't need to _byteBuilder = byteBuilder = new ByteArrayBuilder(null); } int inputPtr = 0; int inputEnd = text.length(); int outputPtr = 0; byte[] outputBuffer = byteBuilder.resetAndGetFirstSegment(); main_loop: 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) { outputBuffer = byteBuilder.finishCurrentSegment(); outputPtr = 0; } outputBuffer[outputPtr++] = (byte) ch; if (++inputPtr >= inputEnd) { break main_loop; } } if (outputPtr >= outputBuffer.length) { outputBuffer = byteBuilder.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 = _appendByteEscape(ch, escape, byteBuilder, outputPtr); outputBuffer = byteBuilder.getCurrentSegment(); continue main_loop; } else 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 = byteBuilder.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 _illegalSurrogate(ch); } // and if so, followed by another from next range if (inputPtr >= inputEnd) { _illegalSurrogate(ch); } ch = _convertSurrogate(ch, text.charAt(inputPtr++)); if (ch > 0x10FFFF) { // illegal, as per RFC 4627 _illegalSurrogate(ch); } outputBuffer[outputPtr++] = (byte) (0xf0 | (ch >> 18)); if (outputPtr >= outputBuffer.length) { outputBuffer = byteBuilder.finishCurrentSegment(); outputPtr = 0; } outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 12) & 0x3f)); if (outputPtr >= outputBuffer.length) { outputBuffer = byteBuilder.finishCurrentSegment(); outputPtr = 0; } outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f)); ch = (0x80 | (ch & 0x3f)); } } if (outputPtr >= outputBuffer.length) { outputBuffer = byteBuilder.finishCurrentSegment(); outputPtr = 0; } outputBuffer[outputPtr++] = (byte) ch; } return _byteBuilder.completeAndCoalesce(outputPtr); } /** * Will encode given String as UTF-8 (without any quoting), return * resulting byte array. */ @SuppressWarnings("resource") public byte[] encodeAsUTF8(String text) { ByteArrayBuilder byteBuilder = _byteBuilder; if (byteBuilder == null) { // no allocator; can add if we must, shouldn't need to _byteBuilder = byteBuilder = new ByteArrayBuilder(null); } int inputPtr = 0; int inputEnd = text.length(); int outputPtr = 0; byte[] outputBuffer = byteBuilder.resetAndGetFirstSegment(); int outputEnd = outputBuffer.length; main_loop: while (inputPtr < inputEnd) { int c = text.charAt(inputPtr++); // first tight loop for ascii while (c <= 0x7F) { if (outputPtr >= outputEnd) { outputBuffer = byteBuilder.finishCurrentSegment(); outputEnd = outputBuffer.length; outputPtr = 0; } outputBuffer[outputPtr++] = (byte) c; if (inputPtr >= inputEnd) { break main_loop; } c = text.charAt(inputPtr++); } // then multi-byte... if (outputPtr >= outputEnd) { outputBuffer = byteBuilder.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 = byteBuilder.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 _illegalSurrogate(c); } // and if so, followed by another from next range if (inputPtr >= inputEnd) { _illegalSurrogate(c); } c = _convertSurrogate(c, text.charAt(inputPtr++)); if (c > 0x10FFFF) { // illegal, as per RFC 4627 _illegalSurrogate(c); } outputBuffer[outputPtr++] = (byte) (0xf0 | (c >> 18)); if (outputPtr >= outputEnd) { outputBuffer = byteBuilder.finishCurrentSegment(); outputEnd = outputBuffer.length; outputPtr = 0; } outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); if (outputPtr >= outputEnd) { outputBuffer = byteBuilder.finishCurrentSegment(); outputEnd = outputBuffer.length; outputPtr = 0; } outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); } } if (outputPtr >= outputEnd) { outputBuffer = byteBuilder.finishCurrentSegment(); outputEnd = outputBuffer.length; outputPtr = 0; } outputBuffer[outputPtr++] = (byte) (0x80 | (c & 0x3f)); } return _byteBuilder.completeAndCoalesce(outputPtr); } /* /********************************************************** /* Internal methods /********************************************************** */ private int _appendNumericEscape(int value, char[] quoteBuffer) { quoteBuffer[1] = 'u'; // We know it's a control char, so only the last 2 chars are non-0 quoteBuffer[4] = HEX_CHARS[value >> 4]; quoteBuffer[5] = HEX_CHARS[value & 0xF]; return 6; } private int _appendNamedEscape(int escCode, char[] quoteBuffer) { quoteBuffer[1] = (char) escCode; return 2; } private int _appendByteEscape(int ch, int escCode, ByteArrayBuilder byteBuilder, int ptr) { byteBuilder.setCurrentSegmentLength(ptr); byteBuilder.append(INT_BACKSLASH); if (escCode < 0) { // standard escape byteBuilder.append(INT_U); if (ch > 0xFF) { int hi = (ch >> 8); byteBuilder.append(HEX_BYTES[hi >> 4]); byteBuilder.append(HEX_BYTES[hi & 0xF]); ch &= 0xFF; } else { byteBuilder.append(INT_0); byteBuilder.append(INT_0); } byteBuilder.append(HEX_BYTES[ch >> 4]); byteBuilder.append(HEX_BYTES[ch & 0xF]); } else { // 2-char simple escape byteBuilder.append((byte) escCode); } return byteBuilder.getCurrentSegmentLength(); } protected static int _convertSurrogate(int firstPart, int secondPart) { // Ok, then, is the second part valid? if (secondPart < SURR2_FIRST || secondPart > SURR2_LAST) { throw new IllegalArgumentException("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) { throw new IllegalArgumentException(UTF8Writer.illegalSurrogateDesc(code)); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/io/MergedStream.java000066400000000000000000000063001215056622200320770ustar00rootroot00000000000000package 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 protected IOContext _context; final InputStream _in; byte[] _buffer; int _ptr; final int _end; public MergedStream(IOContext context, InputStream in, byte[] buf, int start, int end) { _context = context; _in = in; _buffer = buf; _ptr = start; _end = end; } @Override public int available() throws IOException { if (_buffer != null) { return _end - _ptr; } return _in.available(); } @Override public void close() throws IOException { freeMergedBuffer(); _in.close(); } @Override public void mark(int readlimit) { if (_buffer == null) { _in.mark(readlimit); } } @Override public boolean markSupported() { // Only supports marks past the initial rewindable section... return (_buffer == null) && _in.markSupported(); } @Override public int read() throws IOException { if (_buffer != null) { int c = _buffer[_ptr++] & 0xFF; if (_ptr >= _end) { freeMergedBuffer(); } 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 (_buffer != null) { int avail = _end - _ptr; if (len > avail) { len = avail; } System.arraycopy(_buffer, _ptr, b, off, len); _ptr += len; if (_ptr >= _end) { freeMergedBuffer(); } return len; } return _in.read(b, off, len); } @Override public void reset() throws IOException { if (_buffer == null) { _in.reset(); } } @Override public long skip(long n) throws IOException { long count = 0L; if (_buffer != null) { int amount = _end - _ptr; if (amount > n) { // all in pushed back segment? _ptr += (int) n; return n; } freeMergedBuffer(); count += amount; n -= amount; } if (n > 0) { count += _in.skip(n); } return count; } private void freeMergedBuffer() { byte[] buf = _buffer; if (buf != null) { _buffer = null; if (_context != null) { _context.releaseReadIOBuffer(buf); } } } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java000066400000000000000000000235421215056622200317770ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; 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[] digitChars, int offset, int len) { int num = digitChars[offset] - '0'; len += offset; // This looks ugly, but appears the fastest way (as per measurements) if (++offset < len) { num = (num * 10) + (digitChars[offset] - '0'); if (++offset < len) { num = (num * 10) + (digitChars[offset] - '0'); if (++offset < len) { num = (num * 10) + (digitChars[offset] - '0'); if (++offset < len) { num = (num * 10) + (digitChars[offset] - '0'); if (++offset < len) { num = (num * 10) + (digitChars[offset] - '0'); if (++offset < len) { num = (num * 10) + (digitChars[offset] - '0'); if (++offset < len) { num = (num * 10) + (digitChars[offset] - '0'); if (++offset < len) { num = (num * 10) + (digitChars[offset] - '0'); } } } } } } } } return num; } /** * Helper method to (more) efficiently parse integer numbers from * String values. */ public static int parseInt(String str) { /* 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 = str.charAt(0); int length = str.length(); boolean negative = (c == '-'); int offset = 1; // must have 1 - 9 digits after optional sign: // negative? if (negative) { if (length == 1 || length > 10) { return Integer.parseInt(str); } c = str.charAt(offset++); } else { if (length > 9) { return Integer.parseInt(str); } } if (c > '9' || c < '0') { return Integer.parseInt(str); } int num = c - '0'; if (offset < length) { c = str.charAt(offset++); if (c > '9' || c < '0') { return Integer.parseInt(str); } num = (num * 10) + (c - '0'); if (offset < length) { c = str.charAt(offset++); if (c > '9' || c < '0') { return Integer.parseInt(str); } num = (num * 10) + (c - '0'); // Let's just loop if we have more than 3 digits: if (offset < length) { do { c = str.charAt(offset++); if (c > '9' || c < '0') { return Integer.parseInt(str); } num = (num * 10) + (c - '0'); } while (offset < length); } } } return negative ? -num : num; } public static long parseLong(char[] digitChars, int offset, int len) { // Note: caller must ensure length is [10, 18] int len1 = len-9; long val = parseInt(digitChars, offset, len1) * L_BILLION; return val + (long) parseInt(digitChars, offset+len1, 9); } public static long parseLong(String str) { /* 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 = str.length(); if (length <= 9) { return (long) parseInt(str); } // !!! TODO: implement efficient 2-int parsing... return Long.parseLong(str); } /** * 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[] digitChars, int offset, 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 = digitChars[offset+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 numberStr, boolean negative) { String cmpStr = negative ? MIN_LONG_STR_NO_SIGN : MAX_LONG_STR; int cmpLen = cmpStr.length(); int actualLen = numberStr.length(); if (actualLen < cmpLen) return true; if (actualLen > cmpLen) return false; // could perhaps just use String.compareTo()? for (int i = 0; i < cmpLen; ++i) { int diff = numberStr.charAt(i) - cmpStr.charAt(i); if (diff != 0) { return (diff < 0); } } return true; } public static int parseAsInt(String input, int defaultValue) { if (input == null) { return defaultValue; } input = input.trim(); int len = input.length(); if (len == 0) { return defaultValue; } // One more thing: use integer parsing for 'simple' int i = 0; if (i < len) { // skip leading sign: char c = input.charAt(0); if (c == '+') { // for plus, actually physically remove input = input.substring(1); len = input.length(); } else if (c == '-') { // minus, just skip for checks, must retain ++i; } } for (; i < len; ++i) { char c = input.charAt(i); // if other symbols, parse as Double, coerce if (c > '9' || c < '0') { try { return (int) parseDouble(input); } catch (NumberFormatException e) { return defaultValue; } } } try { return Integer.parseInt(input); } catch (NumberFormatException e) { } return defaultValue; } public static long parseAsLong(String input, long defaultValue) { if (input == null) { return defaultValue; } input = input.trim(); int len = input.length(); if (len == 0) { return defaultValue; } // One more thing: use long parsing for 'simple' int i = 0; if (i < len) { // skip leading sign: char c = input.charAt(0); if (c == '+') { // for plus, actually physically remove input = input.substring(1); len = input.length(); } else if (c == '-') { // minus, just skip for checks, must retain ++i; } } for (; i < len; ++i) { char c = input.charAt(i); // if other symbols, parse as Double, coerce if (c > '9' || c < '0') { try { return (long) parseDouble(input); } catch (NumberFormatException e) { return defaultValue; } } } try { return Long.parseLong(input); } catch (NumberFormatException e) { } return defaultValue; } public static double parseAsDouble(String input, double defaultValue) { if (input == null) { return defaultValue; } input = input.trim(); int len = input.length(); if (len == 0) { return defaultValue; } try { return parseDouble(input); } catch (NumberFormatException e) { } return defaultValue; } public static double parseDouble(String numStr) 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(numStr)) { return Double.MIN_VALUE; } return Double.parseDouble(numStr); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/io/NumberOutput.java000066400000000000000000000322361215056622200322000ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; public final class NumberOutput { private final static char NULL_CHAR = (char) 0; private static int MILLION = 1000000; private static int BILLION = 1000000000; private static long TEN_BILLION_L = 10000000000L; private static long THOUSAND_L = 1000L; private static long MIN_INT_AS_LONG = (long) Integer.MIN_VALUE; private static long MAX_INT_AS_LONG = (long) Integer.MAX_VALUE; final static String SMALLEST_LONG = String.valueOf(Long.MIN_VALUE); final static char[] LEADING_TRIPLETS = new char[4000]; final static char[] FULL_TRIPLETS = new char[4000]; static { /* Let's fill it with NULLs for ignorable leading digits, * and digit chars for others */ int ix = 0; for (int i1 = 0; i1 < 10; ++i1) { char f1 = (char) ('0' + i1); char l1 = (i1 == 0) ? NULL_CHAR : f1; for (int i2 = 0; i2 < 10; ++i2) { char f2 = (char) ('0' + i2); char l2 = (i1 == 0 && i2 == 0) ? NULL_CHAR : f2; for (int i3 = 0; i3 < 10; ++i3) { // Last is never to be empty char f3 = (char) ('0' + i3); LEADING_TRIPLETS[ix] = l1; LEADING_TRIPLETS[ix+1] = l2; LEADING_TRIPLETS[ix+2] = f3; FULL_TRIPLETS[ix] = f1; FULL_TRIPLETS[ix+1] = f2; FULL_TRIPLETS[ix+2] = f3; ix += 4; } } } } final static byte[] FULL_TRIPLETS_B = new byte[4000]; static { for (int i = 0; i < 4000; ++i) { FULL_TRIPLETS_B[i] = (byte) FULL_TRIPLETS[i]; } } final static String[] sSmallIntStrs = new String[] { "0","1","2","3","4","5","6","7","8","9","10" }; 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 value, char[] buffer, int offset) { if (value < 0) { if (value == Integer.MIN_VALUE) { /* Special case: no matching positive value within range; * let's then "upgrade" to long and output as such. */ return outputLong((long) value, buffer, offset); } buffer[offset++] = '-'; value = -value; } if (value < MILLION) { // at most 2 triplets... if (value < 1000) { if (value < 10) { buffer[offset++] = (char) ('0' + value); } else { offset = outputLeadingTriplet(value, buffer, offset); } } else { int thousands = value / 1000; value -= (thousands * 1000); // == value % 1000 offset = outputLeadingTriplet(thousands, buffer, offset); offset = outputFullTriplet(value, buffer, offset); } return offset; } // 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. */ boolean hasBillions = (value >= BILLION); if (hasBillions) { value -= BILLION; if (value >= BILLION) { value -= BILLION; buffer[offset++] = '2'; } else { buffer[offset++] = '1'; } } int newValue = value / 1000; int ones = (value - (newValue * 1000)); // == value % 1000 value = newValue; newValue /= 1000; int thousands = (value - (newValue * 1000)); // value now has millions, which have 1, 2 or 3 digits if (hasBillions) { offset = outputFullTriplet(newValue, buffer, offset); } else { offset = outputLeadingTriplet(newValue, buffer, offset); } offset = outputFullTriplet(thousands, buffer, offset); offset = outputFullTriplet(ones, buffer, offset); return offset; } public static int outputInt(int value, byte[] buffer, int offset) { if (value < 0) { if (value == Integer.MIN_VALUE) { return outputLong((long) value, buffer, offset); } buffer[offset++] = '-'; value = -value; } if (value < MILLION) { // at most 2 triplets... if (value < 1000) { if (value < 10) { buffer[offset++] = (byte) ('0' + value); } else { offset = outputLeadingTriplet(value, buffer, offset); } } else { int thousands = value / 1000; value -= (thousands * 1000); // == value % 1000 offset = outputLeadingTriplet(thousands, buffer, offset); offset = outputFullTriplet(value, buffer, offset); } return offset; } boolean hasBillions = (value >= BILLION); if (hasBillions) { value -= BILLION; if (value >= BILLION) { value -= BILLION; buffer[offset++] = '2'; } else { buffer[offset++] = '1'; } } int newValue = value / 1000; int ones = (value - (newValue * 1000)); // == value % 1000 value = newValue; newValue /= 1000; int thousands = (value - (newValue * 1000)); if (hasBillions) { offset = outputFullTriplet(newValue, buffer, offset); } else { offset = outputLeadingTriplet(newValue, buffer, offset); } offset = outputFullTriplet(thousands, buffer, offset); offset = outputFullTriplet(ones, buffer, offset); return offset; } /** * @return Offset within buffer after outputting int */ public static int outputLong(long value, char[] buffer, int offset) { // First: does it actually fit in an int? if (value < 0L) { /* MIN_INT is actually printed as long, just because its * negation is not an int but long */ if (value > MIN_INT_AS_LONG) { return outputInt((int) value, buffer, offset); } if (value == Long.MIN_VALUE) { // Special case: no matching positive value within range int len = SMALLEST_LONG.length(); SMALLEST_LONG.getChars(0, len, buffer, offset); return (offset + len); } buffer[offset++] = '-'; value = -value; } else { if (value <= MAX_INT_AS_LONG) { return outputInt((int) value, buffer, offset); } } /* Ok: real long print. Need to first figure out length * in characters, and then print in from end to beginning */ int origOffset = offset; offset += calcLongStrLength(value); int ptr = offset; // First, with long arithmetics: while (value > MAX_INT_AS_LONG) { // full triplet ptr -= 3; long newValue = value / THOUSAND_L; int triplet = (int) (value - newValue * THOUSAND_L); outputFullTriplet(triplet, buffer, ptr); value = newValue; } // Then with int arithmetics: int ivalue = (int) value; while (ivalue >= 1000) { // still full triplet ptr -= 3; int newValue = ivalue / 1000; int triplet = ivalue - (newValue * 1000); outputFullTriplet(triplet, buffer, ptr); ivalue = newValue; } // And finally, if anything remains, partial triplet outputLeadingTriplet(ivalue, buffer, origOffset); return offset; } public static int outputLong(long value, byte[] buffer, int offset) { if (value < 0L) { if (value > MIN_INT_AS_LONG) { return outputInt((int) value, buffer, offset); } if (value == Long.MIN_VALUE) { // Special case: no matching positive value within range int len = SMALLEST_LONG.length(); for (int i = 0; i < len; ++i) { buffer[offset++] = (byte) SMALLEST_LONG.charAt(i); } return offset; } buffer[offset++] = '-'; value = -value; } else { if (value <= MAX_INT_AS_LONG) { return outputInt((int) value, buffer, offset); } } int origOffset = offset; offset += calcLongStrLength(value); int ptr = offset; // First, with long arithmetics: while (value > MAX_INT_AS_LONG) { // full triplet ptr -= 3; long newValue = value / THOUSAND_L; int triplet = (int) (value - newValue * THOUSAND_L); outputFullTriplet(triplet, buffer, ptr); value = newValue; } // Then with int arithmetics: int ivalue = (int) value; while (ivalue >= 1000) { // still full triplet ptr -= 3; int newValue = ivalue / 1000; int triplet = ivalue - (newValue * 1000); outputFullTriplet(triplet, buffer, ptr); ivalue = newValue; } outputLeadingTriplet(ivalue, buffer, origOffset); return offset; } /* /********************************************************** /* Secondary 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 value) { // Lookup table for small values if (value < sSmallIntStrs.length) { if (value >= 0) { return sSmallIntStrs[value]; } int v2 = -value - 1; if (v2 < sSmallIntStrs2.length) { return sSmallIntStrs2[v2]; } } return Integer.toString(value); } public static String toString(long value) { if (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) { return toString((int) value); } return Long.toString(value); } public static String toString(double value) { return Double.toString(value); } /* /********************************************************** /* Internal methods /********************************************************** */ private static int outputLeadingTriplet(int triplet, char[] buffer, int offset) { int digitOffset = (triplet << 2); char c = LEADING_TRIPLETS[digitOffset++]; if (c != NULL_CHAR) { buffer[offset++] = c; } c = LEADING_TRIPLETS[digitOffset++]; if (c != NULL_CHAR) { buffer[offset++] = c; } // Last is required to be non-empty buffer[offset++] = LEADING_TRIPLETS[digitOffset]; return offset; } private static int outputLeadingTriplet(int triplet, byte[] buffer, int offset) { int digitOffset = (triplet << 2); char c = LEADING_TRIPLETS[digitOffset++]; if (c != NULL_CHAR) { buffer[offset++] = (byte) c; } c = LEADING_TRIPLETS[digitOffset++]; if (c != NULL_CHAR) { buffer[offset++] = (byte) c; } // Last is required to be non-empty buffer[offset++] = (byte) LEADING_TRIPLETS[digitOffset]; return offset; } private static int outputFullTriplet(int triplet, char[] buffer, int offset) { int digitOffset = (triplet << 2); buffer[offset++] = FULL_TRIPLETS[digitOffset++]; buffer[offset++] = FULL_TRIPLETS[digitOffset++]; buffer[offset++] = FULL_TRIPLETS[digitOffset]; return offset; } private static int outputFullTriplet(int triplet, byte[] buffer, int offset) { int digitOffset = (triplet << 2); buffer[offset++] = FULL_TRIPLETS_B[digitOffset++]; buffer[offset++] = FULL_TRIPLETS_B[digitOffset++]; buffer[offset++] = FULL_TRIPLETS_B[digitOffset]; return offset; } /** *

* Pre-conditions: posValue is positive, and larger than * Integer.MAX_VALUE (about 2 billions). */ private static int calcLongStrLength(long posValue) { int len = 10; long comp = TEN_BILLION_L; // 19 is longest, need to worry about overflow while (posValue >= comp) { if (len == 19) { break; } ++len; comp = (comp << 3) + (comp << 1); // 10x } return len; } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/io/OutputDecorator.java000066400000000000000000000027401215056622200326670ustar00rootroot00000000000000package 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. */ public abstract class OutputDecorator 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 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.java000066400000000000000000000050451215056622200337450ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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 protected 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.2.2/src/main/java/com/fasterxml/jackson/core/io/SerializedString.java000066400000000000000000000177671215056622200330250ustar00rootroot00000000000000package 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 { 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#intern} works; lazily and * without synchronization or use of volatile keyword. */ 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(); } @Override public final char[] asQuotedChars() { char[] result = _quotedChars; if (result == null) { result = JsonStringEncoder.getInstance().quoteAsString(_value); _quotedChars = result; } return result; } /** * Accessor for accessing value that has been quoted using JSON * quoting rules, and encoded using UTF-8 encoding. */ @Override public final byte[] asUnquotedUTF8() { byte[] result = _unquotedUTF8Ref; if (result == null) { result = JsonStringEncoder.getInstance().encodeAsUTF8(_value); _unquotedUTF8Ref = result; } return result; } /** * Accessor for accessing value as is (without JSON quoting) * encoded using UTF-8 encoding. */ @Override public final byte[] asQuotedUTF8() { byte[] result = _quotedUTF8Ref; if (result == null) { result = JsonStringEncoder.getInstance().quoteAsUTF8(_value); _quotedUTF8Ref = result; } return result; } /* /********************************************************** /* Additional 2.0 methods for appending/writing contents /********************************************************** */ @Override public int appendQuotedUTF8(byte[] buffer, int offset) { byte[] result = _quotedUTF8Ref; if (result == null) { result = JsonStringEncoder.getInstance().quoteAsUTF8(_value); _quotedUTF8Ref = result; } final int length = result.length; if ((offset + length) > buffer.length) { return -1; } System.arraycopy(result, 0, buffer, offset, length); return length; } @Override public int appendQuoted(char[] buffer, int offset) { char[] result = _quotedChars; if (result == null) { result = JsonStringEncoder.getInstance().quoteAsString(_value); _quotedChars = result; } final int length = result.length; if ((offset + length) > buffer.length) { return -1; } System.arraycopy(result, 0, buffer, offset, length); return length; } @Override public int appendUnquotedUTF8(byte[] buffer, int offset) { byte[] result = _unquotedUTF8Ref; if (result == null) { result = JsonStringEncoder.getInstance().encodeAsUTF8(_value); _unquotedUTF8Ref = result; } 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 writeQuotedUTF8(OutputStream out) throws IOException { byte[] result = _quotedUTF8Ref; if (result == null) { result = JsonStringEncoder.getInstance().quoteAsUTF8(_value); _quotedUTF8Ref = result; } 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) { result = JsonStringEncoder.getInstance().encodeAsUTF8(_value); _unquotedUTF8Ref = result; } final int length = result.length; out.write(result, 0, length); return length; } @Override public int putQuotedUTF8(ByteBuffer buffer) { byte[] result = _quotedUTF8Ref; if (result == null) { result = JsonStringEncoder.getInstance().quoteAsUTF8(_value); _quotedUTF8Ref = result; } 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) { result = JsonStringEncoder.getInstance().encodeAsUTF8(_value); _unquotedUTF8Ref = result; } 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.2.2/src/main/java/com/fasterxml/jackson/core/io/UTF32Reader.java000066400000000000000000000153371215056622200314600ustar00rootroot00000000000000package 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 BaseReader { 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 = NULL_CHAR; /** * Total read character count; used for error reporting purposes */ protected int _charCount = 0; /** * Total read byte count; used for error reporting purposes */ protected int _byteCount = 0; protected final boolean _managedBuffers; /* /********************************************************** /* Life-cycle /********************************************************** */ public UTF32Reader(IOContext ctxt, InputStream in, byte[] buf, int ptr, int len, boolean isBigEndian) { super(ctxt, in, buf, ptr, len); _bigEndian = isBigEndian; _managedBuffers = (in != null); } /* /********************************************************** /* Public API /********************************************************** */ @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); } len += start; int outPtr = start; // Ok, first; do we have a surrogate from last round? if (_surrogate != NULL_CHAR) { cbuf[outPtr++] = _surrogate; _surrogate = NULL_CHAR; // 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? return -1; } } } main_loop: while (outPtr < len) { int ptr = _ptr; int ch; if (_bigEndian) { ch = (_buffer[ptr] << 24) | ((_buffer[ptr+1] & 0xFF) << 16) | ((_buffer[ptr+2] & 0xFF) << 8) | (_buffer[ptr+3] & 0xFF); } else { ch = (_buffer[ptr] & 0xFF) | ((_buffer[ptr+1] & 0xFF) << 8) | ((_buffer[ptr+2] & 0xFF) << 16) | (_buffer[ptr+3] << 24); } _ptr += 4; // Does it need to be split to surrogates? // (also, we can and need to verify illegal chars) if (ch > 0xFFFF) { // need to split into surrogates? if (ch > LAST_VALID_UNICODE_CHAR) { reportInvalid(ch, outPtr-start, "(above "+Integer.toHexString(LAST_VALID_UNICODE_CHAR)+") "); } ch -= 0x10000; // to normalize it starting with 0x0 cbuf[outPtr++] = (char) (0xD800 + (ch >> 10)); // hmmh. can this ever be 0? (not legal, at least?) ch = (0xDC00 | (ch & 0x03FF)); // Room for second part? if (outPtr >= len) { // nope _surrogate = (char) ch; break main_loop; } } cbuf[outPtr++] = (char) ch; if (_ptr >= _length) { break main_loop; } } len = outPtr - start; _charCount += len; return len; } /* /********************************************************** /* Internal methods /********************************************************** */ private void reportUnexpectedEOF(int gotBytes, int needed) throws IOException { int bytePos = _byteCount + gotBytes; int 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; int 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) { for (int i = 0; i < available; ++i) { _buffer[i] = _buffer[_ptr+i]; } _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; } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/io/UTF8Writer.java000066400000000000000000000313561215056622200314540ustar00rootroot00000000000000package 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 = 0; 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.2.2/src/main/java/com/fasterxml/jackson/core/json/000077500000000000000000000000001215056622200272205ustar00rootroot00000000000000ByteSourceJsonBootstrapper.java000066400000000000000000000416771215056622200353460ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.BytesToNameCanonicalizer; 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 { final static byte UTF8_BOM_1 = (byte) 0xEF; final static byte UTF8_BOM_2 = (byte) 0xBB; final static byte UTF8_BOM_3 = (byte) 0xBF; /* /********************************************************** /* Configuration /********************************************************** */ protected final IOContext _context; protected final InputStream _in; /* /********************************************************** /* Input buffering /********************************************************** */ protected 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. */ protected int _inputProcessed; /* /********************************************************** /* Data gathered /********************************************************** */ protected boolean _bigEndian = true; protected int _bytesPerChar = 0; // 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, JsonParseException { 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; } /* /********************************************************** /* Constructing a Reader /********************************************************** */ @SuppressWarnings("resource") public Reader constructReader() throws IOException { JsonEncoding enc = _context.getEncoding(); switch (enc) { case UTF32_BE: case UTF32_LE: return new UTF32Reader(_context, _in, _inputBuffer, _inputPtr, _inputEnd, _context.getEncoding().isBigEndian()); case UTF16_BE: case UTF16_LE: case UTF8: // only in non-common case where we don't want to do direct mapping { // 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()); } } throw new RuntimeException("Internal error"); // should never get here } public JsonParser constructParser(int parserFeatures, ObjectCodec codec, BytesToNameCanonicalizer rootByteSymbols, CharsToNameCanonicalizer rootCharSymbols, boolean canonicalize, boolean intern) throws IOException, JsonParseException { JsonEncoding enc = detectEncoding(); if (enc == JsonEncoding.UTF8) { /* and without canonicalization, byte-based approach is not performance; 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 (canonicalize) { BytesToNameCanonicalizer can = rootByteSymbols.makeChild(canonicalize, intern); return new UTF8StreamJsonParser(_context, parserFeatures, _in, codec, can, _inputBuffer, _inputPtr, _inputEnd, _bufferRecyclable); } } return new ReaderBasedJsonParser(_context, parserFeatures, constructReader(), codec, rootCharSymbols.makeChild(canonicalize, intern)); } /* /********************************************************** /* 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(); ch = (int) b & 0xFF; } } /* /********************************************************** /* 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 case 0xFEFF0000: // UCS-4, in-order... reportWeirdUCS4("3412"); // throws exception } // 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.2.2/src/main/java/com/fasterxml/jackson/core/json/JsonGeneratorImpl.java000066400000000000000000000121061215056622200334650ustar00rootroot00000000000000package 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; /* /********************************************************** /* Life-cycle /********************************************************** */ public JsonGeneratorImpl(IOContext ctxt, int features, ObjectCodec codec) { super(features, codec); _ioContext = ctxt; if (isEnabled(Feature.ESCAPE_NON_ASCII)) { setHighestNonEscapedChar(127); } } /* /********************************************************** /* Overridden configuration methods /********************************************************** */ @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; } /* /********************************************************** /* Versioned /********************************************************** */ @Override public Version version() { return VersionUtil.versionFor(getClass()); } /* /********************************************************** /* Partial API /********************************************************** */ // // Overrides just to make things final, to possibly help with inlining @Override public final void writeStringField(String fieldName, String value) throws IOException, JsonGenerationException { writeFieldName(fieldName); writeString(value); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/json/JsonReadContext.java000066400000000000000000000117041215056622200331400ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.CharTypes; /** * 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 protected final JsonReadContext _parent; // // // Location information (minus source reference) protected int _lineNr; protected int _columnNr; protected String _currentName; /* /********************************************************** /* 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 = null; /* /********************************************************** /* Instance construction, reuse /********************************************************** */ public JsonReadContext(JsonReadContext parent, int type, int lineNr, int colNr) { super(); _type = type; _parent = parent; _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; } // // // Factory methods public static JsonReadContext createRootContext(int lineNr, int colNr) { return new JsonReadContext(null, TYPE_ROOT, lineNr, colNr); } public static JsonReadContext createRootContext() { return new JsonReadContext(null, TYPE_ROOT, 1, 0); } public JsonReadContext createChildArrayContext(int lineNr, int colNr) { JsonReadContext ctxt = _child; if (ctxt == null) { _child = ctxt = new JsonReadContext(this, TYPE_ARRAY, lineNr, colNr); return ctxt; } 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, TYPE_OBJECT, lineNr, colNr); return ctxt; } ctxt.reset(TYPE_OBJECT, lineNr, colNr); return ctxt; } /* /********************************************************** /* Abstract method implementation /********************************************************** */ @Override public String getCurrentName() { return _currentName; } @Override public JsonReadContext getParent() { return _parent; } /* /********************************************************** /* Extended API /********************************************************** */ /** * @return Location pointing to the point where the context * start marker was found */ 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); } /* /********************************************************** /* 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) { _currentName = name; } /* /********************************************************** /* Overridden standard methods /********************************************************** */ /** * Overridden to provide developer readable "JsonPath" representation * of the context. */ @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: sb.append('{'); 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.2.2/src/main/java/com/fasterxml/jackson/core/json/JsonWriteContext.java000066400000000000000000000114211215056622200333530ustar00rootroot00000000000000package 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; protected final JsonWriteContext _parent; /** * Name of the field of which value is to be parsed; only * used for OBJECT contexts */ protected String _currentName; /* /********************************************************** /* Simple instance reuse slots; speed up things /* a bit (10-15%) for docs with lots of small /* arrays/objects /********************************************************** */ protected JsonWriteContext _child = null; /* /********************************************************** /* Life-cycle /********************************************************** */ protected JsonWriteContext(int type, JsonWriteContext parent) { super(); _type = type; _parent = parent; _index = -1; } // // // Factory methods public static JsonWriteContext createRootContext() { return new JsonWriteContext(TYPE_ROOT, null); } private JsonWriteContext reset(int type) { _type = type; _index = -1; _currentName = null; return this; } public final JsonWriteContext createChildArrayContext() { JsonWriteContext ctxt = _child; if (ctxt == null) { _child = ctxt = new JsonWriteContext(TYPE_ARRAY, this); return ctxt; } return ctxt.reset(TYPE_ARRAY); } public final JsonWriteContext createChildObjectContext() { JsonWriteContext ctxt = _child; if (ctxt == null) { _child = ctxt = new JsonWriteContext(TYPE_OBJECT, this); return ctxt; } return ctxt.reset(TYPE_OBJECT); } // // // Shared API @Override public final JsonWriteContext getParent() { return _parent; } @Override public final String getCurrentName() { return _currentName; } // // // API sub-classes are to implement /** * Method that writer is to call before it writes a field name. * * @return Index of the field entry (0-based) */ public final int writeFieldName(String name) { if (_type == TYPE_OBJECT) { if (_currentName != null) { // just wrote a name... return STATUS_EXPECT_VALUE; } _currentName = name; return (_index < 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_COMMA; } return STATUS_EXPECT_VALUE; } public final int writeValue() { // Most likely, object: if (_type == TYPE_OBJECT) { if (_currentName == null) { return STATUS_EXPECT_NAME; } _currentName = null; ++_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; } // // // Internally used abstract methods protected final void appendDesc(StringBuilder 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 final String toString() { StringBuilder sb = new StringBuilder(64); appendDesc(sb); return sb.toString(); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/json/PackageVersion.java.in000066400000000000000000000011071215056622200333700ustar00rootroot00000000000000package @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.java000066400000000000000000002112731215056622200341620ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.*; /** * 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 final class ReaderBasedJsonParser extends ParserBase { /* /********************************************************** /* 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; /* /********************************************************** /* 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 = false; /* /********************************************************** /* Life-cycle /********************************************************** */ public ReaderBasedJsonParser(IOContext ctxt, int features, Reader r, ObjectCodec codec, CharsToNameCanonicalizer st) { super(ctxt, features); _reader = r; _inputBuffer = ctxt.allocTokenBuffer(); _objectCodec = codec; _symbols = st; _hashSeed = st.hashSeed(); } /* /********************************************************** /* 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; } @Override protected boolean loadMore() throws IOException { _currInputProcessed += _inputEnd; _currInputRowStart -= _inputEnd; if (_reader != null) { int count = _reader.read(_inputBuffer, 0, _inputBuffer.length); if (count > 0) { _inputPtr = 0; _inputEnd = count; return true; } // End of input _closeInput(); // Should never return 0, so let's fail if (count == 0) { throw new IOException("Reader returned 0 characters when trying to read "+_inputEnd); } } return false; } protected char getNextChar(String eofMsg) throws IOException, JsonParseException { if (_inputPtr >= _inputEnd) { if (!loadMore()) { _reportInvalidEOF(eofMsg); } } 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(); char[] buf = _inputBuffer; if (buf != null) { _inputBuffer = null; _ioContext.releaseTokenBuffer(buf); } } /* /********************************************************** /* 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 String getText() throws IOException, JsonParseException { JsonToken t = _currToken; if (t == JsonToken.VALUE_STRING) { if (_tokenIncomplete) { _tokenIncomplete = false; _finishString(); // only strings can be incomplete } return _textBuffer.contentsAsString(); } return _getText2(t); } // // // Let's override default impls for improved performance // @since 2.1 @Override public String getValueAsString() throws IOException, JsonParseException { if (_currToken == JsonToken.VALUE_STRING) { if (_tokenIncomplete) { _tokenIncomplete = false; _finishString(); // only strings can be incomplete } return _textBuffer.contentsAsString(); } return super.getValueAsString(null); } // @since 2.1 @Override public String getValueAsString(String defValue) throws IOException, JsonParseException { if (_currToken == JsonToken.VALUE_STRING) { if (_tokenIncomplete) { _tokenIncomplete = false; _finishString(); // only strings can be incomplete } return _textBuffer.contentsAsString(); } return super.getValueAsString(defValue); } protected String _getText2(JsonToken t) { if (t == null) { return null; } switch (t) { case FIELD_NAME: return _parsingContext.getCurrentName(); case VALUE_STRING: // fall through case VALUE_NUMBER_INT: case VALUE_NUMBER_FLOAT: return _textBuffer.contentsAsString(); default: return t.asString(); } } @Override public char[] getTextCharacters() throws IOException, JsonParseException { if (_currToken != null) { // null only before/after document switch (_currToken) { case 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 VALUE_STRING: if (_tokenIncomplete) { _tokenIncomplete = false; _finishString(); // only strings can be incomplete } // fall through case VALUE_NUMBER_INT: case VALUE_NUMBER_FLOAT: return _textBuffer.getTextBuffer(); default: return _currToken.asCharArray(); } } return null; } @Override public int getTextLength() throws IOException, JsonParseException { if (_currToken != null) { // null only before/after document switch (_currToken) { case FIELD_NAME: return _parsingContext.getCurrentName().length(); case VALUE_STRING: if (_tokenIncomplete) { _tokenIncomplete = false; _finishString(); // only strings can be incomplete } // fall through case VALUE_NUMBER_INT: case VALUE_NUMBER_FLOAT: return _textBuffer.size(); default: return _currToken.asCharArray().length; } } return 0; } @Override public int getTextOffset() throws IOException, JsonParseException { // Most have offset of 0, only some may have other values: if (_currToken != null) { switch (_currToken) { case FIELD_NAME: return 0; case VALUE_STRING: if (_tokenIncomplete) { _tokenIncomplete = false; _finishString(); // only strings can be incomplete } // fall through case VALUE_NUMBER_INT: case VALUE_NUMBER_FLOAT: return _textBuffer.getTextOffset(); default: } } return 0; } @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException { 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) { ByteArrayBuilder builder = _getByteArrayBuilder(); _decodeBase64(getText(), builder, b64variant); _binaryValue = builder.toByteArray(); } } return _binaryValue; } @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException, JsonParseException { // 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, JsonParseException { 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 == '"' && !b64variant.usesPadding()) { decodedData >>= 4; buffer[outputPtr++] = (byte) decodedData; 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)) { 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 == '"' && !b64variant.usesPadding()) { decodedData >>= 2; buffer[outputPtr++] = (byte) (decodedData >> 8); buffer[outputPtr++] = (byte) decodedData; break; } bits = _decodeBase64Escape(b64variant, ch, 3); } if (bits == Base64Variant.BASE64_VALUE_PADDING) { /* With padding we only get 2 bytes; but we have * to shift it a bit so it is identical to triplet * case with partial output. * 3 chars gives 3x6 == 18 bits, of which 2 are * dummies, need to discard: */ decodedData >>= 2; buffer[outputPtr++] = (byte) (decodedData >> 8); buffer[outputPtr++] = (byte) decodedData; continue; } } // otherwise, our triplet is now complete decodedData = (decodedData << 6) | bits; buffer[outputPtr++] = (byte) (decodedData >> 16); buffer[outputPtr++] = (byte) (decodedData >> 8); buffer[outputPtr++] = (byte) decodedData; } _tokenIncomplete = false; if (outputPtr > 0) { outputCount += outputPtr; out.write(buffer, 0, outputPtr); } return outputCount; } /* /********************************************************** /* Public API, traversal /********************************************************** */ /** * @return Next token from the stream, if any found, or null * to indicate end-of-input */ @Override public JsonToken nextToken() throws IOException, JsonParseException { _numTypesValid = NR_UNKNOWN; /* 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(); } if (_tokenIncomplete) { _skipString(); // only strings can be partial } int i = _skipWSOrEnd(); if (i < 0) { // end-of-input /* 19-Feb-2009, tatu: Should actually close/release things * like input source, symbol table and recyclable buffers now. */ close(); return (_currToken = null); } /* First, need to ensure we know the starting location of token * after skipping leading white space */ _tokenInputTotal = _currInputProcessed + _inputPtr - 1; _tokenInputRow = _currInputRow; _tokenInputCol = _inputPtr - _currInputRowStart - 1; // finally: clear any data retained so far _binaryValue = null; // Closing scope? if (i == INT_RBRACKET) { if (!_parsingContext.inArray()) { _reportMismatchedEndMarker(i, '}'); } _parsingContext = _parsingContext.getParent(); return (_currToken = JsonToken.END_ARRAY); } if (i == INT_RCURLY) { if (!_parsingContext.inObject()) { _reportMismatchedEndMarker(i, ']'); } _parsingContext = _parsingContext.getParent(); 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.getTypeDesc()+" entries"); } i = _skipWS(); } /* 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: String name = _parseFieldName(i); _parsingContext.setCurrentName(name); _currToken = JsonToken.FIELD_NAME; i = _skipWS(); if (i != INT_COLON) { _reportUnexpectedChar(i, "was expecting a colon to separate field name and value"); } i = _skipWS(); } // Ok: we must have a value... what is it? JsonToken t; switch (i) { case INT_QUOTE: _tokenIncomplete = true; t = JsonToken.VALUE_STRING; break; case INT_LBRACKET: if (!inObject) { _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); } t = JsonToken.START_ARRAY; break; case INT_LCURLY: if (!inObject) { _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); } t = JsonToken.START_OBJECT; break; case INT_RBRACKET: case INT_RCURLY: // Error: neither is valid at this point; valid closers have // been handled earlier _reportUnexpectedChar(i, "expected a value"); case INT_t: _matchToken("true", 1); t = JsonToken.VALUE_TRUE; break; case INT_f: _matchToken("false", 1); t = JsonToken.VALUE_FALSE; break; case INT_n: _matchToken("null", 1); t = JsonToken.VALUE_NULL; break; case INT_MINUS: /* 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 INT_0: case INT_1: case INT_2: case INT_3: case INT_4: case INT_5: case INT_6: case INT_7: case INT_8: case INT_9: t = parseNumberText(i); break; default: t = _handleUnexpectedValue(i); break; } if (inObject) { _nextToken = t; return _currToken; } _currToken = t; return t; } private 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 boolean nextFieldName(SerializableString str) throws IOException, JsonParseException */ // note: identical to one in Utf8StreamParser @Override public String nextTextValue() throws IOException, JsonParseException { 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 int nextIntValue(int defaultValue) throws IOException, JsonParseException { 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 long nextLongValue(long defaultValue) throws IOException, JsonParseException { 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 Utf8StreamParser @Override public Boolean nextBooleanValue() throws IOException, JsonParseException { 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; } switch (nextToken()) { case VALUE_TRUE: return Boolean.TRUE; case VALUE_FALSE: return Boolean.FALSE; default: return null; } } @Override public void close() throws IOException { super.close(); _symbols.release(); } /* /********************************************************** /* Internal methods, number parsing /* (note: in 1.8 and prior, part of "ReaderBasedNumericParser" /********************************************************** */ /** * 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 parseNumberText(int ch) throws IOException, JsonParseException { /* 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 */ boolean negative = (ch == INT_MINUS); int ptr = _inputPtr; int startPtr = ptr-1; // to include sign/digit already read final int inputLen = _inputEnd; dummy_loop: do { // dummy loop, to be able to break out if (negative) { // need to read the next digit if (ptr >= _inputEnd) { break dummy_loop; } 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); } /* (note: has been checked for non-negative already, in * the dispatching code that determined it should be * a numeric value) */ } // One special case, leading zero(es): if (ch == INT_0) { break dummy_loop; } /* 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 >= _inputEnd) { break dummy_loop; } ch = (int) _inputBuffer[ptr++]; if (ch < INT_0 || ch > INT_9) { break int_loop; } ++intLen; } int fractLen = 0; // And then see if we get other parts if (ch == INT_DECIMAL_POINT) { // yes, fraction fract_loop: while (true) { if (ptr >= inputLen) { break dummy_loop; } 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 == INT_e || ch == INT_E) { // and/or exponent if (ptr >= inputLen) { break dummy_loop; } // Sign indicator? ch = (int) _inputBuffer[ptr++]; if (ch == INT_MINUS || ch == INT_PLUS) { // yup, skip for now if (ptr >= inputLen) { break dummy_loop; } ch = (int) _inputBuffer[ptr++]; } while (ch <= INT_9 && ch >= INT_0) { ++expLen; if (ptr >= inputLen) { break dummy_loop; } 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"); } } // Got it all: let's add to text buffer for parsing, access --ptr; // need to push back following separator _inputPtr = ptr; int len = ptr-startPtr; _textBuffer.resetWithShared(_inputBuffer, startPtr, len); return reset(negative, intLen, fractLen, expLen); } while (false); _inputPtr = negative ? (startPtr+1) : startPtr; return parseNumberText2(negative); } /** * 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 JsonToken parseNumberText2(boolean negative) throws IOException, JsonParseException { char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); int outPtr = 0; // Need to prepend sign? if (negative) { 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"); 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) { reportInvalidNumber("Missing integer part (next char "+_getCharDesc(c)+")"); } int fractLen = 0; // And then see if we get other parts if (c == '.') { // yes, fraction 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; } _textBuffer.setCurrentLength(outPtr); // And there we have it! return reset(negative, intLen, fractLen, expLen); } /** * Method called when we have seen one zero, and want to ensure * it is not followed by another */ private char _verifyNoLeadingZeroes() throws IOException, JsonParseException { // Ok to have plain "0" if (_inputPtr >= _inputEnd && !loadMore()) { return '0'; } 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'; } if (!isEnabled(Feature.ALLOW_NUMERIC_LEADING_ZEROS)) { reportInvalidNumber("Leading zeroes not allowed"); } // if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number) ++_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, JsonParseException { if (ch == 'I') { if (_inputPtr >= _inputEnd) { if (!loadMore()) { _reportInvalidEOFInValue(); } } ch = _inputBuffer[_inputPtr++]; if (ch == 'N') { String match = negative ? "-INF" :"+INF"; _matchToken(match, 3); if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { 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 (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { 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; } /* /********************************************************** /* Internal methods, secondary parsing /********************************************************** */ protected String _parseFieldName(int i) throws IOException, JsonParseException { if (i != INT_QUOTE) { return _handleUnusualFieldName(i); } /* 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 inputLen = _inputEnd; if (ptr < inputLen) { final int[] codes = CharTypes.getInputCodeLatin1(); final int maxCode = codes.length; do { int ch = _inputBuffer[ptr]; if (ch < maxCode && 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; } while (ptr < inputLen); } int start = _inputPtr; _inputPtr = ptr; return _parseFieldName2(start, hash, INT_QUOTE); } private String _parseFieldName2(int startPtr, int hash, int endChar) throws IOException, JsonParseException { _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(": was expecting closing '"+((char) endChar)+"' for 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) + 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); } } /** * 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 _handleUnusualFieldName(int i) throws IOException, JsonParseException { // [JACKSON-173]: allow single quotes if (i == INT_APOSTROPHE && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { return _parseApostropheFieldName(); } // [JACKSON-69]: allow unquoted names if feature enabled: if (!isEnabled(Feature.ALLOW_UNQUOTED_FIELD_NAMES)) { _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, and not a number firstOk = (codes[i] == 0) && (i < INT_0 || i > INT_9); } 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 _parseUnusualFieldName2(start, hash, codes); } protected String _parseApostropheFieldName() throws IOException, JsonParseException { // Note: mostly copy of_parseFieldName int ptr = _inputPtr; int hash = _hashSeed; final int inputLen = _inputEnd; if (ptr < inputLen) { final int[] codes = CharTypes.getInputCodeLatin1(); 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 _parseFieldName2(start, hash, INT_APOSTROPHE); } /** * 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 i) throws IOException, JsonParseException { // Most likely an error, unless we are to allow single-quote-strings switch (i) { case '\'': /* [JACKSON-173]: allow single quotes. Unlike with regular * Strings, we'll eagerly parse contents; this so that there's * no need to store information on quote char used. * * Also, no separation to fast/slow parsing; we'll just do * one regular (~= slowish) parsing, to keep code simple */ if (isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { return _handleApostropheValue(); } break; case 'N': _matchToken("NaN", 1); if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { return resetAsNaN("NaN", Double.NaN); } _reportError("Non-standard token 'NaN': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); break; case '+': // note: '-' is taken as number if (_inputPtr >= _inputEnd) { if (!loadMore()) { _reportInvalidEOFInValue(); } } return _handleInvalidNumberStart(_inputBuffer[_inputPtr++], false); } _reportUnexpectedChar(i, "expected a valid value (number, String, array, object, 'true', 'false' or 'null')"); return null; } protected JsonToken _handleApostropheValue() throws IOException, JsonParseException { char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); int outPtr = _textBuffer.getCurrentSegmentSize(); while (true) { if (_inputPtr >= _inputEnd) { if (!loadMore()) { _reportInvalidEOF(": was expecting closing quote for a string value"); } } 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 <= INT_APOSTROPHE) { if (i == INT_APOSTROPHE) { 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 _parseUnusualFieldName2(int startPtr, int hash, int[] codes) throws IOException, JsonParseException { _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 void _finishString() throws IOException, JsonParseException { /* 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 = CharTypes.getInputCodeLatin1(); 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, JsonParseException { char[] outBuf = _textBuffer.getCurrentSegment(); int outPtr = _textBuffer.getCurrentSegmentSize(); while (true) { if (_inputPtr >= _inputEnd) { if (!loadMore()) { _reportInvalidEOF(": was expecting closing quote for a string value"); } } 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 <= INT_QUOTE) { if (i == INT_QUOTE) { 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); } /** * 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, JsonParseException { _tokenIncomplete = false; int inputPtr = _inputPtr; int inputLen = _inputEnd; char[] inputBuffer = _inputBuffer; while (true) { if (inputPtr >= inputLen) { _inputPtr = inputPtr; if (!loadMore()) { _reportInvalidEOF(": was expecting closing quote for a string value"); } inputPtr = _inputPtr; inputLen = _inputEnd; } 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. */ _inputPtr = inputPtr; c = _decodeEscaped(); inputPtr = _inputPtr; inputLen = _inputEnd; } else if (i <= INT_QUOTE) { if (i == INT_QUOTE) { _inputPtr = inputPtr; break; } if (i < INT_SPACE) { _inputPtr = inputPtr; _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 void _skipCR() throws IOException { if (_inputPtr < _inputEnd || loadMore()) { if (_inputBuffer[_inputPtr] == '\n') { ++_inputPtr; } } ++_currInputRow; _currInputRowStart = _inputPtr; } protected void _skipLF() throws IOException { ++_currInputRow; _currInputRowStart = _inputPtr; } private int _skipWS() throws IOException, JsonParseException { while (_inputPtr < _inputEnd || loadMore()) { int i = (int) _inputBuffer[_inputPtr++]; if (i > INT_SPACE) { if (i != INT_SLASH) { return i; } _skipComment(); } else if (i != INT_SPACE) { if (i == INT_LF) { _skipLF(); } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } throw _constructError("Unexpected end-of-input within/between "+_parsingContext.getTypeDesc()+" entries"); } private int _skipWSOrEnd() throws IOException, JsonParseException { while ((_inputPtr < _inputEnd) || loadMore()) { int i = (int) _inputBuffer[_inputPtr++]; if (i > INT_SPACE) { if (i == INT_SLASH) { _skipComment(); continue; } return i; } if (i != INT_SPACE) { if (i == INT_LF) { _skipLF(); } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } // We ran out of input... _handleEOF(); return -1; } private void _skipComment() throws IOException, JsonParseException { if (!isEnabled(Feature.ALLOW_COMMENTS)) { _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"); } char c = _inputBuffer[_inputPtr++]; if (c == '/') { _skipCppComment(); } else if (c == '*') { _skipCComment(); } else { _reportUnexpectedChar(c, "was expecting either '*' or '/' for a comment"); } } private void _skipCComment() throws IOException, JsonParseException { // Ok: need the matching '*/' main_loop: while ((_inputPtr < _inputEnd) || loadMore()) { int i = (int) _inputBuffer[_inputPtr++]; if (i <= INT_ASTERISK) { if (i == INT_ASTERISK) { // end? if ((_inputPtr >= _inputEnd) && !loadMore()) { break main_loop; } if (_inputBuffer[_inputPtr] == INT_SLASH) { ++_inputPtr; return; } continue; } if (i < INT_SPACE) { if (i == INT_LF) { _skipLF(); } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } } _reportInvalidEOF(" in a comment"); } private void _skipCppComment() throws IOException, JsonParseException { // Ok: need to find EOF or linefeed while ((_inputPtr < _inputEnd) || loadMore()) { int i = (int) _inputBuffer[_inputPtr++]; if (i < INT_SPACE) { if (i == INT_LF) { _skipLF(); break; } else if (i == INT_CR) { _skipCR(); break; } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } } @Override protected char _decodeEscaped() throws IOException, JsonParseException { if (_inputPtr >= _inputEnd) { if (!loadMore()) { _reportInvalidEOF(" in character escape sequence"); } } char c = _inputBuffer[_inputPtr++]; switch ((int) c) { // First, ones that are mapped case INT_b: return '\b'; case INT_t: return '\t'; case INT_n: return '\n'; case INT_f: return '\f'; case INT_r: return '\r'; // And these are to be returned as they are case INT_QUOTE: case INT_SLASH: case INT_BACKSLASH: return c; case INT_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"); } } 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; } /** * Helper method for checking whether input matches expected token */ protected void _matchToken(String matchStr, int i) throws IOException, JsonParseException { final int len = matchStr.length(); do { if (_inputPtr >= _inputEnd) { if (!loadMore()) { _reportInvalidToken(matchStr.substring(0, i)); } } if (_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) { if (!loadMore()) { return; } } char c = _inputBuffer[_inputPtr]; if (c < '0' || c == ']' || c == '}') { // expected/allowed chars return; } // if Java letter, it's a problem tho if (Character.isJavaIdentifierPart(c)) { _reportInvalidToken(matchStr.substring(0, i)); } return; } /* /********************************************************** /* Binary access /********************************************************** */ /** * Efficient handling for incremental parsing of base64-encoded * textual content. */ protected byte[] _decodeBase64(Base64Variant b64variant) throws IOException, JsonParseException { 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 == '"' && !b64variant.usesPadding()) { decodedData >>= 4; builder.append(decodedData); 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)) { 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 == '"' && !b64variant.usesPadding()) { decodedData >>= 2; builder.appendTwoBytes(decodedData); return builder.toByteArray(); } bits = _decodeBase64Escape(b64variant, ch, 3); } if (bits == Base64Variant.BASE64_VALUE_PADDING) { // With padding we only get 2 bytes; but we have // to shift it a bit so it is identical to triplet // case with partial output. // 3 chars gives 3x6 == 18 bits, of which 2 are // dummies, need to discard: decodedData >>= 2; builder.appendTwoBytes(decodedData); continue; } // otherwise we got escaped other char, to be processed below } // otherwise, our triplet is now complete decodedData = (decodedData << 6) | bits; builder.appendThreeBytes(decodedData); } } /* /********************************************************** /* Error reporting /********************************************************** */ protected void _reportInvalidToken(String matchedPart) throws IOException, JsonParseException { _reportInvalidToken(matchedPart, "'null', 'true', 'false' or NaN"); } protected void _reportInvalidToken(String matchedPart, String msg) throws IOException, JsonParseException { 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. */ while (true) { if (_inputPtr >= _inputEnd) { if (!loadMore()) { break; } } char c = _inputBuffer[_inputPtr]; if (!Character.isJavaIdentifierPart(c)) { break; } ++_inputPtr; sb.append(c); } _reportError("Unrecognized token '"+sb.toString()+"': was expecting "); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java000066400000000000000000001775671215056622200333410ustar00rootroot00000000000000package 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.*; 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) ':'; private final static byte BYTE_QUOTE = (byte) '"'; protected final static int SURR1_FIRST = 0xD800; protected final static int SURR1_LAST = 0xDBFF; protected final static int SURR2_FIRST = 0xDC00; protected final static int SURR2_LAST = 0xDFFF; // intermediate copies only made up to certain length... private final static int MAX_BYTES_TO_BUFFER = 512; 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' }; /* /********************************************************** /* Output buffering /********************************************************** */ /** * Underlying output stream used for writing JSON content. */ final protected OutputStream _outputStream; /** * 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 = 0; /** * 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 /********************************************************** */ public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec, OutputStream out) { super(ctxt, features, codec); _outputStream = out; _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); } } public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec, OutputStream out, byte[] outputBuffer, int outputOffset, boolean bufferRecyclable) { super(ctxt, features, codec); _outputStream = out; _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; } /* /********************************************************** /* Overridden configuration methods /********************************************************** */ @Override public Object getOutputTarget() { return _outputStream; } /* /********************************************************** /* Overridden methods /********************************************************** */ @Override public final void writeFieldName(String name) throws IOException, JsonGenerationException { int status = _writeContext.writeFieldName(name); if (status == JsonWriteContext.STATUS_EXPECT_VALUE) { _reportError("Can not write a field name, expecting a value"); } if (_cfgPrettyPrinter != null) { _writePPFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA)); return; } if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) { // need comma if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_COMMA; } _writeFieldName(name); } @Override public final void writeFieldName(SerializableString name) throws IOException, JsonGenerationException { // 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"); } if (_cfgPrettyPrinter != null) { _writePPFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA)); return; } if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_COMMA; } _writeFieldName(name); } /* /********************************************************** /* Output method implementations, structural /********************************************************** */ @Override public final void writeStartArray() throws IOException, JsonGenerationException { _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, JsonGenerationException { if (!_writeContext.inArray()) { _reportError("Current context not an ARRAY but "+_writeContext.getTypeDesc()); } if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount()); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_RBRACKET; } _writeContext = _writeContext.getParent(); } @Override public final void writeStartObject() throws IOException, JsonGenerationException { _verifyValueWrite("start an object"); _writeContext = _writeContext.createChildObjectContext(); if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeStartObject(this); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_LCURLY; } } @Override public final void writeEndObject() throws IOException, JsonGenerationException { if (!_writeContext.inObject()) { _reportError("Current context not an object but "+_writeContext.getTypeDesc()); } if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount()); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_RCURLY; } _writeContext = _writeContext.getParent(); } protected final void _writeFieldName(String name) throws IOException, JsonGenerationException { /* To support [JACKSON-46], we'll do this: * (Question: should quoting of spaces (etc) still be enabled?) */ if (!isEnabled(Feature.QUOTE_FIELD_NAMES)) { _writeStringSegments(name); return; } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; // The beef: final int len = name.length(); if (len <= _charBufferLength) { // yes, fits right in 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); } } else { _writeStringSegments(name); } // and closing quotes; need room for one more char: if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; } protected final void _writeFieldName(SerializableString name) throws IOException, JsonGenerationException { if (!isEnabled(Feature.QUOTE_FIELD_NAMES)) { int len = name.appendQuotedUTF8(_outputBuffer, _outputTail); // different quoting (escaping) if (len < 0) { _writeBytes(name.asQuotedUTF8()); } else { _outputTail += len; } return; } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; 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++] = BYTE_QUOTE; } /** * 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, JsonGenerationException { if (commaBefore) { _cfgPrettyPrinter.writeObjectEntrySeparator(this); } else { _cfgPrettyPrinter.beforeObjectEntries(this); } if (isEnabled(Feature.QUOTE_FIELD_NAMES)) { // standard if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; final int len = name.length(); if (len <= _charBufferLength) { // yes, fits right in 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); } } else { _writeStringSegments(name); } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; } else { // non-standard, omit quotes _writeStringSegments(name); } } protected final void _writePPFieldName(SerializableString name, boolean commaBefore) throws IOException, JsonGenerationException { if (commaBefore) { _cfgPrettyPrinter.writeObjectEntrySeparator(this); } else { _cfgPrettyPrinter.beforeObjectEntries(this); } boolean addQuotes = isEnabled(Feature.QUOTE_FIELD_NAMES); // standard if (addQuotes) { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; } _writeBytes(name.asQuotedUTF8()); if (addQuotes) { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; } } /* /********************************************************** /* Output method implementations, textual /********************************************************** */ @Override public void writeString(String text) throws IOException, JsonGenerationException { _verifyValueWrite("write text value"); if (text == null) { _writeNull(); return; } // First: can we make a local copy of chars that make up text? final int len = text.length(); if (len > _charBufferLength) { // nope: off-line handling _writeLongString(text); return; } // yes: good. text.getChars(0, len, _charBuffer, 0); // Output: if we can't guarantee it fits in output buffer, off-line as well: if (len > _outputMaxContiguous) { _writeLongString(_charBuffer, 0, len); return; } if ((_outputTail + len) >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; _writeStringSegment(_charBuffer, 0, len); // we checked space already above /* [JACKSON-462] But that method may have had to expand multi-byte Unicode * chars, so we must check again */ if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; } private void _writeLongString(String text) throws IOException, JsonGenerationException { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; _writeStringSegments(text); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; } private void _writeLongString(char[] text, int offset, int len) throws IOException, JsonGenerationException { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; _writeStringSegments(_charBuffer, 0, len); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; } @Override public void writeString(char[] text, int offset, int len) throws IOException, JsonGenerationException { _verifyValueWrite("write text value"); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; // 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++] = BYTE_QUOTE; } @Override public final void writeString(SerializableString text) throws IOException, JsonGenerationException { _verifyValueWrite("write text value"); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; int len = text.appendQuotedUTF8(_outputBuffer, _outputTail); if (len < 0) { _writeBytes(text.asQuotedUTF8()); } else { _outputTail += len; } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; } @Override public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException, JsonGenerationException { _verifyValueWrite("write text value"); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; _writeBytes(text, offset, length); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; } @Override public void writeUTF8String(byte[] text, int offset, int len) throws IOException, JsonGenerationException { _verifyValueWrite("write text value"); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; // One or multiple segments? if (len <= _outputMaxContiguous) { _writeUTF8Segment(text, offset, len); } else { _writeUTF8Segments(text, offset, len); } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; } /* /********************************************************** /* Output method implementations, unprocessed ("raw") /********************************************************** */ @Override public void writeRaw(String text) throws IOException, JsonGenerationException { int start = 0; int len = text.length(); while (len > 0) { char[] buf = _charBuffer; final int blen = buf.length; final int len2 = (len < blen) ? len : blen; text.getChars(start, start+len2, buf, 0); writeRaw(buf, 0, len2); start += len2; len -= len2; } } @Override public void writeRaw(String text, int offset, int len) throws IOException, JsonGenerationException { while (len > 0) { char[] buf = _charBuffer; final int blen = buf.length; final int len2 = (len < blen) ? len : blen; text.getChars(offset, offset+len2, buf, 0); writeRaw(buf, 0, len2); offset += len2; len -= len2; } } @Override public void writeRaw(SerializableString text) throws IOException, JsonGenerationException { byte[] raw = text.asUnquotedUTF8(); if (raw.length > 0) { _writeBytes(raw); } } // @TODO: rewrite for speed... @Override public final void writeRaw(char[] cbuf, int offset, int len) throws IOException, JsonGenerationException { // 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 { _outputRawMultiByteChar(ch, cbuf, offset, len); } } } @Override public void writeRaw(char ch) throws IOException, JsonGenerationException { 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 { _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, JsonGenerationException { final int end = _outputEnd; final byte[] bbuf = _outputBuffer; main_loop: while (offset < len) { 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 >= len) { 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 { _outputRawMultiByteChar(ch, cbuf, offset, 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 value"); // Starting quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; _writeBinary(b64variant, data, offset, offset+len); // and closing quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; } @Override public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) throws IOException, JsonGenerationException { _verifyValueWrite("write binary value"); // Starting quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; 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++] = BYTE_QUOTE; return bytes; } /* /********************************************************** /* Output method implementations, primitive /********************************************************** */ @Override public void writeNumber(short s) throws IOException, JsonGenerationException { _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 void _writeQuotedShort(short s) throws IOException { if ((_outputTail + 8) >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail); _outputBuffer[_outputTail++] = BYTE_QUOTE; } @Override public void writeNumber(int i) throws IOException, JsonGenerationException { _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 void _writeQuotedInt(int i) throws IOException { if ((_outputTail + 13) >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail); _outputBuffer[_outputTail++] = BYTE_QUOTE; } @Override public void writeNumber(long l) throws IOException, JsonGenerationException { _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++] = BYTE_QUOTE; _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail); _outputBuffer[_outputTail++] = BYTE_QUOTE; } @Override public void writeNumber(BigInteger value) throws IOException, JsonGenerationException { _verifyValueWrite("write number"); if (value == null) { _writeNull(); } else if (_cfgNumbersAsStrings) { _writeQuotedRaw(value); } else { writeRaw(value.toString()); } } @Override public void writeNumber(double d) throws IOException, JsonGenerationException { if (_cfgNumbersAsStrings || // [JACKSON-139] (((Double.isNaN(d) || Double.isInfinite(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)); } @Override public void writeNumber(float f) throws IOException, JsonGenerationException { if (_cfgNumbersAsStrings || // [JACKSON-139] (((Float.isNaN(f) || Float.isInfinite(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, JsonGenerationException { // Don't really know max length for big decimal, no point checking _verifyValueWrite("write number"); if (value == null) { _writeNull(); } else if (_cfgNumbersAsStrings) { _writeQuotedRaw(value); } else { writeRaw(value.toString()); } } @Override public void writeNumber(String encodedValue) throws IOException, JsonGenerationException { _verifyValueWrite("write number"); if (_cfgNumbersAsStrings) { _writeQuotedRaw(encodedValue); } else { writeRaw(encodedValue); } } private void _writeQuotedRaw(Object value) throws IOException { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; writeRaw(value.toString()); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = BYTE_QUOTE; } @Override public void writeBoolean(boolean state) throws IOException, JsonGenerationException { _verifyValueWrite("write boolean value"); 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, JsonGenerationException { _verifyValueWrite("write null value"); _writeNull(); } /* /********************************************************** /* Implementations for other methods /********************************************************** */ @Override protected final void _verifyValueWrite(String typeMsg) throws IOException, JsonGenerationException { int status = _writeContext.writeValue(); if (status == JsonWriteContext.STATUS_EXPECT_NAME) { _reportError("Can not "+typeMsg+", expecting field name"); } if (_cfgPrettyPrinter == null) { byte b; switch (status) { case JsonWriteContext.STATUS_OK_AFTER_COMMA: b = BYTE_COMMA; break; case JsonWriteContext.STATUS_OK_AFTER_COLON: b = BYTE_COLON; break; case JsonWriteContext.STATUS_OK_AFTER_SPACE: // root-value separator if (_rootValueSeparator != null) { byte[] raw = _rootValueSeparator.asUnquotedUTF8(); if (raw.length > 0) { _writeBytes(raw); } } return; case JsonWriteContext.STATUS_OK_AS_IS: default: return; } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail] = b; ++_outputTail; return; } // Otherwise, pretty printer knows what to do... _verifyPrettyValueWrite(typeMsg, status); } protected final void _verifyPrettyValueWrite(String typeMsg, int status) throws IOException, JsonGenerationException { // If we have a pretty printer, it knows what to do: switch (status) { case JsonWriteContext.STATUS_OK_AFTER_COMMA: // array _cfgPrettyPrinter.writeArrayValueSeparator(this); break; case JsonWriteContext.STATUS_OK_AFTER_COLON: _cfgPrettyPrinter.writeObjectFieldValueSeparator(this); break; case JsonWriteContext.STATUS_OK_AFTER_SPACE: _cfgPrettyPrinter.writeRootValueSeparator(this); break; case JsonWriteContext.STATUS_OK_AS_IS: // First entry, but of which context? if (_writeContext.inArray()) { _cfgPrettyPrinter.beforeArrayValues(this); } else if (_writeContext.inObject()) { _cfgPrettyPrinter.beforeObjectEntries(this); } break; default: _throwInternal(); break; } } /* /********************************************************** /* Low-level output handling /********************************************************** */ @Override public final 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(); /* 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) throws IOException, JsonGenerationException { int left = text.length(); int offset = 0; final char[] cbuf = _charBuffer; while (left > 0) { int len = Math.min(_outputMaxContiguous, left); text.getChars(offset, offset+len, cbuf, 0); if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space _flushBuffer(); } _writeStringSegment(cbuf, 0, len); offset += len; left -= len; } } /** * 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, JsonGenerationException { 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); } /* /********************************************************** /* 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, JsonGenerationException { // 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) { // [JACKSON-106] if (_characterEscapes != null) { _writeCustomStringSegment2(cbuf, offset, len); // [JACKSON-102] } else if (_maximumNonEscapedChar == 0) { _writeStringSegment2(cbuf, offset, len); } else { _writeStringSegmentASCII2(cbuf, 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, JsonGenerationException { // 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; } /* /********************************************************** /* 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; } /* /********************************************************** /* 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 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 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 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 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 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 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 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 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, 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 int _outputRawMultiByteChar(int ch, char[] cbuf, int inputOffset, int inputLen) 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 >= inputLen) { // nope... have to note down _reportError("Split surrogate on writeRaw() input (last character)"); } _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 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 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 { bbuf[outputPtr++] = (byte) (0xe0 | (ch >> 12)); bbuf[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f)); bbuf[outputPtr++] = (byte) (0x80 | (ch & 0x3f)); } return outputPtr; } 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; } private 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.java000066400000000000000000003261411215056622200337240ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.*; import com.fasterxml.jackson.core.util.*; /** * This is a concrete implementation of {@link JsonParser}, which is * based on a {@link java.io.InputStream} as the input source. */ public final class UTF8StreamJsonParser extends ParserBase { final static byte BYTE_LF = (byte) '\n'; private final static int[] sInputCodesUtf8 = 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. */ private final static int[] sInputCodesLatin1 = 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 BytesToNameCanonicalizer _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 = false; /** * Temporary storage for partially parsed name bytes. */ private int _quad1; /* /********************************************************** /* 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 /********************************************************** */ public UTF8StreamJsonParser(IOContext ctxt, int features, InputStream in, ObjectCodec codec, BytesToNameCanonicalizer sym, byte[] inputBuffer, int start, int end, boolean bufferRecyclable) { super(ctxt, features); _inputStream = in; _objectCodec = codec; _symbols = sym; _inputBuffer = inputBuffer; _inputPtr = start; _inputEnd = end; _bufferRecyclable = bufferRecyclable; } @Override public ObjectCodec getCodec() { return _objectCodec; } @Override public void setCodec(ObjectCodec c) { _objectCodec = c; } /* /********************************************************** /* Overrides /********************************************************** */ @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; } /* /********************************************************** /* Low-level reading, other /********************************************************** */ @Override protected boolean loadMore() throws IOException { _currInputProcessed += _inputEnd; _currInputRowStart -= _inputEnd; if (_inputStream != null) { int count = _inputStream.read(_inputBuffer, 0, _inputBuffer.length); if (count > 0) { _inputPtr = 0; _inputEnd = count; return true; } // End of input _closeInput(); // Should never return 0, so let's fail if (count == 0) { throw new IOException("InputStream.read() returned 0 characters when trying to read "+_inputBuffer.length+" bytes"); } } return false; } /** * Helper method that will try to load at least specified number bytes in * input buffer, possible moving existing data around if necessary */ protected boolean _loadToHaveAtLeast(int minAvailable) throws IOException { // No input stream, no leading (either we are closed, or have non-stream input source) if (_inputStream == null) { return false; } // Need to move remaining data in front? int amount = _inputEnd - _inputPtr; if (amount > 0 && _inputPtr > 0) { _currInputProcessed += _inputPtr; _currInputRowStart -= _inputPtr; System.arraycopy(_inputBuffer, _inputPtr, _inputBuffer, 0, amount); _inputEnd = amount; } else { _inputEnd = 0; } _inputPtr = 0; while (_inputEnd < minAvailable) { int count = _inputStream.read(_inputBuffer, _inputEnd, _inputBuffer.length - _inputEnd); if (count < 1) { // 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 "+amount+" bytes"); } return false; } _inputEnd += count; } return true; } @Override protected void _closeInput() throws IOException { /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close() * on the underlying InputStream, unless we "own" it, or auto-closing * feature is enabled. */ 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(); if (_bufferRecyclable) { byte[] buf = _inputBuffer; if (buf != null) { _inputBuffer = null; _ioContext.releaseReadIOBuffer(buf); } } } /* /********************************************************** /* Public API, data access /********************************************************** */ @Override public String getText() throws IOException, JsonParseException { if (_currToken == JsonToken.VALUE_STRING) { if (_tokenIncomplete) { _tokenIncomplete = false; _finishString(); // only strings can be incomplete } return _textBuffer.contentsAsString(); } return _getText2(_currToken); } // // // Let's override default impls for improved performance // @since 2.1 @Override public String getValueAsString() throws IOException, JsonParseException { if (_currToken == JsonToken.VALUE_STRING) { if (_tokenIncomplete) { _tokenIncomplete = false; _finishString(); // only strings can be incomplete } return _textBuffer.contentsAsString(); } return super.getValueAsString(null); } // @since 2.1 @Override public String getValueAsString(String defValue) throws IOException, JsonParseException { if (_currToken == JsonToken.VALUE_STRING) { if (_tokenIncomplete) { _tokenIncomplete = false; _finishString(); // only strings can be incomplete } return _textBuffer.contentsAsString(); } return super.getValueAsString(defValue); } protected String _getText2(JsonToken t) { if (t == null) { return null; } switch (t) { case FIELD_NAME: return _parsingContext.getCurrentName(); case VALUE_STRING: // fall through case VALUE_NUMBER_INT: case VALUE_NUMBER_FLOAT: return _textBuffer.contentsAsString(); default: return t.asString(); } } @Override public char[] getTextCharacters() throws IOException, JsonParseException { if (_currToken != null) { // null only before/after document switch (_currToken) { case 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 VALUE_STRING: if (_tokenIncomplete) { _tokenIncomplete = false; _finishString(); // only strings can be incomplete } // fall through case VALUE_NUMBER_INT: case VALUE_NUMBER_FLOAT: return _textBuffer.getTextBuffer(); default: return _currToken.asCharArray(); } } return null; } @Override public int getTextLength() throws IOException, JsonParseException { if (_currToken != null) { // null only before/after document switch (_currToken) { case FIELD_NAME: return _parsingContext.getCurrentName().length(); case VALUE_STRING: if (_tokenIncomplete) { _tokenIncomplete = false; _finishString(); // only strings can be incomplete } // fall through case VALUE_NUMBER_INT: case VALUE_NUMBER_FLOAT: return _textBuffer.size(); default: return _currToken.asCharArray().length; } } return 0; } @Override public int getTextOffset() throws IOException, JsonParseException { // Most have offset of 0, only some may have other values: if (_currToken != null) { switch (_currToken) { case FIELD_NAME: return 0; case VALUE_STRING: if (_tokenIncomplete) { _tokenIncomplete = false; _finishString(); // only strings can be incomplete } // fall through case VALUE_NUMBER_INT: case VALUE_NUMBER_FLOAT: return _textBuffer.getTextOffset(); default: } } return 0; } @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException { 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) { ByteArrayBuilder builder = _getByteArrayBuilder(); _decodeBase64(getText(), builder, b64variant); _binaryValue = builder.toByteArray(); } } return _binaryValue; } @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException, JsonParseException { // 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, JsonParseException { 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 == '"' && !b64variant.usesPadding()) { decodedData >>= 4; buffer[outputPtr++] = (byte) decodedData; 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)) { 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 == '"' && !b64variant.usesPadding()) { decodedData >>= 2; buffer[outputPtr++] = (byte) (decodedData >> 8); buffer[outputPtr++] = (byte) decodedData; break; } bits = _decodeBase64Escape(b64variant, ch, 3); } if (bits == Base64Variant.BASE64_VALUE_PADDING) { /* With padding we only get 2 bytes; but we have * to shift it a bit so it is identical to triplet * case with partial output. * 3 chars gives 3x6 == 18 bits, of which 2 are * dummies, need to discard: */ decodedData >>= 2; buffer[outputPtr++] = (byte) (decodedData >> 8); buffer[outputPtr++] = (byte) decodedData; continue; } } // otherwise, our triplet is now complete decodedData = (decodedData << 6) | bits; buffer[outputPtr++] = (byte) (decodedData >> 16); buffer[outputPtr++] = (byte) (decodedData >> 8); buffer[outputPtr++] = (byte) decodedData; } _tokenIncomplete = false; if (outputPtr > 0) { outputCount += outputPtr; out.write(buffer, 0, outputPtr); } return outputCount; } /* /********************************************************** /* Public API, traversal, basic /********************************************************** */ /** * @return Next token from the stream, if any found, or null * to indicate end-of-input */ @Override public JsonToken nextToken() throws IOException, JsonParseException { _numTypesValid = NR_UNKNOWN; /* 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(); } if (_tokenIncomplete) { _skipString(); // only strings can be partial } int i = _skipWSOrEnd(); if (i < 0) { // end-of-input /* 19-Feb-2009, tatu: Should actually close/release things * like input source, symbol table and recyclable buffers now. */ close(); return (_currToken = null); } /* First, need to ensure we know the starting location of token * after skipping leading white space */ _tokenInputTotal = _currInputProcessed + _inputPtr - 1; _tokenInputRow = _currInputRow; _tokenInputCol = _inputPtr - _currInputRowStart - 1; // finally: clear any data retained so far _binaryValue = null; // Closing scope? if (i == INT_RBRACKET) { if (!_parsingContext.inArray()) { _reportMismatchedEndMarker(i, '}'); } _parsingContext = _parsingContext.getParent(); return (_currToken = JsonToken.END_ARRAY); } if (i == INT_RCURLY) { if (!_parsingContext.inObject()) { _reportMismatchedEndMarker(i, ']'); } _parsingContext = _parsingContext.getParent(); 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.getTypeDesc()+" entries"); } i = _skipWS(); } /* And should we now have a name? Always true for * Object contexts, since the intermediate 'expect-value' * state is never retained. */ if (!_parsingContext.inObject()) { return _nextTokenNotInObject(i); } // So first parse the field name itself: Name n = _parseFieldName(i); _parsingContext.setCurrentName(n.getName()); _currToken = JsonToken.FIELD_NAME; i = _skipWS(); if (i != INT_COLON) { _reportUnexpectedChar(i, "was expecting a colon to separate field name and value"); } i = _skipWS(); // 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 INT_LBRACKET: t = JsonToken.START_ARRAY; break; case INT_LCURLY: t = JsonToken.START_OBJECT; break; case INT_RBRACKET: case INT_RCURLY: // Error: neither is valid at this point; valid closers have // been handled earlier _reportUnexpectedChar(i, "expected a value"); case INT_t: _matchToken("true", 1); t = JsonToken.VALUE_TRUE; break; case INT_f: _matchToken("false", 1); t = JsonToken.VALUE_FALSE; break; case INT_n: _matchToken("null", 1); t = JsonToken.VALUE_NULL; break; case INT_MINUS: /* 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 INT_0: case INT_1: case INT_2: case INT_3: case INT_4: case INT_5: case INT_6: case INT_7: case INT_8: case INT_9: t = parseNumberText(i); break; default: t = _handleUnexpectedValue(i); } _nextToken = t; return _currToken; } private JsonToken _nextTokenNotInObject(int i) throws IOException, JsonParseException { if (i == INT_QUOTE) { _tokenIncomplete = true; return (_currToken = JsonToken.VALUE_STRING); } switch (i) { case INT_LBRACKET: _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol); return (_currToken = JsonToken.START_ARRAY); case INT_LCURLY: _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol); return (_currToken = JsonToken.START_OBJECT); case INT_RBRACKET: case INT_RCURLY: // Error: neither is valid at this point; valid closers have // been handled earlier _reportUnexpectedChar(i, "expected a value"); case INT_t: _matchToken("true", 1); return (_currToken = JsonToken.VALUE_TRUE); case INT_f: _matchToken("false", 1); return (_currToken = JsonToken.VALUE_FALSE); case INT_n: _matchToken("null", 1); return (_currToken = JsonToken.VALUE_NULL); case INT_MINUS: /* 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 INT_0: case INT_1: case INT_2: case INT_3: case INT_4: case INT_5: case INT_6: case INT_7: case INT_8: case INT_9: return (_currToken = parseNumberText(i)); } return (_currToken = _handleUnexpectedValue(i)); } private 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 close() throws IOException { super.close(); // Merge found symbols, if any: _symbols.release(); } /* /********************************************************** /* Public API, traversal, nextXxxValue/nextFieldName /********************************************************** */ @Override public boolean nextFieldName(SerializableString str) throws IOException, JsonParseException { // // // 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; } _tokenInputTotal = _currInputProcessed + _inputPtr - 1; _tokenInputRow = _currInputRow; _tokenInputCol = _inputPtr - _currInputRowStart - 1; // finally: clear any data retained so far _binaryValue = null; // Closing scope? if (i == INT_RBRACKET) { if (!_parsingContext.inArray()) { _reportMismatchedEndMarker(i, '}'); } _parsingContext = _parsingContext.getParent(); _currToken = JsonToken.END_ARRAY; return false; } if (i == INT_RCURLY) { if (!_parsingContext.inObject()) { _reportMismatchedEndMarker(i, ']'); } _parsingContext = _parsingContext.getParent(); _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.getTypeDesc()+" entries"); } i = _skipWS(); } if (!_parsingContext.inObject()) { _nextTokenNotInObject(i); return false; } // // // This part differs, name parsing if (i == INT_QUOTE) { // when doing literal match, must consider escaping: byte[] nameBytes = str.asQuotedUTF8(); final int len = nameBytes.length; if ((_inputPtr + len) < _inputEnd) { // maybe... // first check length match by final int end = _inputPtr+len; if (_inputBuffer[end] == INT_QUOTE) { int offset = 0; final int ptr = _inputPtr; while (true) { if (offset == len) { // yes, match! _inputPtr = end+1; // skip current value first // First part is simple; setting of name _parsingContext.setCurrentName(str.getValue()); _currToken = JsonToken.FIELD_NAME; // But then we also must handle following value etc _isNextTokenNameYes(); return true; } if (nameBytes[offset] != _inputBuffer[ptr+offset]) { break; } ++offset; } } } } return _isNextTokenNameMaybe(i, str); } private void _isNextTokenNameYes() throws IOException, JsonParseException { // very first thing: common case, colon, value, no white space int i; if (_inputPtr < (_inputEnd-1) && _inputBuffer[_inputPtr] == INT_COLON) { // fast case first i = _inputBuffer[++_inputPtr]; ++_inputPtr; if (i == INT_QUOTE) { _tokenIncomplete = true; _nextToken = JsonToken.VALUE_STRING; return; } if (i == INT_LCURLY) { _nextToken = JsonToken.START_OBJECT; return; } if (i == INT_LBRACKET) { _nextToken = JsonToken.START_ARRAY; return; } i &= 0xFF; if (i <= INT_SPACE || i == INT_SLASH) { --_inputPtr; i = _skipWS(); } } else { i = _skipColon(); } switch (i) { case INT_QUOTE: _tokenIncomplete = true; _nextToken = JsonToken.VALUE_STRING; return; case INT_LBRACKET: _nextToken = JsonToken.START_ARRAY; return; case INT_LCURLY: _nextToken = JsonToken.START_OBJECT; return; case INT_RBRACKET: case INT_RCURLY: _reportUnexpectedChar(i, "expected a value"); case INT_t: _matchToken("true", 1); _nextToken = JsonToken.VALUE_TRUE; return; case INT_f: _matchToken("false", 1); _nextToken = JsonToken.VALUE_FALSE; return; case INT_n: _matchToken("null", 1); _nextToken = JsonToken.VALUE_NULL; return; case INT_MINUS: case INT_0: case INT_1: case INT_2: case INT_3: case INT_4: case INT_5: case INT_6: case INT_7: case INT_8: case INT_9: _nextToken = parseNumberText(i); return; } _nextToken = _handleUnexpectedValue(i); } private boolean _isNextTokenNameMaybe(int i, SerializableString str) throws IOException, JsonParseException { // // // and this is back to standard nextToken() Name n = _parseFieldName(i); final boolean match; { String nameStr = n.getName(); _parsingContext.setCurrentName(nameStr); match = nameStr.equals(str.getValue()); } _currToken = JsonToken.FIELD_NAME; i = _skipWS(); if (i != INT_COLON) { _reportUnexpectedChar(i, "was expecting a colon to separate field name and value"); } i = _skipWS(); // 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 INT_LBRACKET: t = JsonToken.START_ARRAY; break; case INT_LCURLY: t = JsonToken.START_OBJECT; break; case INT_RBRACKET: case INT_RCURLY: _reportUnexpectedChar(i, "expected a value"); case INT_t: _matchToken("true", 1); t = JsonToken.VALUE_TRUE; break; case INT_f: _matchToken("false", 1); t = JsonToken.VALUE_FALSE; break; case INT_n: _matchToken("null", 1); t = JsonToken.VALUE_NULL; break; case INT_MINUS: case INT_0: case INT_1: case INT_2: case INT_3: case INT_4: case INT_5: case INT_6: case INT_7: case INT_8: case INT_9: t = parseNumberText(i); break; default: t = _handleUnexpectedValue(i); } _nextToken = t; return match; } @Override public String nextTextValue() throws IOException, JsonParseException { // 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; _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; } @Override public int nextIntValue(int defaultValue) throws IOException, JsonParseException { // 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, JsonParseException { // 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, JsonParseException { // 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; } switch (nextToken()) { case VALUE_TRUE: return Boolean.TRUE; case VALUE_FALSE: return Boolean.FALSE; default: return null; } } /* /********************************************************** /* Internal methods, number parsing /* (note: in 1.6 and prior, part of "Utf8NumericParser" /********************************************************** */ /** * 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 parseNumberText(int c) throws IOException, JsonParseException { char[] outBuf = _textBuffer.emptyAndGetCurrentSegment(); int outPtr = 0; boolean negative = (c == INT_MINUS); // Need to prepend sign? if (negative) { outBuf[outPtr++] = '-'; // Must have something after sign too if (_inputPtr >= _inputEnd) { loadMoreGuaranteed(); } c = (int) _inputBuffer[_inputPtr++] & 0xFF; // Note: must be followed by a digit if (c < INT_0 || c > INT_9) { return _handleInvalidNumberStart(c, true); } } // 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[outPtr++] = (char) c; int intLen = 1; // And then figure out how far we can read without further checks: int end = _inputPtr + outBuf.length; if (end > _inputEnd) { end = _inputEnd; } // With this, we have a nice and tight loop: while (true) { if (_inputPtr >= end) { // Long enough to be split across boundary, so: return _parserNumber2(outBuf, outPtr, negative, intLen); } c = (int) _inputBuffer[_inputPtr++] & 0xFF; if (c < INT_0 || c > INT_9) { break; } ++intLen; outBuf[outPtr++] = (char) c; } if (c == '.' || c == 'e' || c == 'E') { return _parseFloatText(outBuf, outPtr, c, negative, intLen); } --_inputPtr; // to push back trailing char (comma etc) _textBuffer.setCurrentLength(outPtr); // And there we have it! return resetInt(negative, intLen); } /** * Method called to handle parsing when input is split across buffer boundary * (or output is longer than segment used to store it) */ private JsonToken _parserNumber2(char[] outBuf, int outPtr, boolean negative, int intPartLength) throws IOException, JsonParseException { // 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 == '.' || c == 'e' || c == 'E') { return _parseFloatText(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); // 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 int _verifyNoLeadingZeroes() throws IOException, JsonParseException { // 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 (!isEnabled(Feature.ALLOW_NUMERIC_LEADING_ZEROS)) { reportInvalidNumber("Leading zeroes not allowed"); } // if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number) ++_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 JsonToken _parseFloatText(char[] outBuf, int outPtr, int c, boolean negative, int integerPartLength) throws IOException, JsonParseException { int fractLen = 0; boolean eof = false; // And then see if we get other parts if (c == '.') { // yes, fraction 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 == 'e' || c == '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_9 && c >= INT_0) { ++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; } _textBuffer.setCurrentLength(outPtr); // And there we have it! return resetFloat(negative, integerPartLength, fractLen, expLen); } /* /********************************************************** /* Internal methods, secondary parsing /********************************************************** */ protected Name _parseFieldName(int i) throws IOException, JsonParseException { if (i != INT_QUOTE) { return _handleUnusualFieldName(i); } // First: can we optimize out bounds checks? if ((_inputPtr + 9) > _inputEnd) { // Need 8 chars, plus one trailing (quote) return slowParseFieldName(); } // 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 = sInputCodesLatin1; 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 parseMediumFieldName(i, codes); } if (i == INT_QUOTE) { // one byte/char case or broken return findName(q, 4); } return parseFieldName(q, i, 4); } if (i == INT_QUOTE) { // one byte/char case or broken return findName(q, 3); } return parseFieldName(q, i, 3); } if (i == INT_QUOTE) { // one byte/char case or broken return findName(q, 2); } return parseFieldName(q, i, 2); } if (i == INT_QUOTE) { // one byte/char case or broken return findName(q, 1); } return parseFieldName(q, i, 1); } if (q == INT_QUOTE) { // special case, "" return BytesToNameCanonicalizer.getEmptyName(); } return parseFieldName(0, q, 0); // quoting or invalid char } protected Name parseMediumFieldName(int q2, final int[] codes) throws IOException, JsonParseException { // Ok, got 5 name bytes so far int i = _inputBuffer[_inputPtr++] & 0xFF; if (codes[i] != 0) { if (i == INT_QUOTE) { // 5 bytes return findName(_quad1, q2, 1); } return parseFieldName(_quad1, q2, i, 1); // quoting or invalid char } q2 = (q2 << 8) | i; i = _inputBuffer[_inputPtr++] & 0xFF; if (codes[i] != 0) { if (i == INT_QUOTE) { // 6 bytes return findName(_quad1, q2, 2); } return parseFieldName(_quad1, q2, i, 2); } q2 = (q2 << 8) | i; i = _inputBuffer[_inputPtr++] & 0xFF; if (codes[i] != 0) { if (i == INT_QUOTE) { // 7 bytes return findName(_quad1, q2, 3); } return parseFieldName(_quad1, q2, i, 3); } q2 = (q2 << 8) | i; i = _inputBuffer[_inputPtr++] & 0xFF; if (codes[i] != 0) { if (i == INT_QUOTE) { // 8 bytes return findName(_quad1, q2, 4); } return parseFieldName(_quad1, q2, i, 4); } _quadBuffer[0] = _quad1; _quadBuffer[1] = q2; return parseLongFieldName(i); } protected Name parseLongFieldName(int q) throws IOException, JsonParseException { // As explained above, will ignore UTF-8 encoding at this point final int[] codes = sInputCodesLatin1; int qlen = 2; while (true) { /* 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) */ if ((_inputEnd - _inputPtr) < 4) { return parseEscapedFieldName(_quadBuffer, qlen, 0, q, 0); } // Otherwise can skip boundary checks for 4 bytes in loop int i = _inputBuffer[_inputPtr++] & 0xFF; if (codes[i] != 0) { if (i == INT_QUOTE) { return findName(_quadBuffer, qlen, q, 1); } return parseEscapedFieldName(_quadBuffer, qlen, q, i, 1); } q = (q << 8) | i; i = _inputBuffer[_inputPtr++] & 0xFF; if (codes[i] != 0) { if (i == INT_QUOTE) { return findName(_quadBuffer, qlen, q, 2); } return parseEscapedFieldName(_quadBuffer, qlen, q, i, 2); } q = (q << 8) | i; i = _inputBuffer[_inputPtr++] & 0xFF; if (codes[i] != 0) { if (i == INT_QUOTE) { return findName(_quadBuffer, qlen, q, 3); } return parseEscapedFieldName(_quadBuffer, qlen, q, i, 3); } q = (q << 8) | i; i = _inputBuffer[_inputPtr++] & 0xFF; if (codes[i] != 0) { if (i == INT_QUOTE) { return findName(_quadBuffer, qlen, q, 4); } return parseEscapedFieldName(_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; } } /** * Method called when not even first 8 bytes are guaranteed * to come consequtively. Happens rarely, so this is offlined; * plus we'll also do full checks for escaping etc. */ protected Name slowParseFieldName() throws IOException, JsonParseException { if (_inputPtr >= _inputEnd) { if (!loadMore()) { _reportInvalidEOF(": was expecting closing '\"' for name"); } } int i = _inputBuffer[_inputPtr++] & 0xFF; if (i == INT_QUOTE) { // special case, "" return BytesToNameCanonicalizer.getEmptyName(); } return parseEscapedFieldName(_quadBuffer, 0, 0, i, 0); } private Name parseFieldName(int q1, int ch, int lastQuadBytes) throws IOException, JsonParseException { return parseEscapedFieldName(_quadBuffer, 0, q1, ch, lastQuadBytes); } private Name parseFieldName(int q1, int q2, int ch, int lastQuadBytes) throws IOException, JsonParseException { _quadBuffer[0] = q1; return parseEscapedFieldName(_quadBuffer, 1, q2, ch, lastQuadBytes); } /** * Slower parsing method which is generally branched to when * an escape sequence is detected (or alternatively for long * names, or ones crossing input buffer boundary). In any case, * needs to be able to handle more exceptional cases, gets * slower, and hance is offlined to a separate method. */ protected Name parseEscapedFieldName(int[] quads, int qlen, int currQuad, int ch, int currQuadBytes) throws IOException, JsonParseException { /* 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 = sInputCodesLatin1; 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"); } } ch = _inputBuffer[_inputPtr++] & 0xFF; } if (currQuadBytes > 0) { if (qlen >= quads.length) { _quadBuffer = quads = growArrayBy(quads, quads.length); } quads[qlen++] = currQuad; } Name 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 expection; but * in non-standard modes may be able to parse name. */ protected Name _handleUnusualFieldName(int ch) throws IOException, JsonParseException { // [JACKSON-173]: allow single quotes if (ch == INT_APOSTROPHE && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { return _parseApostropheFieldName(); } // [JACKSON-69]: allow unquoted names if feature enabled: if (!isEnabled(Feature.ALLOW_UNQUOTED_FIELD_NAMES)) { _reportUnexpectedChar(ch, "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"); } } 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; } Name 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 Name _parseApostropheFieldName() throws IOException, JsonParseException { if (_inputPtr >= _inputEnd) { if (!loadMore()) { _reportInvalidEOF(": was expecting closing '\'' for name"); } } int ch = _inputBuffer[_inputPtr++] & 0xFF; if (ch == INT_APOSTROPHE) { // special case, '' return BytesToNameCanonicalizer.getEmptyName(); } int[] quads = _quadBuffer; int qlen = 0; int currQuad = 0; int currQuadBytes = 0; // Copied from parseEscapedFieldName, with minor mods: final int[] codes = sInputCodesLatin1; while (true) { if (ch == INT_APOSTROPHE) { break; } // additional check to skip handling of double-quotes if (ch != INT_QUOTE && codes[ch] != 0) { if (ch != INT_BACKSLASH) { // 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; } if (_inputPtr >= _inputEnd) { if (!loadMore()) { _reportInvalidEOF(" in field name"); } } ch = _inputBuffer[_inputPtr++] & 0xFF; } if (currQuadBytes > 0) { if (qlen >= quads.length) { _quadBuffer = quads = growArrayBy(quads, quads.length); } quads[qlen++] = currQuad; } Name name = _symbols.findName(quads, qlen); if (name == null) { name = addName(quads, qlen, currQuadBytes); } return name; } /* /********************************************************** /* Internal methods, symbol (name) handling /********************************************************** */ private Name findName(int q1, int lastQuadBytes) throws JsonParseException { // Usually we'll find it from the canonical symbol table already Name 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 Name findName(int q1, int q2, int lastQuadBytes) throws JsonParseException { // Usually we'll find it from the canonical symbol table already Name 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 Name findName(int[] quads, int qlen, int lastQuad, int lastQuadBytes) throws JsonParseException { if (qlen >= quads.length) { _quadBuffer = quads = growArrayBy(quads, quads.length); } quads[qlen++] = lastQuad; Name 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 Name 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"); } // 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, JsonParseException { // 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 = sInputCodesUtf8; 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); } private void _finishString2(char[] outBuf, int outPtr) throws IOException, JsonParseException { int c; // Here we do want to do full decoding, hence: final int[] codes = sInputCodesUtf8; 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, JsonParseException { _tokenIncomplete = false; // Need to be fully UTF-8 aware here: final int[] codes = sInputCodesUtf8; 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(c); break; case 3: // 3-byte UTF _skipUtf8_3(c); break; case 4: // 4-byte UTF _skipUtf8_4(c); 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); } } } } /** * 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, JsonParseException { // Most likely an error, unless we are to allow single-quote-strings switch (c) { case '\'': if (isEnabled(Feature.ALLOW_SINGLE_QUOTES)) { return _handleApostropheValue(); } break; case 'N': _matchToken("NaN", 1); if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { return resetAsNaN("NaN", Double.NaN); } _reportError("Non-standard token 'NaN': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); break; case '+': // note: '-' is taken as number if (_inputPtr >= _inputEnd) { if (!loadMore()) { _reportInvalidEOFInValue(); } } return _handleInvalidNumberStart(_inputBuffer[_inputPtr++] & 0xFF, false); } _reportUnexpectedChar(c, "expected a valid value (number, String, array, object, 'true', 'false' or 'null')"); return null; } protected JsonToken _handleApostropheValue() throws IOException, JsonParseException { 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 = sInputCodesUtf8; 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_APOSTROPHE || codes[c] != 0) { break ascii_loop; } outBuf[outPtr++] = (char) c; } } // Ok: end marker, escape or multi-byte? if (c == INT_APOSTROPHE) { break main_loop; } switch (codes[c]) { case 1: // backslash if (c != INT_QUOTE) { // marked as special, isn't here 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; } /** * 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, JsonParseException { while (ch == 'I') { if (_inputPtr >= _inputEnd) { if (!loadMore()) { _reportInvalidEOFInValue(); } } 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 (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) { return resetAsNaN(match, neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY); } _reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow"); } reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value"); return null; } protected void _matchToken(String matchStr, int i) throws IOException, JsonParseException { 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 return; } // but actually only alphanums are problematic char c = (char) _decodeCharForError(ch); if (Character.isJavaIdentifierPart(c)) { _reportInvalidToken(matchStr.substring(0, i)); } } protected void _reportInvalidToken(String matchedPart) throws IOException, JsonParseException { _reportInvalidToken(matchedPart, "'null', 'true', 'false' or NaN"); } protected void _reportInvalidToken(String matchedPart, String msg) throws IOException, JsonParseException { StringBuilder sb = new StringBuilder(matchedPart); /* Let's just try to find what appears to be the token, using * regular Java identifier character rules. It's just a heuristic, * nothing fancy here (nor fast). */ while (true) { if (_inputPtr >= _inputEnd && !loadMore()) { break; } int i = (int) _inputBuffer[_inputPtr++]; char c = (char) _decodeCharForError(i); if (!Character.isJavaIdentifierPart(c)) { break; } sb.append(c); } _reportError("Unrecognized token '"+sb.toString()+"': was expecting "+msg); } /* /********************************************************** /* Internal methods, ws skipping, escape/unescape /********************************************************** */ private int _skipWS() throws IOException, JsonParseException { while (_inputPtr < _inputEnd || loadMore()) { int i = _inputBuffer[_inputPtr++] & 0xFF; if (i > INT_SPACE) { if (i != INT_SLASH) { return i; } _skipComment(); } else if (i != INT_SPACE) { if (i == INT_LF) { _skipLF(); } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } throw _constructError("Unexpected end-of-input within/between "+_parsingContext.getTypeDesc()+" entries"); } private int _skipWSOrEnd() throws IOException, JsonParseException { while ((_inputPtr < _inputEnd) || loadMore()) { int i = _inputBuffer[_inputPtr++] & 0xFF; if (i > INT_SPACE) { if (i != INT_SLASH) { return i; } _skipComment(); } else if (i != INT_SPACE) { if (i == INT_LF) { _skipLF(); } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } // We ran out of input... _handleEOF(); return -1; } /** * Helper method for matching and skipping a colon character, * optionally surrounded by white space */ private int _skipColon() throws IOException, JsonParseException { if (_inputPtr >= _inputEnd) { loadMoreGuaranteed(); } // first fast case: we just got a colon without white space: int i = _inputBuffer[_inputPtr++]; if (i == INT_COLON) { if (_inputPtr < _inputEnd) { i = _inputBuffer[_inputPtr] & 0xFF; if (i > INT_SPACE && i != INT_SLASH) { ++_inputPtr; return i; } } } else { // need to skip potential leading space i &= 0xFF; space_loop: while (true) { switch (i) { case INT_SPACE: case INT_TAB: break; case INT_CR: _skipCR(); break; case INT_LF: _skipLF(); break; case INT_SLASH: _skipComment(); break; default: if (i < INT_SPACE) { _throwInvalidSpace(i); } break space_loop; } if (_inputPtr >= _inputEnd) { loadMoreGuaranteed(); } i = _inputBuffer[_inputPtr++] & 0xFF; } if (i != INT_COLON) { _reportUnexpectedChar(i, "was expecting a colon to separate field name and value"); } } // either way, found colon, skip through trailing WS while (_inputPtr < _inputEnd || loadMore()) { i = _inputBuffer[_inputPtr++] & 0xFF; if (i > INT_SPACE) { if (i != INT_SLASH) { return i; } _skipComment(); } else if (i != INT_SPACE) { if (i == INT_LF) { _skipLF(); } else if (i == INT_CR) { _skipCR(); } else if (i != INT_TAB) { _throwInvalidSpace(i); } } } throw _constructError("Unexpected end-of-input within/between "+_parsingContext.getTypeDesc()+" entries"); } private void _skipComment() throws IOException, JsonParseException { if (!isEnabled(Feature.ALLOW_COMMENTS)) { _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"); } int c = _inputBuffer[_inputPtr++] & 0xFF; if (c == INT_SLASH) { _skipCppComment(); } else if (c == INT_ASTERISK) { _skipCComment(); } else { _reportUnexpectedChar(c, "was expecting either '*' or '/' for a comment"); } } private void _skipCComment() throws IOException, JsonParseException { // 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 INT_ASTERISK: if (_inputPtr >= _inputEnd && !loadMore()) { break main_loop; } if (_inputBuffer[_inputPtr] == INT_SLASH) { ++_inputPtr; return; } break; case INT_LF: _skipLF(); break; case INT_CR: _skipCR(); break; case 2: // 2-byte UTF _skipUtf8_2(i); break; case 3: // 3-byte UTF _skipUtf8_3(i); 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"); } private void _skipCppComment() throws IOException, JsonParseException { // 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: _skipLF(); return; case INT_CR: _skipCR(); return; case INT_ASTERISK: // nop for these comments break; case 2: // 2-byte UTF _skipUtf8_2(i); break; case 3: // 3-byte UTF _skipUtf8_3(i); break; case 4: // 4-byte UTF _skipUtf8_4(i); break; default: // e.g. -1 // Is this good enough error message? _reportInvalidChar(i); } } } } @Override protected char _decodeEscaped() throws IOException, JsonParseException { if (_inputPtr >= _inputEnd) { if (!loadMore()) { _reportInvalidEOF(" in character escape sequence"); } } int c = (int) _inputBuffer[_inputPtr++]; switch ((int) c) { // First, ones that are mapped case INT_b: return '\b'; case INT_t: return '\t'; case INT_n: return '\n'; case INT_f: return '\f'; case INT_r: return '\r'; // And these are to be returned as they are case INT_QUOTE: case INT_SLASH: case INT_BACKSLASH: return (char) c; case INT_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"); } } 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; } protected int _decodeCharForError(int firstByte) throws IOException, JsonParseException { int c = (int) firstByte; if (c < 0) { // 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 int _decodeUtf8_2(int c) throws IOException, JsonParseException { if (_inputPtr >= _inputEnd) { loadMoreGuaranteed(); } int d = (int) _inputBuffer[_inputPtr++]; if ((d & 0xC0) != 0x080) { _reportInvalidOther(d & 0xFF, _inputPtr); } return ((c & 0x1F) << 6) | (d & 0x3F); } private int _decodeUtf8_3(int c1) throws IOException, JsonParseException { 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 int _decodeUtf8_3fast(int c1) throws IOException, JsonParseException { 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 int _decodeUtf8_4(int c) throws IOException, JsonParseException { 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 void _skipUtf8_2(int c) throws IOException, JsonParseException { if (_inputPtr >= _inputEnd) { loadMoreGuaranteed(); } 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 void _skipUtf8_3(int c) throws IOException, JsonParseException { if (_inputPtr >= _inputEnd) { loadMoreGuaranteed(); } //c &= 0x0F; 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 void _skipUtf8_4(int c) throws IOException, JsonParseException { 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 void _skipCR() throws IOException { if (_inputPtr < _inputEnd || loadMore()) { if (_inputBuffer[_inputPtr] == BYTE_LF) { ++_inputPtr; } } ++_currInputRow; _currInputRowStart = _inputPtr; } protected void _skipLF() throws IOException { ++_currInputRow; _currInputRowStart = _inputPtr; } private int nextByte() throws IOException, JsonParseException { if (_inputPtr >= _inputEnd) { loadMoreGuaranteed(); } return _inputBuffer[_inputPtr++] & 0xFF; } /* /********************************************************** /* Internal methods, error reporting /********************************************************** */ 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); } public static int[] growArrayBy(int[] arr, int more) { if (arr == null) { return new int[more]; } int[] old = arr; int len = arr.length; arr = new int[len + more]; System.arraycopy(old, 0, arr, 0, len); return arr; } /* /********************************************************** /* Binary access /********************************************************** */ /** * Efficient handling for incremental parsing of base64-encoded * textual content. */ protected byte[] _decodeBase64(Base64Variant b64variant) throws IOException, JsonParseException { ByteArrayBuilder builder = _getByteArrayBuilder(); //main_loop: 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) { // as per [JACKSON-631], could also just be 'missing' padding if (ch == '"' && !b64variant.usesPadding()) { decodedData >>= 4; builder.append(decodedData); return builder.toByteArray(); } bits = _decodeBase64Escape(b64variant, ch, 2); } if (bits == Base64Variant.BASE64_VALUE_PADDING) { // Ok, must get padding if (_inputPtr >= _inputEnd) { loadMoreGuaranteed(); } ch = _inputBuffer[_inputPtr++] & 0xFF; if (!b64variant.usesPaddingChar(ch)) { throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'"); } // Got 12 bits, only need 8, need to shift decodedData >>= 4; builder.append(decodedData); continue; } } // Nope, 2 or 3 bytes decodedData = (decodedData << 6) | bits; // fourth and last base64 char; can be padding, but not ws 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 == '"' && !b64variant.usesPadding()) { decodedData >>= 2; builder.appendTwoBytes(decodedData); return builder.toByteArray(); } bits = _decodeBase64Escape(b64variant, ch, 3); } if (bits == Base64Variant.BASE64_VALUE_PADDING) { /* With padding we only get 2 bytes; but we have * to shift it a bit so it is identical to triplet * case with partial output. * 3 chars gives 3x6 == 18 bits, of which 2 are * dummies, need to discard: */ decodedData >>= 2; builder.appendTwoBytes(decodedData); continue; } } // otherwise, our triplet is now complete decodedData = (decodedData << 6) | bits; builder.appendThreeBytes(decodedData); } } } WriterBasedJsonGenerator.java000066400000000000000000001774001215056622200347310ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.*; /** * {@link JsonGenerator} that outputs JSON content using a {@link java.io.Writer} * which handles character encoding. */ public final class WriterBasedJsonGenerator extends JsonGeneratorImpl { final protected static int SHORT_WRITE = 32; final protected static char[] HEX_CHARS = CharTypes.copyHexChars(); /* /********************************************************** /* Output buffering /********************************************************** */ final protected Writer _writer; /** * 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 = 0; /** * 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 = 0; /** * 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; /* /********************************************************** /* Life-cycle /********************************************************** */ public WriterBasedJsonGenerator(IOContext ctxt, int features, ObjectCodec codec, Writer w) { super(ctxt, features, codec); _writer = w; _outputBuffer = ctxt.allocConcatBuffer(); _outputEnd = _outputBuffer.length; } /* /********************************************************** /* Overridden configuration methods /********************************************************** */ @Override public Object getOutputTarget() { return _writer; } /* /********************************************************** /* Overridden methods /********************************************************** */ @Override public void writeFieldName(String name) throws IOException, JsonGenerationException { 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, JsonGenerationException { // 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)); } /* /********************************************************** /* Output method implementations, structural /********************************************************** */ @Override public void writeStartArray() throws IOException, JsonGenerationException { _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, JsonGenerationException { if (!_writeContext.inArray()) { _reportError("Current context not an ARRAY but "+_writeContext.getTypeDesc()); } if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount()); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = ']'; } _writeContext = _writeContext.getParent(); } @Override public void writeStartObject() throws IOException, JsonGenerationException { _verifyValueWrite("start an object"); _writeContext = _writeContext.createChildObjectContext(); if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeStartObject(this); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '{'; } } @Override public void writeEndObject() throws IOException, JsonGenerationException { if (!_writeContext.inObject()) { _reportError("Current context not an object but "+_writeContext.getTypeDesc()); } if (_cfgPrettyPrinter != null) { _cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount()); } else { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '}'; } _writeContext = _writeContext.getParent(); } protected void _writeFieldName(String name, boolean commaBefore) throws IOException, JsonGenerationException { 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++] = ','; } /* To support [JACKSON-46], we'll do this: * (Question: should quoting of spaces (etc) still be enabled?) */ if (!isEnabled(Feature.QUOTE_FIELD_NAMES)) { _writeString(name); return; } // we know there's room for at least one more char _outputBuffer[_outputTail++] = '"'; // The beef: _writeString(name); // and closing quotes; need room for one more char: if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; } public void _writeFieldName(SerializableString name, boolean commaBefore) throws IOException, JsonGenerationException { 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++] = ','; } /* To support [JACKSON-46], we'll do this: * (Question: should quoting of spaces (etc) still be enabled?) */ final char[] quoted = name.asQuotedChars(); if (!isEnabled(Feature.QUOTE_FIELD_NAMES)) { writeRaw(quoted, 0, quoted.length); return; } // we know there's room for at least one more char _outputBuffer[_outputTail++] = '"'; // The beef: final int qlen = quoted.length; if ((_outputTail + qlen + 1) >= _outputEnd) { writeRaw(quoted, 0, qlen); // and closing quotes; need room for one more char: if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; } else { System.arraycopy(quoted, 0, _outputBuffer, _outputTail, qlen); _outputTail += qlen; _outputBuffer[_outputTail++] = '"'; } } /** * Specialized version of _writeFieldName, off-lined * to keep the "fast path" as simple (and hopefully fast) as possible. */ protected void _writePPFieldName(String name, boolean commaBefore) throws IOException, JsonGenerationException { if (commaBefore) { _cfgPrettyPrinter.writeObjectEntrySeparator(this); } else { _cfgPrettyPrinter.beforeObjectEntries(this); } if (isEnabled(Feature.QUOTE_FIELD_NAMES)) { // standard if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; _writeString(name); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; } else { // non-standard, omit quotes _writeString(name); } } protected void _writePPFieldName(SerializableString name, boolean commaBefore) throws IOException, JsonGenerationException { if (commaBefore) { _cfgPrettyPrinter.writeObjectEntrySeparator(this); } else { _cfgPrettyPrinter.beforeObjectEntries(this); } final char[] quoted = name.asQuotedChars(); if (isEnabled(Feature.QUOTE_FIELD_NAMES)) { // standard if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; writeRaw(quoted, 0, quoted.length); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; } else { // non-standard, omit quotes writeRaw(quoted, 0, quoted.length); } } /* /********************************************************** /* Output method implementations, textual /********************************************************** */ @Override public void writeString(String text) throws IOException, JsonGenerationException { _verifyValueWrite("write text value"); if (text == null) { _writeNull(); return; } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; _writeString(text); // And finally, closing quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; } @Override public void writeString(char[] text, int offset, int len) throws IOException, JsonGenerationException { _verifyValueWrite("write text value"); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; _writeString(text, offset, len); // And finally, closing quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; } @Override public void writeString(SerializableString sstr) throws IOException, JsonGenerationException { _verifyValueWrite("write text value"); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; // Note: copied from writeRaw: char[] text = sstr.asQuotedChars(); final int len = text.length; // Only worth buffering if it's a short write? if (len < SHORT_WRITE) { int room = _outputEnd - _outputTail; if (len > room) { _flushBuffer(); } System.arraycopy(text, 0, _outputBuffer, _outputTail, len); _outputTail += len; } else { // Otherwise, better just pass through: _flushBuffer(); _writer.write(text, 0, len); } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; } @Override public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException, JsonGenerationException { // could add support for buffering if we really want it... _reportUnsupportedOperation(); } @Override public void writeUTF8String(byte[] text, int offset, int length) throws IOException, JsonGenerationException { // could add support for buffering if we really want it... _reportUnsupportedOperation(); } /* /********************************************************** /* Output method implementations, unprocessed ("raw") /********************************************************** */ @Override public void writeRaw(String text) throws IOException, JsonGenerationException { // 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, JsonGenerationException { // 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, JsonGenerationException { writeRaw(text.getValue()); } @Override public void writeRaw(char[] text, int offset, int len) throws IOException, JsonGenerationException { // 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, JsonGenerationException { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = c; } private void writeRawLong(String text) throws IOException, JsonGenerationException { 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 value"); // Starting quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; _writeBinary(b64variant, data, offset, offset+len); // and closing quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; } @Override public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) throws IOException, JsonGenerationException { _verifyValueWrite("write binary value"); // Starting quotes if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; 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++] = '"'; return bytes; } /* /********************************************************** /* Output method implementations, primitive /********************************************************** */ @Override public void writeNumber(short s) throws IOException, JsonGenerationException { _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++] = '"'; _outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail); _outputBuffer[_outputTail++] = '"'; } @Override public void writeNumber(int i) throws IOException, JsonGenerationException { _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++] = '"'; _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail); _outputBuffer[_outputTail++] = '"'; } @Override public void writeNumber(long l) throws IOException, JsonGenerationException { _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++] = '"'; _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail); _outputBuffer[_outputTail++] = '"'; } // !!! 05-Aug-2008, tatus: Any ways to optimize these? @Override public void writeNumber(BigInteger value) throws IOException, JsonGenerationException { _verifyValueWrite("write number"); if (value == null) { _writeNull(); } else if (_cfgNumbersAsStrings) { _writeQuotedRaw(value); } else { writeRaw(value.toString()); } } @Override public void writeNumber(double d) throws IOException, JsonGenerationException { if (_cfgNumbersAsStrings || // [JACKSON-139] (((Double.isNaN(d) || Double.isInfinite(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)); } @Override public void writeNumber(float f) throws IOException, JsonGenerationException { if (_cfgNumbersAsStrings || // [JACKSON-139] (((Float.isNaN(f) || Float.isInfinite(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, JsonGenerationException { // Don't really know max length for big decimal, no point checking _verifyValueWrite("write number"); if (value == null) { _writeNull(); } else if (_cfgNumbersAsStrings) { _writeQuotedRaw(value); } else { writeRaw(value.toString()); } } @Override public void writeNumber(String encodedValue) throws IOException, JsonGenerationException { _verifyValueWrite("write number"); if (_cfgNumbersAsStrings) { _writeQuotedRaw(encodedValue); } else { writeRaw(encodedValue); } } private void _writeQuotedRaw(Object value) throws IOException { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; writeRaw(value.toString()); if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = '"'; } @Override public void writeBoolean(boolean state) throws IOException, JsonGenerationException { _verifyValueWrite("write boolean value"); 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, JsonGenerationException { _verifyValueWrite("write null value"); _writeNull(); } /* /********************************************************** /* Implementations for other methods /********************************************************** */ @Override protected void _verifyValueWrite(String typeMsg) throws IOException, JsonGenerationException { int status = _writeContext.writeValue(); if (status == JsonWriteContext.STATUS_EXPECT_NAME) { _reportError("Can not "+typeMsg+", expecting field name"); } if (_cfgPrettyPrinter == null) { char c; switch (status) { 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_OK_AS_IS: default: return; } if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail] = c; ++_outputTail; return; } // Otherwise, pretty printer knows what to do... _verifyPrettyValueWrite(typeMsg, status); } protected void _verifyPrettyValueWrite(String typeMsg, int status) throws IOException, JsonGenerationException { // If we have a pretty printer, it knows what to do: switch (status) { case JsonWriteContext.STATUS_OK_AFTER_COMMA: // array _cfgPrettyPrinter.writeArrayValueSeparator(this); break; case JsonWriteContext.STATUS_OK_AFTER_COLON: _cfgPrettyPrinter.writeObjectFieldValueSeparator(this); break; case JsonWriteContext.STATUS_OK_AFTER_SPACE: _cfgPrettyPrinter.writeRootValueSeparator(this); break; case JsonWriteContext.STATUS_OK_AS_IS: // First entry, but of which context? if (_writeContext.inArray()) { _cfgPrettyPrinter.beforeArrayValues(this); } else if (_writeContext.inObject()) { _cfgPrettyPrinter.beforeObjectEntries(this); } break; default: _throwInternal(); break; } } /* /********************************************************** /* 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(); /* 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); } } /* /********************************************************** /* Internal methods, low-level writing; text, default /********************************************************** */ private void _writeString(String text) throws IOException, JsonGenerationException { /* 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, JsonGenerationException { // And then we'll need to verify need for escaping etc: 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, JsonGenerationException { // 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, JsonGenerationException { 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, JsonGenerationException { 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 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 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 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 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 + 2) > _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; } 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.2.2/src/main/java/com/fasterxml/jackson/core/json/package-info.java000066400000000000000000000004361215056622200324120ustar00rootroot00000000000000/** * 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.2.2/src/main/java/com/fasterxml/jackson/core/package-info.java000066400000000000000000000022141215056622200314350ustar00rootroot00000000000000/** * 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.JsonParser}) * 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.2.2/src/main/java/com/fasterxml/jackson/core/sym/000077500000000000000000000000001215056622200270575ustar00rootroot00000000000000BytesToNameCanonicalizer.java000066400000000000000000001170461215056622200345500ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.util.InternCache; /** * A caching symbol table implementation used for canonicalizing JSON field * names (as {@link Name}s which are constructed directly from a byte-based * input source). * Complications arise from trying to do efficient reuse and merging of * symbol tables, to be able to make use of usually shared vocabulary * of subsequent parsing runs. * * @author Tatu Saloranta */ public final class BytesToNameCanonicalizer { protected static final int DEFAULT_TABLE_SIZE = 64; /** * Let's not expand symbol tables past some maximum size; * this should protected against OOMEs caused by large documents * with unique (~= random) names. */ protected static final int MAX_TABLE_SIZE = 0x10000; // 64k entries == 256k mem /** * Let's only share reasonably sized symbol tables. Max size set to 3/4 of 16k; * this corresponds to 64k main hash index. This should allow for enough distinct * names for almost any case. */ final static int MAX_ENTRIES_FOR_REUSE = 6000; /** * 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 255 entries * for the longest legal chain. *

* Note: longest chain we have been able to produce without malicious * intent has been 60 (with "com.fasterxml.jackson.core.main.TestWithTonsaSymbols"); * our setting should be reasonable here. * * @since 2.1 */ final static int MAX_COLL_CHAIN_LENGTH = 255; /** * And to support reduce likelihood of accidental collisions causing * exceptions, let's prevent reuse of tables with long collision * overflow lists as well. * * @since 2.1 */ final static int MAX_COLL_CHAIN_FOR_REUSE = 63; /** * No point in trying to construct tiny tables, just need to resize * soon. */ final static int MIN_HASH_SIZE = 16; /** * We will also need to defin */ final static int INITIAL_COLLISION_LEN = 32; /** * Bucket index is 8 bits, and value 0 is reserved to represent * 'empty' status. */ final static int LAST_VALID_BUCKET = 0xFE; /* /********************************************************** /* 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 protected BytesToNameCanonicalizer _parent; /** * Member that is only used by the root table instance: root * passes immutable state into child instances, and children * may return new state if they add entries to the table. * Child tables do NOT use the reference. */ final protected 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 _hashSeed; /* /********************************************************** /* Main table state /********************************************************** */ /** * Whether canonical symbol Strings are to be intern()ed before added * to the table or not */ protected final boolean _intern; // // // First, global information /** * Total number of Names in the symbol table; * only used for child tables. */ protected int _count; /** * 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 */ protected int _longestCollisionList; // // // Then information regarding primary hash array and its // // // matching Name array /** * Mask used to truncate 32-bit hash value to current hash array * size; essentially, hash array size - 1 (since hash array sizes * are 2^N). */ protected int _mainHashMask; /** * Array of 2^N size, which contains combination * of 24-bits of hash (0 to indicate 'empty' slot), * and 8-bit collision bucket index (0 to indicate empty * collision bucket chain; otherwise subtract one from index) */ protected int[] _mainHash; /** * Array that contains Name instances matching * entries in _mainHash. Contains nulls for unused * entries. */ protected Name[] _mainNames; // // // Then the collision/spill-over area info /** * Array of heads of collision bucket chains; size dynamically */ protected Bucket[] _collList; /** * Total number of Names in collision buckets (included in * _count along with primary entries) */ protected int _collCount; /** * Index of the first unused collision bucket entry (== size of * the used portion of collision list): less than * or equal to 0xFF (255), since max number of entries is 255 * (8-bit, minus 0 used as 'empty' marker) */ protected int _collEnd; // // // Info regarding pending rehashing... /** * This flag is set if, after adding a new entry, it is deemed * that a rehash is warranted if any more entries are to be added. */ private transient boolean _needRehash; /* /********************************************************** /* 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 _mainHashShared; private boolean _mainNamesShared; /** * Flag that indicates whether underlying data structures for * the collision list 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 when adding new collision entries. */ private boolean _collListShared; /* /********************************************************** /* Life-cycle: constructors /********************************************************** */ /** * Constructor used for creating per-JsonFactory "root" * symbol tables: ones used for merging and sharing common symbols * * @param hashSize Initial 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 BytesToNameCanonicalizer(int hashSize, boolean intern, int seed) { _parent = null; _hashSeed = seed; _intern = intern; // Sanity check: let's now allow hash sizes below certain minimum value if (hashSize < MIN_HASH_SIZE) { hashSize = 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 ((hashSize & (hashSize - 1)) != 0) { // only true if it's 2^N int curr = MIN_HASH_SIZE; while (curr < hashSize) { curr += curr; } hashSize = curr; } } _tableInfo = new AtomicReference(initTableInfo(hashSize)); } /** * Constructor used when creating a child instance */ private BytesToNameCanonicalizer(BytesToNameCanonicalizer parent, boolean intern, int seed, TableInfo state) { _parent = parent; _hashSeed = seed; _intern = intern; _tableInfo = null; // not used by child tables // Then copy shared state _count = state.count; _mainHashMask = state.mainHashMask; _mainHash = state.mainHash; _mainNames = state.mainNames; _collList = state.collList; _collCount = state.collCount; _collEnd = state.collEnd; _longestCollisionList = state.longestCollisionList; // and then set other state to reflect sharing status _needRehash = false; _mainHashShared = true; _mainNamesShared = true; _collListShared = true; } /* public TableInfo(int count, int mainHashMask, int[] mainHash, Name[] mainNames, Bucket[] collList, int collCount, int collEnd, int longestCollisionList) */ private TableInfo initTableInfo(int hashSize) { return new TableInfo(0, // count hashSize - 1, // mainHashMask new int[hashSize], // mainHash new Name[hashSize], // mainNames null, // collList 0, // collCount, 0, // collEnd 0 // longestCollisionList ); } /* /********************************************************** /* Life-cycle: factory methods, merging /********************************************************** */ /** * Factory method to call to create a symbol table instance with a * randomized seed value. */ public static BytesToNameCanonicalizer createRoot() { /* [Issue-21]: Need to use a variable seed, to thwart hash-collision * based attacks. */ 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 BytesToNameCanonicalizer createRoot(int hashSeed) { return new BytesToNameCanonicalizer(DEFAULT_TABLE_SIZE, true, hashSeed); } /** * Factory method used to create actual symbol table instance to * use for parsing. * * @param intern Whether canonical symbol Strings should be interned * or not */ public BytesToNameCanonicalizer makeChild(boolean canonicalize, boolean intern) { return new BytesToNameCanonicalizer(this, intern, _hashSeed, _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 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. */ _mainHashShared = true; _mainNamesShared = true; _collListShared = true; } } private void mergeChild(TableInfo childState) { final int childCount = childState.count; TableInfo currState = _tableInfo.get(); // Only makes sense if child actually has more entries 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 || childState.longestCollisionList > MAX_COLL_CHAIN_FOR_REUSE) { /* Should there be a way to get notified about this * event, to log it or such? (as it's somewhat abnormal * thing to happen) */ // At any rate, need to clean up the tables childState = initTableInfo(DEFAULT_TABLE_SIZE); } _tableInfo.compareAndSet(currState, childState); } /* /********************************************************** /* API, accessors /********************************************************** */ public int size() { if (_tableInfo != null) { // root table return _tableInfo.get().count; } // nope, child table return _count; } /** * @since 2.1 */ public int bucketCount() { return _mainHash.length; } /** * 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 !_mainHashShared; } /** * @since 2.1 */ public int hashSeed() { return _hashSeed; } /** * 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() { return _collCount; } /** * 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 static Name getEmptyName() { return Name1.getEmptyName(); } /** * Finds and returns name matching the specified symbol, if such * name already exists in the table. * If not, will return null. *

* Note: separate methods to optimize common case of * short element/attribute names (4 or less ascii characters) * * @param firstQuad int32 containing first 4 bytes of the name; * if the whole name less than 4 bytes, padded with zero bytes * in front (zero MSBs, ie. right aligned) * * @return Name matching the symbol passed (or constructed for * it) */ public Name findName(int firstQuad) { int hash = calcHash(firstQuad); int ix = (hash & _mainHashMask); int val = _mainHash[ix]; /* High 24 bits of the value are low 24 bits of hash (low 8 bits * are bucket index)... match? */ if ((((val >> 8) ^ hash) << 8) == 0) { // match // Ok, but do we have an actual match? Name name = _mainNames[ix]; if (name == null) { // main slot empty; can't find return null; } if (name.equals(firstQuad)) { return name; } } else if (val == 0) { // empty slot? no match return null; } // Maybe a spill-over? val &= 0xFF; if (val > 0) { // 0 means 'empty' val -= 1; // to convert from 1-based to 0... Bucket bucket = _collList[val]; if (bucket != null) { return bucket.find(hash, firstQuad, 0); } } // Nope, no match whatsoever return null; } /** * Finds and returns name matching the specified symbol, if such * name already exists in the table. * If not, will return null. *

* Note: separate methods to optimize common case of relatively * short element/attribute names (8 or less ascii characters) * * @param firstQuad int32 containing first 4 bytes of the name. * @param secondQuad int32 containing bytes 5 through 8 of the * name; if less than 8 bytes, padded with up to 3 zero bytes * in front (zero MSBs, ie. right aligned) * * @return Name matching the symbol passed (or constructed for it) */ public Name findName(int firstQuad, int secondQuad) { int hash = (secondQuad == 0) ? calcHash(firstQuad) : calcHash(firstQuad, secondQuad); int ix = (hash & _mainHashMask); int val = _mainHash[ix]; /* High 24 bits of the value are low 24 bits of hash (low 8 bits * are bucket index)... match? */ if ((((val >> 8) ^ hash) << 8) == 0) { // match // Ok, but do we have an actual match? Name name = _mainNames[ix]; if (name == null) { // main slot empty; can't find return null; } if (name.equals(firstQuad, secondQuad)) { return name; } } else if (val == 0) { // empty slot? no match return null; } // Maybe a spill-over? val &= 0xFF; if (val > 0) { // 0 means 'empty' val -= 1; // to convert from 1-based to 0... Bucket bucket = _collList[val]; if (bucket != null) { return bucket.find(hash, firstQuad, secondQuad); } } // Nope, no match whatsoever return null; } /** * Finds and returns name matching the specified symbol, if such * name already exists in the table; or if not, creates name object, * adds to the table, and returns it. *

* Note: this is the general purpose method that can be called for * names of any length. However, if name is less than 9 bytes long, * it is preferable to call the version optimized for short * names. * * @param quads Array of int32s, each of which contain 4 bytes of * encoded name * @param qlen Number of int32s, starting from index 0, in quads * parameter * * @return Name matching the symbol passed (or constructed for it) */ public Name findName(int[] quads, int qlen) { if (qlen < 3) { // another sanity check return findName(quads[0], (qlen < 2) ? 0 : quads[1]); } int hash = calcHash(quads, qlen); // (for rest of comments regarding logic, see method above) int ix = (hash & _mainHashMask); int val = _mainHash[ix]; if ((((val >> 8) ^ hash) << 8) == 0) { Name name = _mainNames[ix]; if (name == null // main slot empty; no collision list then either || name.equals(quads, qlen)) { // should be match, let's verify return name; } } else if (val == 0) { // empty slot? no match return null; } val &= 0xFF; if (val > 0) { // 0 means 'empty' val -= 1; // to convert from 1-based to 0... Bucket bucket = _collList[val]; if (bucket != null) { return bucket.find(hash, quads, qlen); } } return null; } /* /********************************************************** /* API, mutators /********************************************************** */ public Name addName(String symbolStr, int q1, int q2) { if (_intern) { symbolStr = InternCache.instance.intern(symbolStr); } int hash = (q2 == 0) ? calcHash(q1) : calcHash(q1, q2); Name symbol = constructName(hash, symbolStr, q1, q2); _addSymbol(hash, symbol); return symbol; } public Name addName(String symbolStr, int[] quads, int qlen) { if (_intern) { symbolStr = InternCache.instance.intern(symbolStr); } int hash; if (qlen < 3) { hash = (qlen == 1) ? calcHash(quads[0]) : calcHash(quads[0], quads[1]); } else { hash = calcHash(quads, qlen); } Name symbol = constructName(hash, symbolStr, quads, qlen); _addSymbol(hash, symbol); return symbol; } /* /********************************************************** /* Helper methods /********************************************************** */ /* 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 firstQuad) { int hash = firstQuad ^ _hashSeed; hash += (hash >>> 15); // to xor hi- and low- 16-bits hash ^= (hash >>> 9); // as well as lowest 2 bytes return hash; } public int calcHash(int firstQuad, int secondQuad) { /* For two quads, let's change algorithm a bit, to spice * things up (can do bit more processing anyway) */ int hash = firstQuad; hash ^= (hash >>> 15); // try mixing first and second byte pairs first hash += (secondQuad * MULT); // then add second quad hash ^= _hashSeed; hash += (hash >>> 7); // and shuffle some more return hash; } public int calcHash(int[] quads, int qlen) { // Note: may be called for qlen < 3; but has at least one int if (qlen < 3) { 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 = quads[0] ^ _hashSeed; hash += (hash >>> 9); hash *= MULT; hash += quads[1]; hash *= MULT2; hash += (hash >>> 15); hash ^= quads[2]; hash += (hash >>> 17); for (int i = 3; i < qlen; ++i) { hash = (hash * MULT3) ^ quads[i]; // for longer entries, mess a bit in-between too hash += (hash >>> 3); hash ^= (hash << 7); } // and finally shuffle some more once done hash += (hash >>> 15); // to get high-order bits to mix more hash ^= (hash << 9); // as well as lowest 2 bytes return hash; } // Method only used by unit tests protected static 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; } /* /********************************************************** /* Standard methods /********************************************************** */ /* @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("[BytesToNameCanonicalizer, size: "); sb.append(_count); sb.append('/'); sb.append(_mainHash.length); sb.append(", "); sb.append(_collCount); 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 = _count; for (int i = 0; i < _collEnd; ++i) { int spillLen = _collList[i].length(); for (int j = 1; j <= spillLen; ++j) { pathCount += j; } } double avgLength; if (_count == 0) { avgLength = 0.0; } else { avgLength = (double) pathCount / (double) _count; } // let's round up a bit (two 2 decimal places) //avgLength -= (avgLength % 0.01); sb.append(avgLength); sb.append(']'); return sb.toString(); } */ /* /********************************************************** /* Internal methods /********************************************************** */ private void _addSymbol(int hash, Name symbol) { if (_mainHashShared) { // always have to modify main entry unshareMain(); } // First, do we need to rehash? if (_needRehash) { rehash(); } ++_count; /* Ok, enough about set up: now we need to find the slot to add * symbol in: */ int ix = (hash & _mainHashMask); if (_mainNames[ix] == null) { // primary empty? _mainHash[ix] = (hash << 8); if (_mainNamesShared) { unshareNames(); } _mainNames[ix] = symbol; } else { // nope, it's a collision, need to spill over /* How about spill-over area... do we already know the bucket * (is the case if it's not the first collision) */ if (_collListShared) { unshareCollision(); // also allocates if list was null } ++_collCount; int entryValue = _mainHash[ix]; int bucket = entryValue & 0xFF; if (bucket == 0) { // first spill over? if (_collEnd <= LAST_VALID_BUCKET) { // yup, still unshared bucket bucket = _collEnd; ++_collEnd; // need to expand? if (bucket >= _collList.length) { expandCollision(); } } else { // nope, have to share... let's find shortest? bucket = findBestBucket(); } // Need to mark the entry... and the spill index is 1-based _mainHash[ix] = (entryValue & ~0xFF) | (bucket + 1); } else { --bucket; // 1-based index in value } // And then just need to link the new bucket entry in Bucket newB = new Bucket(symbol, _collList[bucket]); _collList[bucket] = newB; // but, be careful wrt attacks _longestCollisionList = Math.max(newB.length(), _longestCollisionList); if (_longestCollisionList > MAX_COLL_CHAIN_LENGTH) { reportTooManyCollisions(MAX_COLL_CHAIN_LENGTH); } } /* Ok. Now, do we need a rehash next time? Need to have at least * 50% fill rate no matter what: */ { int hashSize = _mainHash.length; if (_count > (hashSize >> 1)) { int hashQuarter = (hashSize >> 2); /* And either strictly above 75% (the usual) or * just 50%, and collision count >= 25% of total hash size */ if (_count > (hashSize - hashQuarter)) { _needRehash = true; } else if (_collCount >= hashQuarter) { _needRehash = true; } } } } private void rehash() { _needRehash = false; // Note: since we'll make copies, no need to unshare, can just mark as such: _mainNamesShared = 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. */ int[] oldMainHash = _mainHash; int len = oldMainHash.length; int newLen = len+len; /* 13-Mar-2010, tatu: Let's guard against OOME that could be caused by * large documents with unique (or mostly so) names */ if (newLen > MAX_TABLE_SIZE) { nukeSymbols(); return; } _mainHash = new int[newLen]; _mainHashMask = (newLen - 1); Name[] oldNames = _mainNames; _mainNames = new Name[newLen]; int symbolsSeen = 0; // let's do a sanity check for (int i = 0; i < len; ++i) { Name symbol = oldNames[i]; if (symbol != null) { ++symbolsSeen; int hash = symbol.hashCode(); int ix = (hash & _mainHashMask); _mainNames[ix] = symbol; _mainHash[ix] = hash << 8; // will clear spill index } } /* And then the spill area. This may cause collisions, although * not necessarily as many as there were earlier. Let's allocate * same amount of space, however */ int oldEnd = _collEnd; if (oldEnd == 0) { // no prior collisions... _longestCollisionList = 0; return; } _collCount = 0; _collEnd = 0; _collListShared = false; int maxColl = 0; Bucket[] oldBuckets = _collList; _collList = new Bucket[oldBuckets.length]; for (int i = 0; i < oldEnd; ++i) { for (Bucket curr = oldBuckets[i]; curr != null; curr = curr._next) { ++symbolsSeen; Name symbol = curr._name; int hash = symbol.hashCode(); int ix = (hash & _mainHashMask); int val = _mainHash[ix]; if (_mainNames[ix] == null) { // no primary entry? _mainHash[ix] = (hash << 8); _mainNames[ix] = symbol; } else { // nope, it's a collision, need to spill over ++_collCount; int bucket = val & 0xFF; if (bucket == 0) { // first spill over? if (_collEnd <= LAST_VALID_BUCKET) { // yup, still unshared bucket bucket = _collEnd; ++_collEnd; // need to expand? if (bucket >= _collList.length) { expandCollision(); } } else { // nope, have to share... let's find shortest? bucket = findBestBucket(); } // Need to mark the entry... and the spill index is 1-based _mainHash[ix] = (val & ~0xFF) | (bucket + 1); } else { --bucket; // 1-based index in value } // And then just need to link the new bucket entry in Bucket newB = new Bucket(symbol, _collList[bucket]); _collList[bucket] = newB; maxColl = Math.max(maxColl, newB.length()); } } // for (... buckets in the chain ...) } // for (... list of bucket heads ... ) _longestCollisionList = maxColl; if (symbolsSeen != _count) { // sanity check throw new RuntimeException("Internal error: count after rehash "+symbolsSeen+"; should be "+_count); } } /** * Helper method called to empty all shared symbols, but to leave * arrays allocated */ private void nukeSymbols() { _count = 0; _longestCollisionList = 0; Arrays.fill(_mainHash, 0); Arrays.fill(_mainNames, null); Arrays.fill(_collList, null); _collCount = 0; _collEnd = 0; } /** * Method called to find the best bucket to spill a Name over to: * usually the first bucket that has only one entry, but in general * first one of the buckets with least number of entries */ private int findBestBucket() { Bucket[] buckets = _collList; int bestCount = Integer.MAX_VALUE; int bestIx = -1; for (int i = 0, len = _collEnd; i < len; ++i) { int count = buckets[i].length(); if (count < bestCount) { if (count == 1) { // best possible return i; } bestCount = count; bestIx = i; } } return bestIx; } /** * Method that needs to be called, if the main hash structure * is (may be) shared. This happens every time something is added, * even if addition is to the collision list (since collision list * index comes from lowest 8 bits of the primary hash entry) */ private void unshareMain() { int[] old = _mainHash; int len = _mainHash.length; _mainHash = new int[len]; System.arraycopy(old, 0, _mainHash, 0, len); _mainHashShared = false; } private void unshareCollision() { Bucket[] old = _collList; if (old == null) { _collList = new Bucket[INITIAL_COLLISION_LEN]; } else { int len = old.length; _collList = new Bucket[len]; System.arraycopy(old, 0, _collList, 0, len); } _collListShared = false; } private void unshareNames() { Name[] old = _mainNames; int len = old.length; _mainNames = new Name[len]; System.arraycopy(old, 0, _mainNames, 0, len); _mainNamesShared = false; } private void expandCollision() { Bucket[] old = _collList; int len = old.length; _collList = new Bucket[len+len]; System.arraycopy(old, 0, _collList, 0, len); } /* /********************************************************** /* Constructing name objects /********************************************************** */ private static Name constructName(int hash, String name, int q1, int q2) { if (q2 == 0) { // one quad only? return new Name1(name, hash, q1); } return new Name2(name, hash, q1, q2); } private static Name constructName(int hash, String name, int[] quads, int qlen) { if (qlen < 4) { // Need to check for 3 quad one, can do others too switch (qlen) { case 1: return new Name1(name, hash, quads[0]); case 2: return new Name2(name, hash, quads[0], quads[1]); case 3: return new Name3(name, hash, quads[0], quads[1], quads[2]); default: } } // Otherwise, need to copy the incoming buffer int[] buf = new int[qlen]; for (int i = 0; i < qlen; ++i) { buf[i] = quads[i]; } return new NameN(name, hash, buf, qlen); } /* /********************************************************** /* Other helper methods /********************************************************** */ /** * @since 2.1 */ protected void reportTooManyCollisions(int maxLen) { throw new IllegalStateException("Longest collision chain in symbol table (of size "+_count +") now exceeds maximum, "+maxLen+" -- suspect a DoS attack based on hash collisions"); } /* /********************************************************** /* 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 count; public final int mainHashMask; public final int[] mainHash; public final Name[] mainNames; public final Bucket[] collList; public final int collCount; public final int collEnd; public final int longestCollisionList; public TableInfo(int count, int mainHashMask, int[] mainHash, Name[] mainNames, Bucket[] collList, int collCount, int collEnd, int longestCollisionList) { this.count = count; this.mainHashMask = mainHashMask; this.mainHash = mainHash; this.mainNames = mainNames; this.collList = collList; this.collCount = collCount; this.collEnd = collEnd; this.longestCollisionList = longestCollisionList; } public TableInfo(BytesToNameCanonicalizer src) { count = src._count; mainHashMask = src._mainHashMask; mainHash = src._mainHash; mainNames = src._mainNames; collList = src._collList; collCount = src._collCount; collEnd = src._collEnd; longestCollisionList = src._longestCollisionList; } } /** * */ final static class Bucket { protected final Name _name; protected final Bucket _next; private final int _length; Bucket(Name name, Bucket next) { _name = name; _next = next; _length = (next == null) ? 1 : next._length+1; } public int length() { return _length; } public Name find(int hash, int firstQuad, int secondQuad) { if (_name.hashCode() == hash) { if (_name.equals(firstQuad, secondQuad)) { return _name; } } for (Bucket curr = _next; curr != null; curr = curr._next) { Name currName = curr._name; if (currName.hashCode() == hash) { if (currName.equals(firstQuad, secondQuad)) { return currName; } } } return null; } public Name find(int hash, int[] quads, int qlen) { if (_name.hashCode() == hash) { if (_name.equals(quads, qlen)) { return _name; } } for (Bucket curr = _next; curr != null; curr = curr._next) { Name currName = curr._name; if (currName.hashCode() == hash) { if (currName.equals(quads, qlen)) { return currName; } } } return null; } } } CharsToNameCanonicalizer.java000066400000000000000000000621221215056622200345140ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/sympackage com.fasterxml.jackson.core.sym; import java.util.Arrays; 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. */ 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 nonetheless. For systems that properly * reuse factories it doesn't matter either way; but when * recreating factories often, initial overhead may dominate. */ protected static final int DEFAULT_TABLE_SIZE = 64; /** * Let's not expand symbol tables past some maximum size; * this should protected against OOMEs caused by large documents * with uniquer (~= random) names. */ protected static final int MAX_TABLE_SIZE = 0x10000; // 64k entries == 256k mem /** * Let's only share reasonably sized symbol tables. Max size set to 3/4 of 16k; * this corresponds to 64k main hash index. This should allow for enough distinct * names for almost any case. */ final static int MAX_ENTRIES_FOR_REUSE = 12000; /** * 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 255 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 */ final static int MAX_COLL_CHAIN_LENGTH = 255; /** * And to support reduce likelihood of accidental collisons causing * exceptions, let's prevent reuse of tables with long collision * overflow lists as well. * * @since 2.1 */ final static int MAX_COLL_CHAIN_FOR_REUSE = 63; final static CharsToNameCanonicalizer sBootstrapSymbolTable; static { sBootstrapSymbolTable = new CharsToNameCanonicalizer(); } /* /********************************************************** /* 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. */ protected CharsToNameCanonicalizer _parent; /** * 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 _hashSeed; /** * Whether canonical symbol Strings are to be intern()ed before added * to the table or not */ final protected boolean _intern; /** * Whether any canonicalization should be attempted (whether using * intern or not) */ final protected boolean _canonicalize; /* /********************************************************** /* Actual symbol table data /********************************************************** */ /** * Primary matching symbols; it's expected most match occur from * here. */ protected 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. */ protected Bucket[] _buckets; /** * Current size (number of entries); needed to know if and when * rehash. */ protected int _size; /** * Limit that indicates maximum size this instance can hold before * it needs to be expanded and rehashed. Calculated using fill * factor passed in to constructor. */ protected int _sizeThreshold; /** * Mask used to get index from hash values; equal to * _buckets.length - 1, when _buckets.length is * a power of two. */ protected 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 */ protected int _longestCollisionList; /* /********************************************************** /* State regarding shared arrays /********************************************************** */ /** * Flag that indicates if any changes have been made to the data; * used to both determine if bucket array needs to be copied when * (first) change is made, and potentially if updated bucket list * is to be resync'ed back to master instance. */ protected boolean _dirty; /* /********************************************************** /* Life-cycle /********************************************************** */ /** * 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() { /* [Issue-21]: Need to use a variable seed, to thwart hash-collision * based attacks. */ 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 hashSeed) { return sBootstrapSymbolTable.makeOrphan(hashSeed); } /** * Main method for constructing a master symbol table instance. * * @param initialSize Minimum initial size for bucket array; internally * will always use a power of two equal to or bigger than this value. */ private CharsToNameCanonicalizer() { // these settings don't really matter for the bootstrap instance _canonicalize = true; _intern = true; // And we'll also set flags so no copying of buckets is needed: _dirty = true; _hashSeed = 0; _longestCollisionList = 0; initTables(DEFAULT_TABLE_SIZE); } private void initTables(int initialSize) { _symbols = new String[initialSize]; _buckets = new Bucket[initialSize >> 1]; // Mask is easy to calc for powers of two. _indexMask = initialSize - 1; _size = 0; _longestCollisionList = 0; // Hard-coded fill factor is 75% _sizeThreshold = _thresholdSize(initialSize); } private static int _thresholdSize(int hashAreaSize) { return hashAreaSize - (hashAreaSize >> 2); } /** * Internal constructor used when creating child instances. */ private CharsToNameCanonicalizer(CharsToNameCanonicalizer parent, boolean canonicalize, boolean intern, String[] symbols, Bucket[] buckets, int size, int hashSeed, int longestColl) { _parent = parent; _canonicalize = canonicalize; _intern = intern; _symbols = symbols; _buckets = buckets; _size = size; _hashSeed = hashSeed; // Hard-coded fill factor, 75% int arrayLen = (symbols.length); _sizeThreshold = _thresholdSize(arrayLen); _indexMask = (arrayLen - 1); _longestCollisionList = longestColl; // Need to make copies of arrays, if/when adding new entries _dirty = false; } /** * "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(final boolean canonicalize, final boolean intern) { /* 24-Jul-2012, tatu: Trying to reduce scope of synchronization, assuming * that synchronizing construction is the (potentially) expensive part, * and not so much short copy-the-variables thing. */ final String[] symbols; final Bucket[] buckets; final int size; final int hashSeed; final int longestCollisionList; synchronized (this) { symbols = _symbols; buckets = _buckets; size = _size; hashSeed = _hashSeed; longestCollisionList = _longestCollisionList; } return new CharsToNameCanonicalizer(this, canonicalize, intern, symbols, buckets, size, hashSeed, longestCollisionList); } private CharsToNameCanonicalizer makeOrphan(int seed) { return new CharsToNameCanonicalizer(null, true, true, _symbols, _buckets, _size, seed, _longestCollisionList); } /** * 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(CharsToNameCanonicalizer child) { /* 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 (child.size() > MAX_ENTRIES_FOR_REUSE || child._longestCollisionList > MAX_COLL_CHAIN_FOR_REUSE) { // Should there be a way to get notified about this event, to log it or such? // (as it's somewhat abnormal thing to happen) // At any rate, need to clean up the tables, then: synchronized (this) { initTables(DEFAULT_TABLE_SIZE); // Dirty flag... well, let's just clear it. Shouldn't really matter for master tables // (which this is, given something is merged to it) _dirty = false; } } else { // Otherwise, we'll merge changed stuff in, if there are more entries (which // may not be the case if one of siblings has added symbols first or such) if (child.size() <= size()) { // nothing to add return; } // Okie dokie, let's get the data in! synchronized (this) { _symbols = child._symbols; _buckets = child._buckets; _size = child._size; _sizeThreshold = child._sizeThreshold; _indexMask = child._indexMask; _longestCollisionList = child._longestCollisionList; // Dirty flag... well, let's just clear it. Shouldn't really matter for master tables // (which this is, given something is merged to it) _dirty = false; } } } public void release() { // If nothing has been added, nothing to do if (!maybeDirty()) { return; } if (_parent != null) { _parent.mergeChild(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. */ _dirty = false; } } /* /********************************************************** /* Public API, generic accessors: /********************************************************** */ public int size() { 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 _dirty; } public int hashSeed() { return _hashSeed; } /** * 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; do { if (sym.charAt(i) != buffer[start+i]) { break; } } while (++i < len); // Optimal case; primary match found if (i == len) { return sym; } } // How about collision bucket? Bucket b = _buckets[index >> 1]; if (b != null) { sym = b.find(buffer, start, len); if (sym != null) { return sym; } } } if (!_dirty) { //need to do copy-on-write? copyArrays(); _dirty = true; } else if (_size >= _sizeThreshold) { // Need to expand? rehash(); /* Need to recalc hash; rare occurence (index mask has been * recalculated as part of rehash) */ index = _hashToIndex(calcHash(buffer, start, len)); } String newSymbol = new String(buffer, start, len); if (_intern) { 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 { int bix = (index >> 1); Bucket newB = new Bucket(newSymbol, _buckets[bix]); _buckets[bix] = newB; _longestCollisionList = Math.max(newB.length(), _longestCollisionList); if (_longestCollisionList > MAX_COLL_CHAIN_LENGTH) { reportTooManyCollisions(MAX_COLL_CHAIN_LENGTH); } } return newSymbol; } /** * 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) { rawHash += (rawHash >>> 15); // this seems to help quite a bit, at least for our tests 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 = _hashSeed; for (int i = 0; i < len; ++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 = _hashSeed; 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() { String[] oldSyms = _symbols; int size = oldSyms.length; _symbols = new String[size]; System.arraycopy(oldSyms, 0, _symbols, 0, size); Bucket[] oldBuckets = _buckets; size = oldBuckets.length; _buckets = new Bucket[size]; System.arraycopy(oldBuckets, 0, _buckets, 0, size); } /** * 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() { 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_TABLE_SIZE) { /* If this happens, there's no point in either growing or * shrinking hash areas. Rather, it's better to just clean * them up for reuse. */ _size = 0; Arrays.fill(_symbols, null); Arrays.fill(_buckets, null); _dirty = true; return; } String[] oldSyms = _symbols; 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()); } } } size >>= 1; for (int i = 0; i < size; ++i) { Bucket b = oldBuckets[i]; while (b != null) { ++count; String symbol = b.getSymbol(); 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.getNext(); } } _longestCollisionList = maxColl; if (count != _size) { throw new Error("Internal error on SymbolTable.rehash(): had "+_size+" entries; now have "+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"); } /* /********************************************************** /* Bucket class /********************************************************** */ /** * This class is a symbol table entry. Each entry acts as a node * in a linked list. */ static final class Bucket { private final String _symbol; private final Bucket _next; private final int _length; public Bucket(String symbol, Bucket next) { _symbol = symbol; _next = next; _length = (next == null) ? 1 : next._length+1; } public String getSymbol() { return _symbol; } public Bucket getNext() { return _next; } public int length() { return _length; } public String find(char[] buf, int start, int len) { String sym = _symbol; Bucket b = _next; while (true) { // Inlined equality comparison: if (sym.length() == len) { int i = 0; do { if (sym.charAt(i) != buf[start+i]) { break; } } while (++i < len); if (i == len) { return sym; } } if (b == null) { break; } sym = b.getSymbol(); b = b.getNext(); } return null; } } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/sym/Name.java000066400000000000000000000024231215056622200306030ustar00rootroot00000000000000package 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 quad1); public abstract boolean equals(int quad1, int quad2); 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.2.2/src/main/java/com/fasterxml/jackson/core/sym/Name1.java000066400000000000000000000020261215056622200306630ustar00rootroot00000000000000package 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 { final static Name1 sEmptyName = new Name1("", 0, 0); final int mQuad; Name1(String name, int hash, int quad) { super(name, hash); mQuad = quad; } static Name1 getEmptyName() { return sEmptyName; } @Override public boolean equals(int quad) { return (quad == mQuad); } @Override public boolean equals(int quad1, int quad2) { return (quad1 == mQuad) && (quad2 == 0); } @Override public boolean equals(int[] quads, int qlen) { return (qlen == 1 && quads[0] == mQuad); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/sym/Name2.java000066400000000000000000000017551215056622200306740ustar00rootroot00000000000000package 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 { final int mQuad1; final int mQuad2; Name2(String name, int hash, int quad1, int quad2) { super(name, hash); mQuad1 = quad1; mQuad2 = quad2; } @Override public boolean equals(int quad) { return false; } @Override public boolean equals(int quad1, int quad2) { return (quad1 == mQuad1) && (quad2 == mQuad2); } @Override public boolean equals(int[] quads, int qlen) { return (qlen == 2 && quads[0] == mQuad1 && quads[1] == mQuad2); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/sym/Name3.java000066400000000000000000000017241215056622200306710ustar00rootroot00000000000000package 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 { final int mQuad1; final int mQuad2; final int mQuad3; Name3(String name, int hash, int q1, int q2, int q3) { super(name, hash); mQuad1 = q1; mQuad2 = q2; mQuad3 = q3; } // 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[] quads, int qlen) { return (qlen == 3) && (quads[0] == mQuad1) && (quads[1] == mQuad2) && (quads[2] == mQuad3); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/sym/NameN.java000066400000000000000000000034621215056622200307250ustar00rootroot00000000000000package com.fasterxml.jackson.core.sym; /** * 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 { final int[] mQuads; final int mQuadLen; NameN(String name, int hash, int[] quads, int quadLen) { super(name, hash); /* We have specialized implementations for shorter * names, so let's not allow runt instances here */ if (quadLen < 3) { throw new IllegalArgumentException("Qlen must >= 3"); } mQuads = quads; mQuadLen = quadLen; } // 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[] quads, int qlen) { if (qlen != mQuadLen) { return false; } /* 26-Nov-2008, tatus: Strange, but it does look like * unrolling here is counter-productive, reducing * speed. Perhaps it prevents inlining by HotSpot or * something... */ // Will always have >= 3 quads, can unroll /* if (quads[0] == mQuads[0] && quads[1] == mQuads[1] && quads[2] == mQuads[2]) { for (int i = 3; i < qlen; ++i) { if (quads[i] != mQuads[i]) { return false; } } return true; } */ // or simpler way without unrolling: for (int i = 0; i < qlen; ++i) { if (quads[i] != mQuads[i]) { return false; } } return true; } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/sym/package-info.java000066400000000000000000000002341215056622200322450ustar00rootroot00000000000000/** * 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.2.2/src/main/java/com/fasterxml/jackson/core/type/000077500000000000000000000000001215056622200272305ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/type/ResolvedType.java000066400000000000000000000074261215056622200325310ustar00rootroot00000000000000package 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(); 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(); /** * 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 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.2.2/src/main/java/com/fasterxml/jackson/core/type/TypeReference.java000066400000000000000000000046211215056622200326360ustar00rootroot00000000000000package 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) { // just need an implementation, not a good one... hence: return 0; } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/type/package-info.java000066400000000000000000000006301215056622200324160ustar00rootroot00000000000000/** * 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. */ package com.fasterxml.jackson.core.type; jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/util/000077500000000000000000000000001215056622200272245ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/util/BufferRecycler.java000066400000000000000000000065261215056622200330020ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; /** * 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. */ public class BufferRecycler { public final static int DEFAULT_WRITE_CONCAT_BUFFER_LEN = 2000; public enum ByteBufferType { READ_IO_BUFFER(4000) /** * Buffer used for temporarily storing encoded content; used * for example by UTF-8 encoding writer */ ,WRITE_ENCODING_BUFFER(4000) /** * Buffer used for temporarily concatenating output; used for * example when requesting output as byte array. */ ,WRITE_CONCAT_BUFFER(2000) /** * Buffer used for concatenating binary data that is either being * encoded as base64 output, or decoded from base64 input. * * @since 2.1 */ ,BASE64_CODEC_BUFFER(2000) ; protected final int size; ByteBufferType(int size) { this.size = size; } } public enum CharBufferType { TOKEN_BUFFER(2000) // Tokenizable input ,CONCAT_BUFFER(2000) // concatenated output ,TEXT_BUFFER(200) // Text content from input ,NAME_COPY_BUFFER(200) // Temporary buffer for getting name characters ; protected final int size; CharBufferType(int size) { this.size = size; } } final protected byte[][] _byteBuffers = new byte[ByteBufferType.values().length][]; final protected char[][] _charBuffers = new char[CharBufferType.values().length][]; public BufferRecycler() { } public final byte[] allocByteBuffer(ByteBufferType type) { int ix = type.ordinal(); byte[] buffer = _byteBuffers[ix]; if (buffer == null) { buffer = balloc(type.size); } else { _byteBuffers[ix] = null; } return buffer; } public final void releaseByteBuffer(ByteBufferType type, byte[] buffer) { _byteBuffers[type.ordinal()] = buffer; } public final char[] allocCharBuffer(CharBufferType type) { return allocCharBuffer(type, 0); } public final char[] allocCharBuffer(CharBufferType type, int minSize) { if (type.size > minSize) { minSize = type.size; } int ix = type.ordinal(); char[] buffer = _charBuffers[ix]; if (buffer == null || buffer.length < minSize) { buffer = calloc(minSize); } else { _charBuffers[ix] = null; } return buffer; } public final void releaseCharBuffer(CharBufferType type, char[] buffer) { _charBuffers[type.ordinal()] = buffer; } /* /********************************************************** /* Actual allocations separated for easier debugging/profiling /********************************************************** */ private byte[] balloc(int size) { return new byte[size]; } private char[] calloc(int size) { return new char[size]; } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/util/ByteArrayBuilder.java000066400000000000000000000176141215056622200333110ustar00rootroot00000000000000/* 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 reallocs, copying), as well API * not based on {@link java.io.OutputStream}. In short, a very much * specialized builder object. *

* Since version 1.5, 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. */ public final class ByteArrayBuilder extends OutputStream { private 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. Let's limit to using 256k chunks. */ private final static int MAX_BLOCK_SIZE = (1 << 18); 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; if (br == null) { _currBlock = new byte[firstBlockSize]; } else { _currBlock = br.allocByteBuffer(BufferRecycler.ByteBufferType.WRITE_CONCAT_BUFFER); } } public void reset() { _pastLen = 0; _currBlockPtr = 0; if (!_pastBlocks.isEmpty()) { _pastBlocks.clear(); } } /** * 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.ByteBufferType.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); } } /** * 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), since 1.6 /********************************************************** */ /** * 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() { _pastLen += _currBlock.length; /* 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; } } DefaultPrettyPrinter.java000066400000000000000000000266141215056622200341610ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; import java.io.*; import java.util.Arrays; 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 = -5512586643324525213L; /** * 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 jg, int level) throws IOException, JsonGenerationException; /** * @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 = Lf2SpacesIndenter.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 = 0; /* /********************************************************** /* 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; } 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; _rootSeparator = rootSeparator; } public DefaultPrettyPrinter withRootSeparator(SerializableString rootSeparator) { if (_rootSeparator == rootSeparator || (rootSeparator != null && rootSeparator.equals(_rootSeparator))) { return this; } return new DefaultPrettyPrinter(this, rootSeparator); } public void indentArraysWith(Indenter i) { _arrayIndenter = (i == null) ? NopIndenter.instance : i; } public void indentObjectsWith(Indenter i) { _objectIndenter = (i == null) ? NopIndenter.instance : i; } public void spacesInObjectEntries(boolean b) { _spacesInObjectEntries = b; } /* /********************************************************** /* Instantiatable impl /********************************************************** */ @Override public DefaultPrettyPrinter createInstance() { return new DefaultPrettyPrinter(this); } /* /********************************************************** /* PrettyPrinter impl /********************************************************** */ @Override public void writeRootValueSeparator(JsonGenerator jg) throws IOException, JsonGenerationException { if (_rootSeparator != null) { jg.writeRaw(_rootSeparator); } } @Override public void writeStartObject(JsonGenerator jg) throws IOException, JsonGenerationException { jg.writeRaw('{'); if (!_objectIndenter.isInline()) { ++_nesting; } } @Override public void beforeObjectEntries(JsonGenerator jg) throws IOException, JsonGenerationException { _objectIndenter.writeIndentation(jg, _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 jg) throws IOException, JsonGenerationException { if (_spacesInObjectEntries) { jg.writeRaw(" : "); } else { jg.writeRaw(':'); } } /** * 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 jg) throws IOException, JsonGenerationException { jg.writeRaw(','); _objectIndenter.writeIndentation(jg, _nesting); } @Override public void writeEndObject(JsonGenerator jg, int nrOfEntries) throws IOException, JsonGenerationException { if (!_objectIndenter.isInline()) { --_nesting; } if (nrOfEntries > 0) { _objectIndenter.writeIndentation(jg, _nesting); } else { jg.writeRaw(' '); } jg.writeRaw('}'); } @Override public void writeStartArray(JsonGenerator jg) throws IOException, JsonGenerationException { if (!_arrayIndenter.isInline()) { ++_nesting; } jg.writeRaw('['); } @Override public void beforeArrayValues(JsonGenerator jg) throws IOException, JsonGenerationException { _arrayIndenter.writeIndentation(jg, _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 jg) throws IOException, JsonGenerationException { jg.writeRaw(','); _arrayIndenter.writeIndentation(jg, _nesting); } @Override public void writeEndArray(JsonGenerator jg, int nrOfValues) throws IOException, JsonGenerationException { if (!_arrayIndenter.isInline()) { --_nesting; } if (nrOfValues > 0) { _arrayIndenter.writeIndentation(jg, _nesting); } else { jg.writeRaw(' '); } jg.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 jg, int level) throws IOException, JsonGenerationException { } @Override public boolean isInline() { return true; } } /** * This is a very simple indenter that only every 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 jg, int level) throws IOException, JsonGenerationException { jg.writeRaw(' '); } @Override public boolean isInline() { return true; } } /** * Default linefeed-based indenter uses system-specific linefeeds and * 2 spaces for indentation per level. */ public static class Lf2SpacesIndenter extends NopIndenter { public static final Lf2SpacesIndenter instance = new Lf2SpacesIndenter(); private final static String SYS_LF; static { String lf = null; try { lf = System.getProperty("line.separator"); } catch (Throwable t) { } // access exception? SYS_LF = (lf == null) ? "\n" : lf; } final static int SPACE_COUNT = 64; final static char[] SPACES = new char[SPACE_COUNT]; static { Arrays.fill(SPACES, ' '); } @Override public boolean isInline() { return false; } @Override public void writeIndentation(JsonGenerator jg, int level) throws IOException, JsonGenerationException { jg.writeRaw(SYS_LF); if (level > 0) { // should we err on negative values (as there's some flaw?) level += level; // 2 spaces per level while (level > SPACE_COUNT) { // should never happen but... jg.writeRaw(SPACES, 0, SPACE_COUNT); level -= SPACES.length; } jg.writeRaw(SPACES, 0, level); } } } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/util/Instantiatable.java000066400000000000000000000013741215056622200330360ustar00rootroot00000000000000package 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.2.2/src/main/java/com/fasterxml/jackson/core/util/InternCache.java000066400000000000000000000030441215056622200322530ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import java.util.Map; import java.util.LinkedHashMap; /** * 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. */ @SuppressWarnings("serial") public final class InternCache extends LinkedHashMap { /** * 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). *

* 11-Jul-2012, tatu: Also, consider the nasty case of String hashCode() * collisions; size needs to be small enough to survive linear list * lookup... so let's go down a notch (from 192 to 100) */ private final static int MAX_ENTRIES = 100; public final static InternCache instance = new InternCache(); private InternCache() { super(MAX_ENTRIES, 0.8f, true); } @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_ENTRIES; } public synchronized String intern(String input) { String result = get(input); if (result == null) { result = input.intern(); put(result, result); } return result; } } JsonGeneratorDelegate.java000066400000000000000000000260761215056622200342360ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; 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.io.CharacterEscapes; public class JsonGeneratorDelegate extends JsonGenerator { /** * Delegate object that method calls are delegated to. */ protected JsonGenerator delegate; /* /********************************************************** /* Construction, initialization /********************************************************** */ public JsonGeneratorDelegate(JsonGenerator d) { delegate = d; } @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 boolean canUseSchema(FormatSchema schema) { return delegate.canUseSchema(schema); } @Override public Version version() { return delegate.version(); } @Override public Object getOutputTarget() { return delegate.getOutputTarget(); } @Override public JsonGenerator setRootValueSeparator(SerializableString sep) { delegate.setRootValueSeparator(sep); return this; } /* /********************************************************** /* 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) /* /********************************************************** /* 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; } /* /********************************************************** /* Public API, write methods, structural /********************************************************** */ @Override public void writeStartArray() throws IOException, JsonGenerationException { delegate.writeStartArray(); } @Override public void writeEndArray() throws IOException, JsonGenerationException { delegate.writeEndArray(); } @Override public void writeStartObject() throws IOException, JsonGenerationException { delegate.writeStartObject(); } @Override public void writeEndObject() throws IOException, JsonGenerationException { delegate.writeEndObject(); } @Override public void writeFieldName(String name) throws IOException, JsonGenerationException { delegate.writeFieldName(name); } @Override public void writeFieldName(SerializableString name) throws IOException, JsonGenerationException { delegate.writeFieldName(name); } /* /********************************************************** /* Public API, write methods, text/String values /********************************************************** */ @Override public void writeString(String text) throws IOException,JsonGenerationException { delegate.writeString(text); } @Override public void writeString(char[] text, int offset, int len) throws IOException, JsonGenerationException { delegate.writeString(text, offset, len); } @Override public void writeString(SerializableString text) throws IOException, JsonGenerationException { delegate.writeString(text); } @Override public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException, JsonGenerationException { delegate.writeRawUTF8String(text, offset, length); } @Override public void writeUTF8String(byte[] text, int offset, int length) throws IOException, JsonGenerationException { delegate.writeUTF8String(text, offset, length); } /* /********************************************************** /* Public API, write methods, binary/raw content /********************************************************** */ @Override public void writeRaw(String text) throws IOException, JsonGenerationException { delegate.writeRaw(text); } @Override public void writeRaw(String text, int offset, int len) throws IOException, JsonGenerationException { delegate.writeRaw(text, offset, len); } @Override public void writeRaw(SerializableString raw) throws IOException, JsonGenerationException { delegate.writeRaw(raw); } @Override public void writeRaw(char[] text, int offset, int len) throws IOException, JsonGenerationException { delegate.writeRaw(text, offset, len); } @Override public void writeRaw(char c) throws IOException, JsonGenerationException { delegate.writeRaw(c); } @Override public void writeRawValue(String text) throws IOException, JsonGenerationException { delegate.writeRawValue(text); } @Override public void writeRawValue(String text, int offset, int len) throws IOException, JsonGenerationException { delegate.writeRawValue(text, offset, len); } @Override public void writeRawValue(char[] text, int offset, int len) throws IOException, JsonGenerationException { delegate.writeRawValue(text, offset, len); } @Override public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException, JsonGenerationException { delegate.writeBinary(b64variant, data, offset, len); } @Override public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) throws IOException, JsonGenerationException { return delegate.writeBinary(b64variant, data, dataLength); } /* /********************************************************** /* Public API, write methods, other value types /********************************************************** */ @Override public void writeNumber(short v) throws IOException, JsonGenerationException { delegate.writeNumber(v); } @Override public void writeNumber(int v) throws IOException, JsonGenerationException { delegate.writeNumber(v); } @Override public void writeNumber(long v) throws IOException, JsonGenerationException { delegate.writeNumber(v); } @Override public void writeNumber(BigInteger v) throws IOException, JsonGenerationException { delegate.writeNumber(v); } @Override public void writeNumber(double v) throws IOException, JsonGenerationException { delegate.writeNumber(v); } @Override public void writeNumber(float v) throws IOException, JsonGenerationException { delegate.writeNumber(v); } @Override public void writeNumber(BigDecimal v) throws IOException, JsonGenerationException { delegate.writeNumber(v); } @Override public void writeNumber(String encodedValue) throws IOException, JsonGenerationException, UnsupportedOperationException { delegate.writeNumber(encodedValue); } @Override public void writeBoolean(boolean state) throws IOException, JsonGenerationException { delegate.writeBoolean(state); } @Override public void writeNull() throws IOException, JsonGenerationException { delegate.writeNull(); } /* /********************************************************** /* Public API, write methods, serializing Java objects /********************************************************** */ @Override public void writeObject(Object pojo) throws IOException,JsonProcessingException { delegate.writeObject(pojo); } @Override public void writeTree(TreeNode rootNode) throws IOException, JsonProcessingException { delegate.writeTree(rootNode); } /* /********************************************************** /* Public API, convenience field write methods /********************************************************** */ // // These are fine, just delegate to other methods... /* /********************************************************** /* Public API, copy-through methods /********************************************************** */ @Override public void copyCurrentEvent(JsonParser jp) throws IOException, JsonProcessingException { delegate.copyCurrentEvent(jp); } @Override public void copyCurrentStructure(JsonParser jp) throws IOException, JsonProcessingException { delegate.copyCurrentStructure(jp); } /* /********************************************************** /* 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.java000066400000000000000000000224001215056622200335270ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; import java.io.IOException; import java.io.OutputStream; 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; } /* /********************************************************** /* 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 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 boolean requiresCustomCodec() { return delegate.requiresCustomCodec(); } @Override public Version version() { return delegate.version(); } @Override public Object getInputSource() { return delegate.getInputSource(); } /* /********************************************************** /* Closeable impl /********************************************************** */ @Override public void close() throws IOException { delegate.close(); } @Override public boolean isClosed() { return delegate.isClosed(); } /* /********************************************************** /* Public API, token accessors /********************************************************** */ @Override public JsonToken getCurrentToken() { return delegate.getCurrentToken(); } @Override public boolean hasCurrentToken() { return delegate.hasCurrentToken(); } @Override public String getCurrentName() throws IOException, JsonParseException { return delegate.getCurrentName(); } @Override public JsonLocation getCurrentLocation() { return delegate.getCurrentLocation(); } @Override public JsonStreamContext getParsingContext() { return delegate.getParsingContext(); } /* /********************************************************** /* 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, JsonParseException { return delegate.getText(); } @Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); } @Override public char[] getTextCharacters() throws IOException, JsonParseException { return delegate.getTextCharacters(); } @Override public int getTextLength() throws IOException, JsonParseException { return delegate.getTextLength(); } @Override public int getTextOffset() throws IOException, JsonParseException { return delegate.getTextOffset(); } /* /********************************************************** /* Public API, access to token information, numeric /********************************************************** */ @Override public BigInteger getBigIntegerValue() throws IOException,JsonParseException { return delegate.getBigIntegerValue(); } @Override public boolean getBooleanValue() throws IOException, JsonParseException { return delegate.getBooleanValue(); } @Override public byte getByteValue() throws IOException, JsonParseException { return delegate.getByteValue(); } @Override public short getShortValue() throws IOException, JsonParseException { return delegate.getShortValue(); } @Override public BigDecimal getDecimalValue() throws IOException, JsonParseException { return delegate.getDecimalValue(); } @Override public double getDoubleValue() throws IOException, JsonParseException { return delegate.getDoubleValue(); } @Override public float getFloatValue() throws IOException, JsonParseException { return delegate.getFloatValue(); } @Override public int getIntValue() throws IOException, JsonParseException { return delegate.getIntValue(); } @Override public long getLongValue() throws IOException, JsonParseException { return delegate.getLongValue(); } @Override public NumberType getNumberType() throws IOException, JsonParseException { return delegate.getNumberType(); } @Override public Number getNumberValue() throws IOException, JsonParseException { return delegate.getNumberValue(); } /* /********************************************************** /* Public API, access to token information, coercion/conversion /********************************************************** */ @Override public int getValueAsInt() throws IOException, JsonParseException { return delegate.getValueAsInt(); } @Override public int getValueAsInt(int defaultValue) throws IOException, JsonParseException { return delegate.getValueAsInt(defaultValue); } @Override public long getValueAsLong() throws IOException, JsonParseException { return delegate.getValueAsLong(); } @Override public long getValueAsLong(long defaultValue) throws IOException, JsonParseException { return delegate.getValueAsLong(defaultValue); } @Override public double getValueAsDouble() throws IOException, JsonParseException { return delegate.getValueAsDouble(); } @Override public double getValueAsDouble(double defaultValue) throws IOException, JsonParseException { return delegate.getValueAsDouble(defaultValue); } @Override public boolean getValueAsBoolean() throws IOException, JsonParseException { return delegate.getValueAsBoolean(); } @Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException, JsonParseException { return delegate.getValueAsBoolean(defaultValue); } @Override public String getValueAsString() throws IOException, JsonParseException { return delegate.getValueAsString(); } @Override public String getValueAsString(String defaultValue) throws IOException, JsonParseException { return delegate.getValueAsString(defaultValue); } /* /********************************************************** /* Public API, access to token values, other /********************************************************** */ @Override public Object getEmbeddedObject() throws IOException, JsonParseException { return delegate.getEmbeddedObject(); } @Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException { return delegate.getBinaryValue(b64variant); } @Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException, JsonParseException { return delegate.readBinaryValue(b64variant, out); } @Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); } @Override public JsonToken nextToken() throws IOException, JsonParseException { return delegate.nextToken(); } @Override public JsonToken nextValue() throws IOException, JsonParseException { return delegate.nextValue(); } @Override public JsonParser skipChildren() throws IOException, JsonParseException { delegate.skipChildren(); // NOTE: must NOT delegate this method to delegate, needs to be self-reference for chaining return this; } } JsonParserSequence.java000066400000000000000000000106601215056622200335720ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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; /** * Index of the next parser in {@link #_parsers}. */ protected int _nextParser; /* ******************************************************* * Construction ******************************************************* */ protected JsonParserSequence(JsonParser[] parsers) { super(parsers[0]); _parsers = parsers; _nextParser = 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(JsonParser first, JsonParser second) { if (!(first instanceof JsonParserSequence || second instanceof JsonParserSequence)) { // simple: return new JsonParserSequence(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(p.toArray(new JsonParser[p.size()])); } protected void addFlattenedActiveParsers(List result) { for (int i = _nextParser-1, len = _parsers.length; i < len; ++i) { JsonParser p = _parsers[i]; if (p instanceof JsonParserSequence) { ((JsonParserSequence) p).addFlattenedActiveParsers(result); } else { result.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, JsonParseException { JsonToken t = delegate.nextToken(); if (t != null) return t; while (switchToNext()) { t = delegate.nextToken(); if (t != null) return t; } return null; } /* /******************************************************* /* 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 parser from the current one * to next parser in sequence, if there is another parser left, * making this the new delegate. Old delegate is returned if * switch succeeds. * * @return True if switch succeeded; false otherwise */ protected boolean switchToNext() { if (_nextParser >= _parsers.length) { return false; } delegate = _parsers[_nextParser++]; return true; } } MinimalPrettyPrinter.java000066400000000000000000000110661215056622200341560ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/utilpackage com.fasterxml.jackson.core.util; import java.io.IOException; import com.fasterxml.jackson.core.JsonGenerationException; 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 = -562765100295218442L; /** * Default String used for separating root values is single space. */ public final static String DEFAULT_ROOT_VALUE_SEPARATOR = " "; protected String _rootValueSeparator = DEFAULT_ROOT_VALUE_SEPARATOR; /* /********************************************************** /* Life-cycle, construction, configuration /********************************************************** */ public MinimalPrettyPrinter() { this(DEFAULT_ROOT_VALUE_SEPARATOR); } public MinimalPrettyPrinter(String rootValueSeparator) { _rootValueSeparator = rootValueSeparator; } public void setRootValueSeparator(String sep) { _rootValueSeparator = sep; } /* /********************************************************** /* PrettyPrinter impl /********************************************************** */ @Override public void writeRootValueSeparator(JsonGenerator jg) throws IOException, JsonGenerationException { if (_rootValueSeparator != null) { jg.writeRaw(_rootValueSeparator); } } @Override public void writeStartObject(JsonGenerator jg) throws IOException, JsonGenerationException { jg.writeRaw('{'); } @Override public void beforeObjectEntries(JsonGenerator jg) throws IOException, JsonGenerationException { // 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 jg) throws IOException, JsonGenerationException { jg.writeRaw(':'); } /** * 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 jg) throws IOException, JsonGenerationException { jg.writeRaw(','); } @Override public void writeEndObject(JsonGenerator jg, int nrOfEntries) throws IOException, JsonGenerationException { jg.writeRaw('}'); } @Override public void writeStartArray(JsonGenerator jg) throws IOException, JsonGenerationException { jg.writeRaw('['); } @Override public void beforeArrayValues(JsonGenerator jg) throws IOException, JsonGenerationException { // 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 jg) throws IOException, JsonGenerationException { jg.writeRaw(','); } @Override public void writeEndArray(JsonGenerator jg, int nrOfValues) throws IOException, JsonGenerationException { jg.writeRaw(']'); } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java000066400000000000000000000530161215056622200321520ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import java.math.BigDecimal; import java.util.ArrayList; 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 */ final static int MIN_SEGMENT_LEN = 1000; /** * Let's limit maximum segment length to something sensible * like 256k */ final static int MAX_SEGMENT_LEN = 0x40000; /* /********************************************************** /* 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 = false; // // // 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; } /** * 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.CharBufferType.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(); } } /** * 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 = findBuffer(len); } _currentSize = _segmentSize = 0; append(buf, start, len); } public void resetWithString(String value) { _inputBuffer = null; _inputStart = -1; _inputLen = 0; _resultString = value; _resultArray = null; if (_hasSegments) { clearSegments(); } _currentSize = 0; } /** * Helper method used to find a buffer to use, ideally one * recycled earlier. */ private char[] findBuffer(int needed) { if (_allocator != null) { return _allocator.allocCharBuffer(BufferRecycler.CharBufferType.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; } 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; } // 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 = buildResultArray(); } 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 new BigDecimal(_resultArray); } // Or a shared buffer? if (_inputStart >= 0) { return new BigDecimal(_inputBuffer, _inputStart, _inputLen); } // Or if not, just a single buffer (the usual case) if (_segmentSize == 0) { return new BigDecimal(_currentSegment, 0, _currentSize); } // If not, let's just get it aggregated... return new BigDecimal(contentsAsArray()); } /** * Convenience method for converting contents of the buffer * into a Double value. */ public double contentsAsDouble() throws NumberFormatException { return NumberInput.parseDouble(contentsAsString()); } /* /********************************************************** /* 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. */ // Except, as per [Issue-24], not for HUGE appends... so: 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. */ // Except, as per [Issue-24], not for HUGE appends... so: 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 = findBuffer(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 = findBuffer(0); } return curr; } public int getCurrentSegmentSize() { return _currentSize; } public void setCurrentLength(int len) { _currentSize = len; } public char[] finishCurrentSegment() { if (_segments == null) { _segments = new ArrayList(); } _hasSegments = true; _segments.add(_currentSegment); int oldLen = _currentSegment.length; _segmentSize += oldLen; // Let's grow segments by 50% int newLen = Math.min(oldLen + (oldLen >> 1), MAX_SEGMENT_LEN); char[] curr = _charArray(newLen); _currentSize = 0; _currentSegment = curr; return curr; } /** * Method called to expand size of the current segment, to * accomodate for more contiguous content. Usually only * used when parsing tokens like names. */ public char[] expandCurrentSegment() { char[] curr = _currentSegment; // Let's grow by 50% int len = curr.length; // Must grow by at least 1 char, no matter what int newLen = (len == MAX_SEGMENT_LEN) ? (MAX_SEGMENT_LEN + 1) : Math.min(MAX_SEGMENT_LEN, len + (len >> 1)); _currentSegment = _charArray(newLen); System.arraycopy(curr, 0, _currentSegment, 0, len); return _currentSegment; } /* /********************************************************** /* 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 = findBuffer(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; int oldLen = curr.length; // Let's grow segments by 50% minimum int sizeAddition = oldLen >> 1; if (sizeAddition < minNewSegmentSize) { sizeAddition = minNewSegmentSize; } curr = _charArray(Math.min(MAX_SEGMENT_LEN, oldLen + sizeAddition)); _currentSize = 0; _currentSegment = curr; } private char[] buildResultArray() { if (_resultString != null) { // Can take a shortcut... return _resultString.toCharArray(); } char[] result; // Do we use shared array? if (_inputStart >= 0) { if (_inputLen < 1) { return NO_CHARS; } result = _charArray(_inputLen); System.arraycopy(_inputBuffer, _inputStart, result, 0, _inputLen); } else { // nope int size = size(); if (size < 1) { return NO_CHARS; } int offset = 0; result = _charArray(size); if (_segments != null) { for (int i = 0, len = _segments.size(); i < len; ++i) { char[] curr = (char[]) _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[] _charArray(int len) { return new char[len]; } } jackson-core-jackson-core-2.2.2/src/main/java/com/fasterxml/jackson/core/util/VersionUtil.java000066400000000000000000000225531215056622200323610ustar00rootroot00000000000000package 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. */ public class VersionUtil { /** * @deprecated Since 2.2, use of version file is deprecated, and generated * class should be used instead. */ @Deprecated public final static String VERSION_FILE = "VERSION.txt"; public final static String PACKAGE_VERSION_CLASS_NAME = "PackageVersion"; // public final static String PACKAGE_VERSION_FIELD = "VERSION"; private final static Pattern VERSION_SEPARATOR = Pattern.compile("[-_./;:]"); private final Version _version; /* /********************************************************** /* Instance life-cycle, accesso /********************************************************** */ protected VersionUtil() { Version v = null; try { /* Class we pass only matters for resource-loading: can't use this Class * (as it's just being loaded at this point), nor anything that depends on it. */ v = VersionUtil.versionFor(getClass()); } catch (Exception e) { // not good to dump to stderr; but that's all we have at this low level System.err.println("ERROR: Failed to load Version information for bundle (via "+getClass().getName()+")."); } if (v == null) { v = Version.unknownVersion(); } _version = v; } public Version version() { return _version; } /* /********************************************************** /* 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. * * Next, if that fails, class loader that loaded specified class is * asked to load resource with name "VERSION" from same location * (package) as class itself had. * * If no version information is found, {@link Version#unknownVersion()} is returned. */ public static Version versionFor(Class cls) { Version packageVersion = packageVersionFor(cls); if (packageVersion != null) { return packageVersion; } final InputStream in = cls.getResourceAsStream(VERSION_FILE); if (in == null) return Version.unknownVersion(); try { InputStreamReader reader = new InputStreamReader(in, "UTF-8"); try { return doReadVersion(reader); } finally { try { reader.close(); } catch (IOException ignored) { } } } catch (UnsupportedEncodingException e) { return Version.unknownVersion(); } finally { try { in.close(); } catch (IOException e) { throw new RuntimeException(e); } } } /** * 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) { Class versionInfoClass = null; try { Package p = cls.getPackage(); String versionInfoClassName = new StringBuilder(p.getName()) .append(".") .append(PACKAGE_VERSION_CLASS_NAME) .toString(); versionInfoClass = Class.forName(versionInfoClassName, true, cls.getClassLoader()); } catch (Exception e) { // ok to be missing (not good, acceptable) return null; } if (versionInfoClass == null) { return null; } // However, if class exists, it better work correctly, no swallowing exceptions Object v; try { v = versionInfoClass.newInstance(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException("Failed to instantiate "+versionInfoClass.getName() +" to find version information, problem: "+e.getMessage(), e); } if (!(v instanceof Versioned)) { throw new IllegalArgumentException("Bad version class "+versionInfoClass.getName() +": does not implement "+Versioned.class.getName()); } return ((Versioned) v).version(); } private static Version doReadVersion(final Reader reader) { String version = null, group = null, artifact = null; final BufferedReader br = new BufferedReader(reader); try { version = br.readLine(); if (version != null) { group = br.readLine(); if (group != null) artifact = br.readLine(); } } catch (IOException ignored) { } finally { try { br.close(); } catch (IOException ignored) { } } // We don't trim() version: parseVersion() takes care ot that if (group != null) group = group.trim(); if (artifact != null) artifact = artifact.trim(); return parseVersion(version, group, artifact); } /** * 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 classLoader 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 */ public static Version mavenVersionFor(ClassLoader classLoader, String groupId, String artifactId) { InputStream pomPoperties = classLoader.getResourceAsStream("META-INF/maven/" + groupId.replaceAll("\\.", "/") + "/" + artifactId + "/pom.properties"); if (pomPoperties != null) { try { Properties props = new Properties(); props.load(pomPoperties); 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 { try { pomPoperties.close(); } catch (IOException e) { // Ignore } } } return Version.unknownVersion(); } /** * Use variant that takes three arguments instead * * @deprecated */ @Deprecated public static Version parseVersion(String versionStr) { return parseVersion(versionStr, null, null); } public static Version parseVersion(String versionStr, String groupId, String artifactId) { if (versionStr == null) { return null; } versionStr = versionStr.trim(); if (versionStr.length() == 0) { return null; } String[] parts = VERSION_SEPARATOR.split(versionStr); int major = parseVersionPart(parts[0]); int minor = (parts.length > 1) ? parseVersionPart(parts[1]) : 0; int patch = (parts.length > 2) ? parseVersionPart(parts[2]) : 0; String snapshot = (parts.length > 3) ? parts[3] : null; return new Version(major, minor, patch, snapshot, groupId, artifactId); } protected static int parseVersionPart(String partStr) { partStr = partStr.toString(); int len = partStr.length(); int number = 0; for (int i = 0; i < len; ++i) { char c = partStr.charAt(i); if (c > '9' || c < '0') break; number = (number * 10) + (c - '0'); } return number; } /* /********************************************************** /* 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.2.2/src/main/java/com/fasterxml/jackson/core/util/package-info.java000066400000000000000000000001501215056622200324070ustar00rootroot00000000000000/** * Utility classes used by Jackson Core functionality. */ package com.fasterxml.jackson.core.util; jackson-core-jackson-core-2.2.2/src/main/resources/000077500000000000000000000000001215056622200221755ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/resources/META-INF/000077500000000000000000000000001215056622200233355ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/resources/META-INF/LICENSE000066400000000000000000000005141215056622200243420ustar00rootroot00000000000000This 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.2.2/src/main/resources/META-INF/NOTICE000066400000000000000000000014661215056622200242500ustar00rootroot00000000000000# 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.2.2/src/main/resources/META-INF/services/000077500000000000000000000000001215056622200251605ustar00rootroot00000000000000com.fasterxml.jackson.core.JsonFactory000066400000000000000000000000471215056622200344250ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/main/resources/META-INF/servicescom.fasterxml.jackson.core.JsonFactory jackson-core-jackson-core-2.2.2/src/site/000077500000000000000000000000001215056622200202035ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/site/site.xml000066400000000000000000000016611215056622200216750ustar00rootroot00000000000000 org.apache.maven.skins maven-fluido-skin 1.1 true false cowtowncoder true true

jackson-core-jackson-core-2.2.2/src/test/000077500000000000000000000000001215056622200202165ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/000077500000000000000000000000001215056622200211375ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/000077500000000000000000000000001215056622200217155ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/000077500000000000000000000000001215056622200237225ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/000077500000000000000000000000001215056622200253525ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/000077500000000000000000000000001215056622200263025ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java000066400000000000000000000061061215056622200335010ustar00rootroot00000000000000package com.fasterxml.jackson.core; import java.io.*; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.test.BaseTest; /** * Unit tests for [Issue#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); } /* /********************************************************** /* 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(); } } 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 jg) throws IOException { JsonParser jp = f.createParser(json); while (jp.nextToken() != null) { jg.copyCurrentEvent(jp); } jp.close(); jg.close(); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/TestJsonFactory.java000066400000000000000000000022541215056622200322510ustar00rootroot00000000000000package com.fasterxml.jackson.core; import com.fasterxml.jackson.test.BaseTest; public class TestJsonFactory extends BaseTest { // #72 public void testCopy() throws Exception { JsonFactory jf = new JsonFactory(); // first, verify defaults assertTrue(jf.isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)); assertFalse(jf.isEnabled(JsonParser.Feature.ALLOW_COMMENTS)); assertFalse(jf.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII)); jf.disable(JsonFactory.Feature.INTERN_FIELD_NAMES); jf.enable(JsonParser.Feature.ALLOW_COMMENTS); jf.enable(JsonGenerator.Feature.ESCAPE_NON_ASCII); // then change, verify that changes "stick" assertFalse(jf.isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)); assertTrue(jf.isEnabled(JsonParser.Feature.ALLOW_COMMENTS)); assertTrue(jf.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII)); JsonFactory jf2 = jf.copy(); assertFalse(jf2.isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)); assertTrue(jf.isEnabled(JsonParser.Feature.ALLOW_COMMENTS)); assertTrue(jf.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII)); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/TestVersions.java000066400000000000000000000022661215056622200316230ustar00rootroot00000000000000package 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 [JACKSON-278] */ public class TestVersions extends com.fasterxml.jackson.test.BaseTest { public void testCoreVersions() throws Exception { assertVersion(new JsonFactory().version()); JsonParser 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(); } /* /********************************************************** /* 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.2.2/src/test/java/com/fasterxml/jackson/core/format/000077500000000000000000000000001215056622200275725ustar00rootroot00000000000000TestJsonFormatDetection.java000066400000000000000000000075761215056622200351560ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.test.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.2.2/src/test/java/com/fasterxml/jackson/core/io/000077500000000000000000000000001215056622200267115ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/io/TestIOContext.java000066400000000000000000000053411215056622200322730ustar00rootroot00000000000000package 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.test.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, "not owned"); } // 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, "not owned"); } 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, "not owned"); } 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, "not owned"); } 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, "not owned"); } ctxt.releaseNameCopyBuffer(null); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/io/TestJDKSerializable.java000066400000000000000000000017571215056622200333650ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.io.*; import com.fasterxml.jackson.core.*; public class TestJDKSerializable extends com.fasterxml.jackson.test.BaseTest { public void testLocationSerializability() throws Exception { JsonFactory jf = new JsonFactory(); JsonParser jp = jf.createParser(" { }"); assertToken(JsonToken.START_OBJECT, jp.nextToken()); JsonLocation loc = jp.getCurrentLocation(); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bytes); out.writeObject(loc); out.close(); byte[] stuff = bytes.toByteArray(); ObjectInputStream obIn = new ObjectInputStream(new ByteArrayInputStream(stuff)); JsonLocation loc2 = (JsonLocation) obIn.readObject(); assertNotNull(loc2); assertEquals(loc.getLineNr(), loc2.getLineNr()); assertEquals(loc.getColumnNr(), loc2.getColumnNr()); } } TestJsonStringEncoder.java000066400000000000000000000067551215056622200337520ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.*; import com.fasterxml.jackson.core.io.JsonStringEncoder; public class TestJsonStringEncoder extends com.fasterxml.jackson.test.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); } // 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 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)); } } // [JACKSON-884] 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)); } /* /********************************************************** /* 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.2.2/src/test/java/com/fasterxml/jackson/core/io/TestMergedStream.java000066400000000000000000000037121215056622200327760ustar00rootroot00000000000000package 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.test.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.2.2/src/test/java/com/fasterxml/jackson/core/io/TestUTF8Writer.java000066400000000000000000000032521215056622200323410ustar00rootroot00000000000000package com.fasterxml.jackson.core.io; import java.io.*; import com.fasterxml.jackson.core.io.IOContext; import com.fasterxml.jackson.core.io.UTF8Writer; import com.fasterxml.jackson.core.util.BufferRecycler; public class TestUTF8Writer extends com.fasterxml.jackson.test.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.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 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'); w.close(); assertEquals(1, out.size()); // and this ought to be fine... w.flush(); // as well as some more... w.close(); w.flush(); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/json/000077500000000000000000000000001215056622200272535ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/json/TestBase64Codec.java000066400000000000000000000040321215056622200327370ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import org.junit.Assert; import com.fasterxml.jackson.core.*; public class TestBase64Codec extends com.fasterxml.jackson.test.BaseTest { public void testProps() { Base64Variant std = Base64Variants.MIME; // let's verify basic props of std cocec assertEquals("MIME", std.getName()); assertEquals("MIME", std.toString()); assertTrue(std.usesPadding()); assertFalse(std.usesPaddingChar('X')); assertEquals('=', std.getPaddingChar()); assertTrue(std.usesPaddingChar('=')); assertEquals((byte) '=', std.getPaddingByte()); assertEquals(76, std.getMaxLineLength()); } public void testCharEncoding() throws Exception { Base64Variant std = Base64Variants.MIME; assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Char('?')); assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Char((int) '?')); assertEquals(Base64Variant.BASE64_VALUE_INVALID, std.decodeBase64Char((byte) '?')); assertEquals(0, std.decodeBase64Char('A')); assertEquals(1, std.decodeBase64Char((int) 'B')); assertEquals(2, std.decodeBase64Char((byte)'C')); assertEquals('/', std.encodeBase64BitsAsChar(63)); assertEquals((byte) 'b', std.encodeBase64BitsAsByte(27)); String EXP_STR = "HwdJ"; int TRIPLET = 0x1F0749; StringBuilder sb = new StringBuilder(); std.encodeBase64Chunk(sb, TRIPLET); assertEquals(EXP_STR, sb.toString()); byte[] exp = EXP_STR.getBytes("UTF-8"); byte[] act = new byte[exp.length]; std.encodeBase64Chunk(TRIPLET, act, 0); Assert.assertArrayEquals(exp, act); } @SuppressWarnings("unused") public void testErrors() throws Exception { try { Base64Variant b = new Base64Variant("foobar", "xyz", false, '!', 24); } catch (IllegalArgumentException iae) { verifyException(iae, "length must be exactly"); } } } TestBase64Generation.java000066400000000000000000000100321215056622200337330ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.*; import com.fasterxml.jackson.core.*; public class TestBase64Generation extends com.fasterxml.jackson.test.BaseTest { static class ThrottledInputStream extends FilterInputStream { protected final int _maxBytes; public ThrottledInputStream(byte[] data, int maxBytes) { this(new ByteArrayInputStream(data), maxBytes); } public ThrottledInputStream(InputStream in, int maxBytes) { super(in); _maxBytes = maxBytes; } @Override public int read(byte[] buf) throws IOException { return read(buf, 0, buf.length); } @Override public int read(byte[] buf, int offset, int len) throws IOException { return in.read(buf, offset, Math.min(_maxBytes, len)); } } /* /********************************************************** /* Test methods /********************************************************** */ public void testStreamingWrites() throws Exception { final JsonFactory f = new JsonFactory(); _testStreamingWrites(f, true); _testStreamingWrites(f, false); } // For [#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()); } /* /********************************************************** /* Helper methods /********************************************************** */ private final static Base64Variant[] VARIANTS = { Base64Variants.MIME, Base64Variants.MIME_NO_LINEFEEDS, Base64Variants.MODIFIED_FOR_URL, Base64Variants.PEM }; 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; 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.2.2/src/test/java/com/fasterxml/jackson/core/json/TestBase64Parsing.java000066400000000000000000000116021215056622200333260ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import static org.junit.Assert.assertArrayEquals; import java.io.*; import com.fasterxml.jackson.core.*; public class TestBase64Parsing extends com.fasterxml.jackson.test.BaseTest { public void testBase64UsingInputStream() throws Exception { _testBase64Text(true); } public void testBase64UsingReader() throws Exception { _testBase64Text(false); } // [Issue-15] (streaming binary reads) public void testStreaming() throws IOException { _testStreaming(false); _testStreaming(true); } /* /********************************************************** /* Test helper methods /********************************************************** */ // Test for [JACKSON-631] public void _testBase64Text(boolean useBytes) 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 = new JsonFactory(); 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 jgen; if (useBytes) { bytes.reset(); jgen = jsonFactory.createGenerator(bytes, JsonEncoding.UTF8); } else { chars = new StringWriter(); jgen = jsonFactory.createGenerator(chars); } jgen.writeBinary(variant, input, 0, input.length); jgen.close(); JsonParser jp; if (useBytes) { jp = jsonFactory.createParser(bytes.toByteArray()); } else { jp = jsonFactory.createParser(chars.toString()); } assertToken(JsonToken.VALUE_STRING, jp.nextToken()); byte[] data = null; try { data = jp.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); assertNull(jp.nextToken()); jp.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(boolean useBytes) 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 = new JsonFactory(); final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); StringWriter chars = null; for (int size : SIZES) { byte[] data = _generateData(size); JsonGenerator g; if (useBytes) { bytes.reset(); g = jsonFactory.createGenerator(bytes, JsonEncoding.UTF8); } else { chars = new StringWriter(); g = jsonFactory.createGenerator(chars); } g.writeStartObject(); g.writeFieldName("b"); g.writeBinary(data); g.writeEndObject(); g.close(); // and verify JsonParser p; if (useBytes) { p = jsonFactory.createParser(bytes.toByteArray()); } else { p = jsonFactory.createParser(chars.toString()); } 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()); assertNull(p.nextToken()); p.close(); } } } TestCustomEscaping.java000066400000000000000000000133371215056622200336320ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.test.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) } /* /******************************************************** /* Secondary test methods /******************************************************** */ @SuppressWarnings("resource") 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 jgen; // First: output normally; should not add escaping if (useStream) { jgen = f.createGenerator(bytes, JsonEncoding.UTF8); } else { jgen = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8")); } jgen.writeStartArray(); jgen.writeString(VALUE); jgen.writeEndArray(); jgen.close(); String json = bytes.toString("UTF-8"); assertEquals("["+quote(VALUE)+"]", json); // And then with forced ASCII; first, values bytes = new ByteArrayOutputStream(); if (useStream) { jgen = f.createGenerator(bytes, JsonEncoding.UTF8); } else { jgen = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8")); } jgen.enable(JsonGenerator.Feature.ESCAPE_NON_ASCII); jgen.writeStartArray(); jgen.writeString(VALUE); jgen.writeEndArray(); jgen.close(); json = bytes.toString("UTF-8"); assertEquals("["+quote("chars: [\\u00A0]/[\\u1234]")+"]", json); // and then keys bytes = new ByteArrayOutputStream(); if (useStream) { jgen = f.createGenerator(bytes, JsonEncoding.UTF8); } else { jgen = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8")); } jgen.enable(JsonGenerator.Feature.ESCAPE_NON_ASCII); jgen.writeStartObject(); jgen.writeFieldName(KEY); jgen.writeBoolean(true); jgen.writeEndObject(); jgen.close(); json = bytes.toString("UTF-8"); assertEquals("{"+quote("fun:\\u0088:\\u3456")+":true}", json); } 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 jgen; // First: output normally; should not add escaping if (useStream) { jgen = f.createGenerator(bytes, JsonEncoding.UTF8); } else { jgen = f.createGenerator(new OutputStreamWriter(bytes, "UTF-8")); } jgen.writeStartObject(); jgen.writeStringField(STR_IN, STR_IN); jgen.writeEndObject(); jgen.close(); String json = bytes.toString("UTF-8"); assertEquals("{"+quote(STR_OUT)+":"+quote(STR_OUT)+"}", json); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/json/TestDecorators.java000066400000000000000000000066321215056622200330720ustar00rootroot00000000000000package 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.test.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 = new JsonFactory(); f.setInputDecorator(new SimpleInputDecorator()); JsonParser jp; // first test with Reader jp = f.createParser(new StringReader("{ }")); // should be overridden; assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(789, jp.getIntValue()); jp.close(); // similarly with InputStream jp = f.createParser(new ByteArrayInputStream("[ ]".getBytes("UTF-8"))); // should be overridden; assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(123, jp.getIntValue()); jp.close(); // and with raw bytes jp = f.createParser("[ ]".getBytes("UTF-8")); // should be overridden; assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(456, jp.getIntValue()); jp.close(); } public void testOutputDecoration() throws IOException { JsonFactory f = new JsonFactory(); f.setOutputDecorator(new SimpleOutputDecorator()); JsonGenerator jg; StringWriter sw = new StringWriter(); jg = f.createGenerator(sw); jg.close(); assertEquals("567", sw.toString()); ByteArrayOutputStream out = new ByteArrayOutputStream(); jg = f.createGenerator(out, JsonEncoding.UTF8); jg.close(); assertEquals("123", out.toString("UTF-8")); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/json/TestJsonParser.java000066400000000000000000000452621215056622200330550ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import com.fasterxml.jackson.core.*; import java.io.*; import java.net.URL; import java.util.*; /** * Set of basic unit tests for verifying that the basic parser * functionality works as expected. */ public class TestJsonParser extends com.fasterxml.jackson.test.BaseTest { public void testConfig() throws Exception { JsonParser jp = createParserUsingReader("[ ]"); jp.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE); assertTrue(jp.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); jp.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE); assertFalse(jp.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); jp.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true); assertTrue(jp.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); jp.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false); assertFalse(jp.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); } public void testInterningWithStreams() throws Exception { _testIntern(true, true, "a"); _testIntern(true, false, "b"); } public void testInterningWithReaders() throws Exception { _testIntern(false, true, "c"); _testIntern(false, false, "d"); } private void _testIntern(boolean useStream, boolean enableIntern, String expName) throws IOException { JsonFactory f = new JsonFactory(); f.configure(JsonFactory.Feature.INTERN_FIELD_NAMES, enableIntern); assertEquals(enableIntern, f.isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)); final String JSON = "{ \""+expName+"\" : 1}"; JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // needs to be same of cours String actName = jp.getCurrentName(); assertEquals(expName, actName); if (enableIntern) { assertSame(expName, actName); } else { assertNotSame(expName, actName); } jp.close(); } /** * This basic unit test verifies that example given in the Json * specification (RFC-4627 or later) is properly parsed at * high-level, without verifying values. */ public void testSpecExampleSkipping() throws Exception { doTestSpec(false); } /** * Unit test that verifies that the spec example JSON is completely * parsed, and proper values are given for contents of all * events/tokens. */ public void testSpecExampleFully() throws Exception { doTestSpec(true); } /** * Unit test that verifies that 3 basic keywords (null, true, false) * are properly parsed in various contexts. */ public void testKeywords() throws Exception { final String DOC = "{\n" +"\"key1\" : null,\n" +"\"key2\" : true,\n" +"\"key3\" : false,\n" +"\"key4\" : [ false, null, true ]\n" +"}" ; JsonParser jp = createParserUsingStream(DOC, "UTF-8"); JsonStreamContext ctxt = jp.getParsingContext(); assertTrue(ctxt.inRoot()); assertFalse(ctxt.inArray()); assertFalse(ctxt.inObject()); assertEquals(0, ctxt.getEntryCount()); assertEquals(0, ctxt.getCurrentIndex()); /* Before advancing to content, we should have following * default state... */ assertFalse(jp.hasCurrentToken()); assertNull(jp.getText()); assertNull(jp.getTextCharacters()); assertEquals(0, jp.getTextLength()); // not sure if this is defined but: assertEquals(0, jp.getTextOffset()); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertTrue(jp.hasCurrentToken()); JsonLocation loc = jp.getTokenLocation(); assertNotNull(loc); assertEquals(1, loc.getLineNr()); assertEquals(1, loc.getColumnNr()); ctxt = jp.getParsingContext(); assertFalse(ctxt.inRoot()); assertFalse(ctxt.inArray()); assertTrue(ctxt.inObject()); assertEquals(0, ctxt.getEntryCount()); assertEquals(0, ctxt.getCurrentIndex()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); verifyFieldName(jp, "key1"); assertEquals(2, jp.getTokenLocation().getLineNr()); ctxt = jp.getParsingContext(); assertFalse(ctxt.inRoot()); assertFalse(ctxt.inArray()); assertTrue(ctxt.inObject()); assertEquals(1, ctxt.getEntryCount()); assertEquals(0, ctxt.getCurrentIndex()); assertToken(JsonToken.VALUE_NULL, jp.nextToken()); ctxt = jp.getParsingContext(); assertEquals(1, ctxt.getEntryCount()); assertEquals(0, ctxt.getCurrentIndex()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); verifyFieldName(jp, "key2"); ctxt = jp.getParsingContext(); assertEquals(2, ctxt.getEntryCount()); assertEquals(1, ctxt.getCurrentIndex()); assertToken(JsonToken.VALUE_TRUE, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); verifyFieldName(jp, "key3"); assertToken(JsonToken.VALUE_FALSE, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); verifyFieldName(jp, "key4"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_FALSE, jp.nextToken()); assertToken(JsonToken.VALUE_NULL, jp.nextToken()); assertToken(JsonToken.VALUE_TRUE, jp.nextToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); } public void testInvalidKeywordsStream() throws Exception { _testInvalidKeywords(true); } public void testInvalidKeywordsReader() throws Exception { _testInvalidKeywords(false); } private void _testInvalidKeywords(boolean useStream) throws Exception { doTestInvalidKeyword1(useStream, "nul"); doTestInvalidKeyword1(useStream, "Null"); doTestInvalidKeyword1(useStream, "nulla"); doTestInvalidKeyword1(useStream, "fal"); doTestInvalidKeyword3(useStream, "False"); doTestInvalidKeyword1(useStream, "fals0"); doTestInvalidKeyword1(useStream, "falsett0"); doTestInvalidKeyword1(useStream, "tr"); doTestInvalidKeyword1(useStream, "truE"); doTestInvalidKeyword1(useStream, "treu"); doTestInvalidKeyword1(useStream, "trueenough"); } public void testSkipping() throws Exception { String DOC = "[ 1, 3, [ true, null ], 3, { \"a\":\"b\" }, [ [ ] ], { } ]"; ; JsonParser jp = createParserUsingStream(DOC, "UTF-8"); // First, skipping of the whole thing assertToken(JsonToken.START_ARRAY, jp.nextToken()); jp.skipChildren(); assertEquals(JsonToken.END_ARRAY, jp.getCurrentToken()); JsonToken t = jp.nextToken(); if (t != null) { fail("Expected null at end of doc, got "+t); } jp.close(); // Then individual ones jp = createParserUsingStream(DOC, "UTF-8"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); jp.skipChildren(); // shouldn't move assertToken(JsonToken.VALUE_NUMBER_INT, jp.getCurrentToken()); assertEquals(1, jp.getIntValue()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); // then skip array assertToken(JsonToken.START_ARRAY, jp.nextToken()); jp.skipChildren(); assertToken(JsonToken.END_ARRAY, jp.getCurrentToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertToken(JsonToken.START_OBJECT, jp.nextToken()); jp.skipChildren(); assertToken(JsonToken.END_OBJECT, jp.getCurrentToken()); assertToken(JsonToken.START_ARRAY, jp.nextToken()); jp.skipChildren(); assertToken(JsonToken.END_ARRAY, jp.getCurrentToken()); assertToken(JsonToken.START_OBJECT, jp.nextToken()); jp.skipChildren(); assertToken(JsonToken.END_OBJECT, jp.getCurrentToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); } public void testNameEscaping() throws IOException { _testNameEscaping(false); _testNameEscaping(true); } private void _testNameEscaping(boolean useStream) throws IOException { final Map 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\"!"); JsonFactory jf = new JsonFactory(); 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 jp = useStream ? jf.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8"))) : jf.createParser(new StringReader(DOC)); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // first, sanity check (field name == getText() String act = jp.getCurrentName(); assertEquals(act, getAndVerifyText(jp)); if (!expResult.equals(act)) { String msg = "Failed for name #"+entry+"/"+NAME_MAP.size(); if (expResult.length() != act.length()) { fail(msg+": exp length "+expResult.length()+", actual "+act.length()); } assertEquals(msg, expResult, act); } assertToken(JsonToken.VALUE_NULL, jp.nextToken()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); jp.close(); } } /** * Unit test that verifies that long text segments are handled * correctly; mostly to stress-test underlying segment-based * text buffer(s). */ public void testLongText() throws Exception { final int LEN = 96000; StringBuilder sb = new StringBuilder(LEN + 100); Random r = new Random(99); while (sb.length() < LEN) { sb.append(r.nextInt()); sb.append(" xyz foo"); if (r.nextBoolean()) { sb.append(" and \"bar\""); } else if (r.nextBoolean()) { sb.append(" [whatever].... "); } else { // Let's try some more 'exotic' chars sb.append(" UTF-8-fu: try this {\u00E2/\u0BF8/\uA123!} (look funny?)"); } if (r.nextBoolean()) { if (r.nextBoolean()) { sb.append('\n'); } else if (r.nextBoolean()) { sb.append('\r'); } else { sb.append("\r\n"); } } } final String VALUE = sb.toString(); JsonFactory jf = new JsonFactory(); // Let's use real generator to get json done right StringWriter sw = new StringWriter(LEN + (LEN >> 2)); JsonGenerator jg = jf.createGenerator(sw); jg.writeStartObject(); jg.writeFieldName("doc"); jg.writeString(VALUE); jg.writeEndObject(); jg.close(); final String DOC = sw.toString(); for (int type = 0; type < 3; ++type) { JsonParser jp; switch (type) { default: jp = jf.createParser(DOC.getBytes("UTF-8")); break; case 1: jp = jf.createParser(DOC); break; case 2: // NEW: let's also exercise UTF-32... jp = jf.createParser(encodeInUTF32BE(DOC)); break; } assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("doc", jp.getCurrentName()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); String act = getAndVerifyText(jp); if (act.length() != VALUE.length()) { fail("Expected length "+VALUE.length()+", got "+act.length()); } if (!act.equals(VALUE)) { fail("Long text differs"); } // should still know the field name assertEquals("doc", jp.getCurrentName()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); assertNull(jp.nextToken()); jp.close(); } } /** * Simple unit test that verifies that passing in a byte array * as source works as expected. */ public void testBytesAsSource() throws Exception { String JSON = "[ 1, 2, 3, 4 ]"; byte[] b = JSON.getBytes("UTF-8"); int offset = 50; int len = b.length; byte[] src = new byte[offset + len + offset]; System.arraycopy(b, 0, src, offset, len); JsonFactory jf = new JsonFactory(); JsonParser jp = jf.createParser(src, offset, len); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(1, jp.getIntValue()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(2, jp.getIntValue()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(3, jp.getIntValue()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(4, jp.getIntValue()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertNull(jp.nextToken()); jp.close(); } // [JACKSON-632] public void testUtf8BOMHandling() throws Exception { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); // first, write BOM: bytes.write(0xEF); bytes.write(0xBB); bytes.write(0xBF); bytes.write("[ 1 ]".getBytes("UTF-8")); JsonFactory jf = new JsonFactory(); JsonParser jp = jf.createParser(bytes.toByteArray()); assertEquals(JsonToken.START_ARRAY, jp.nextToken()); // should also have skipped first 3 bytes of BOM; but do we have offset available? /* JsonLocation loc = jp.getTokenLocation(); assertEquals(3, loc.getByteOffset()); assertEquals(-1, loc.getCharOffset()); */ } // [Issue#48] public void testSpacesInURL() throws Exception { File f = File.createTempFile("pre fix&stuff", ".txt"); BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f), "UTF-8")); w.write("{ }"); w.close(); URL url = f.toURI().toURL(); JsonFactory jf = new JsonFactory(); JsonParser jp = jf.createParser(url); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); jp.close(); } /* /********************************************************** /* Helper methods /********************************************************** */ private void doTestSpec(boolean verify) throws IOException { // First, using a StringReader: doTestSpecIndividual(null, verify); // Then with streams using supported encodings: doTestSpecIndividual("UTF-8", verify); doTestSpecIndividual("UTF-16BE", verify); doTestSpecIndividual("UTF-16LE", verify); /* Hmmh. UTF-32 is harder only because JDK doesn't come with * a codec for it. Can't test it yet using this method */ doTestSpecIndividual("UTF-32", verify); } private void doTestSpecIndividual(String enc, boolean verify) throws IOException { String doc = SAMPLE_DOC_JSON_SPEC; JsonParser jp; if (enc == null) { jp = createParserUsingReader(doc); } else { jp = createParserUsingStream(doc, enc); } verifyJsonSpecSampleDoc(jp, verify); jp.close(); } private void doTestInvalidKeyword1(boolean useStream, String value) throws IOException { final String doc = "{ \"key1\" : "+value+" }"; JsonParser jp = useStream ? createParserUsingStream(doc, "UTF-8") : createParserUsingReader(doc); assertToken(JsonToken.START_OBJECT, jp.nextToken()); /* 24-Nov-2008, tatu: Note that depending on parser impl, we may * get the exception early or late... */ try { assertToken(JsonToken.FIELD_NAME, jp.nextToken()); jp.nextToken(); fail("Expected an exception for malformed value keyword"); } catch (JsonParseException jex) { verifyException(jex, "Unrecognized token"); verifyException(jex, value); } finally { jp.close(); } // Try as root-level value as well: jp = useStream ? createParserUsingStream(value, "UTF-8") : createParserUsingReader(value); try { jp.nextToken(); fail("Expected an exception for malformed value keyword"); } catch (JsonParseException jex) { verifyException(jex, "Unrecognized token"); verifyException(jex, value); } finally { jp.close(); } } private void doTestInvalidKeyword3(boolean useStream, String value) throws IOException { final String doc = "{ \"key1\" : "+value+" }"; JsonParser jp = useStream ? createParserUsingStream(doc, "UTF-8") : this.createParserUsingReader(doc); assertToken(JsonToken.START_OBJECT, jp.nextToken()); /* 24-Nov-2008, tatu: Note that depending on parser impl, we may * get the exception early or late... */ try { assertToken(JsonToken.FIELD_NAME, jp.nextToken()); jp.nextToken(); fail("Expected an exception for malformed value keyword"); } catch (JsonParseException jex) { verifyException(jex, "expected a valid value"); } finally { jp.close(); } } } TestJsonParserBinary.java000066400000000000000000000120221215056622200341270ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import java.io.*; import com.fasterxml.jackson.core.*; import static org.junit.Assert.*; /** * Tests for verifying that accessing base64 encoded content works ok. */ public class TestJsonParserBinary extends com.fasterxml.jackson.test.BaseTest { /* /********************************************************************** /* Unit tests /********************************************************************** */ public void testSimple() throws IOException { // let's test reader (char) based first, then stream (byte) _testSimple(false); _testSimple(true); } public void testInArray() throws IOException { // let's test reader (char) based first, then stream (byte) _testInArray(false); _testInArray(true); } public void testWithEscaped() throws IOException { // let's test reader (char) based first, then stream (byte) _testEscaped(false); _testEscaped(true); } /* /********************************************************************** /* Actual test methods /********************************************************************** */ private void _testSimple(boolean useStream) throws IOException { /* The usual sample input string, from Thomas Hobbes's "Leviathan" * (via Wikipedia) */ final String RESULT = "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure."; final byte[] RESULT_BYTES = RESULT.getBytes("US-ASCII"); // And here's what should produce it... final String INPUT_STR = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz" +"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg" +"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu" +"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo" +"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=" ; final String DOC = "\""+INPUT_STR+"\""; JsonParser jp = _getParser(DOC, useStream); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); byte[] data = jp.getBinaryValue(); assertNotNull(data); assertArrayEquals(RESULT_BYTES, data); } private void _testInArray(boolean useStream) throws IOException { JsonFactory jf = new JsonFactory(); final int entryCount = 7; StringWriter sw = new StringWriter(); JsonGenerator jg = jf.createGenerator(sw); jg.writeStartArray(); byte[][] entries = new byte[entryCount][]; for (int i = 0; i < entryCount; ++i) { byte[] b = new byte[200 + i * 100]; for (int x = 0; x < b.length; ++x) { b[x] = (byte) (i + x); } entries[i] = b; jg.writeBinary(b); } jg.writeEndArray(); jg.close(); JsonParser jp = _getParser(sw.toString(), useStream); assertToken(JsonToken.START_ARRAY, jp.nextToken()); for (int i = 0; i < entryCount; ++i) { assertToken(JsonToken.VALUE_STRING, jp.nextToken()); byte[] b = jp.getBinaryValue(); assertArrayEquals(entries[i], b); } assertToken(JsonToken.END_ARRAY, jp.nextToken()); } private void _testEscaped(boolean useStream) throws IOException { // Input: "Test!" -> "VGVzdCE=" // First, try with embedded linefeed half-way through: String DOC = quote("VGVz\\ndCE="); // note: must double-quote to get linefeed JsonParser jp = _getParser(DOC, useStream); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); byte[] b = jp.getBinaryValue(); assertEquals("Test!", new String(b, "US-ASCII")); assertNull(jp.nextToken()); jp.close(); // and then with escaped chars // DOC = quote("V\\u0047V\\u007AdCE="); // note: must escape backslash... DOC = quote("V\\u0047V\\u007AdCE="); // note: must escape backslash... jp = _getParser(DOC, useStream); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); b = jp.getBinaryValue(); assertEquals("Test!", new String(b, "US-ASCII")); assertNull(jp.nextToken()); jp.close(); } /* /********************************************************************** /* Other helper methods /********************************************************************** */ private JsonParser _getParser(String doc, boolean useStream) throws IOException { JsonFactory jf = new JsonFactory(); if (useStream) { return jf.createParser(doc.getBytes("UTF-8")); } return jf.createParser(new StringReader(doc)); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/json/TestNextXxx.java000066400000000000000000000147251215056622200324150ustar00rootroot00000000000000package 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; import com.fasterxml.jackson.core.SerializableString; import com.fasterxml.jackson.core.io.SerializedString; public class TestNextXxx extends com.fasterxml.jackson.test.BaseTest { /* /******************************************************** /* Wrappers to test InputStream vs Reader /******************************************************** */ // [JACKSON-653] public void testIsNextTokenName() throws Exception { _testIsNextTokenName1(false); _testIsNextTokenName1(true); _testIsNextTokenName2(false); _testIsNextTokenName2(true); } // [Issue#34] public void testIssue34() throws Exception { _testIssue34(false); _testIssue34(true); } // [Issue#38] with nextFieldName public void testIssue38() throws Exception { _testIssue38(false); _testIssue38(true); } /* /******************************************************** /* Actual test code /******************************************************** */ private void _testIsNextTokenName1(boolean useStream) throws Exception { final String DOC = "{\"name\":123,\"name2\":14,\"x\":\"name\"}"; JsonFactory jf = new JsonFactory(); JsonParser jp = useStream ? jf.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8"))) : jf.createParser(new StringReader(DOC)); SerializedString NAME = new SerializedString("name"); assertFalse(jp.nextFieldName(NAME)); assertToken(JsonToken.START_OBJECT, jp.getCurrentToken()); assertTrue(jp.nextFieldName(NAME)); assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken()); assertEquals(NAME.getValue(), jp.getCurrentName()); assertEquals(NAME.getValue(), jp.getText()); assertFalse(jp.nextFieldName(NAME)); assertToken(JsonToken.VALUE_NUMBER_INT, jp.getCurrentToken()); assertEquals(123, jp.getIntValue()); assertFalse(jp.nextFieldName(NAME)); assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken()); assertEquals("name2", jp.getCurrentName()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertFalse(jp.nextFieldName(NAME)); assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken()); assertEquals("x", jp.getCurrentName()); assertFalse(jp.nextFieldName(NAME)); assertToken(JsonToken.VALUE_STRING, jp.getCurrentToken()); assertFalse(jp.nextFieldName(NAME)); assertToken(JsonToken.END_OBJECT, jp.getCurrentToken()); assertFalse(jp.nextFieldName(NAME)); assertNull(jp.getCurrentToken()); jp.close(); } private void _testIsNextTokenName2(boolean useStream) throws Exception { final String DOC = "{\"name\":123,\"name2\":14,\"x\":\"name\"}"; JsonFactory jf = new JsonFactory(); JsonParser jp = useStream ? jf.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8"))) : jf.createParser(new StringReader(DOC)); SerializableString NAME = new SerializedString("name"); assertFalse(jp.nextFieldName(NAME)); assertToken(JsonToken.START_OBJECT, jp.getCurrentToken()); assertTrue(jp.nextFieldName(NAME)); assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken()); assertEquals(NAME.getValue(), jp.getCurrentName()); assertEquals(NAME.getValue(), jp.getText()); assertFalse(jp.nextFieldName(NAME)); assertToken(JsonToken.VALUE_NUMBER_INT, jp.getCurrentToken()); assertEquals(123, jp.getIntValue()); assertFalse(jp.nextFieldName(NAME)); assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken()); assertEquals("name2", jp.getCurrentName()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertFalse(jp.nextFieldName(NAME)); assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken()); assertEquals("x", jp.getCurrentName()); assertFalse(jp.nextFieldName(NAME)); assertToken(JsonToken.VALUE_STRING, jp.getCurrentToken()); assertFalse(jp.nextFieldName(NAME)); assertToken(JsonToken.END_OBJECT, jp.getCurrentToken()); assertFalse(jp.nextFieldName(NAME)); assertNull(jp.getCurrentToken()); jp.close(); } private void _testIssue34(boolean useStream) throws Exception { final int TESTROUNDS = 223; final String DOC_PART = "{ \"fieldName\": 1 }"; // build the big document to trigger issue StringBuilder sb = new StringBuilder(2000); for (int i = 0; i < TESTROUNDS; ++i) { sb.append(DOC_PART); } final String DOC = sb.toString(); SerializableString fieldName = new SerializedString("fieldName"); JsonFactory jf = new JsonFactory(); JsonParser parser = useStream ? jf.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8"))) : jf.createParser(new StringReader(DOC)); for (int i = 0; i < TESTROUNDS - 1; i++) { assertEquals(JsonToken.START_OBJECT, parser.nextToken()); // These will succeed assertTrue(parser.nextFieldName(fieldName)); parser.nextLongValue(-1); assertEquals(JsonToken.END_OBJECT, parser.nextToken()); } assertEquals(JsonToken.START_OBJECT, parser.nextToken()); // This will fail assertTrue(parser.nextFieldName(fieldName)); parser.close(); } private void _testIssue38(boolean useStream) throws Exception { final String DOC = "{\"field\" :\"value\"}"; SerializableString fieldName = new SerializedString("field"); JsonFactory jf = new JsonFactory(); JsonParser parser = useStream ? jf.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8"))) : jf.createParser(new StringReader(DOC)); assertEquals(JsonToken.START_OBJECT, parser.nextToken()); assertTrue(parser.nextFieldName(fieldName)); assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); assertEquals("value", parser.getText()); assertEquals(JsonToken.END_OBJECT, parser.nextToken()); assertNull(parser.nextToken()); parser.close(); } } TestParserNonStandard.java000066400000000000000000000427541215056622200343030ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/jsonpackage com.fasterxml.jackson.core.json; import com.fasterxml.jackson.core.*; public class TestParserNonStandard extends com.fasterxml.jackson.test.BaseTest { // // // And then tests to verify [JACKSON-69]: public void testSimpleUnquoted() throws Exception { _testSimpleUnquoted(false); _testSimpleUnquoted(true); } public void testLargeUnquoted() throws Exception { _testLargeUnquoted(false); _testLargeUnquoted(true); } public void testSingleQuotesDefault() throws Exception { _testSingleQuotesDefault(false); _testSingleQuotesDefault(true); } public void testSingleQuotesEnabled() throws Exception { _testSingleQuotesEnabled(false); _testSingleQuotesEnabled(true); _testSingleQuotesEscaped(false); _testSingleQuotesEscaped(true); } // Test for [JACKSON-267], allowing '@' as name char, for unquoted names public void testNonStandardNameChars() throws Exception { _testNonStandardNameChars(false); _testNonStandardNameChars(true); } // Test for [JACKSON-300] public void testNonStandardAnyCharQuoting() throws Exception { _testNonStandarBackslashQuoting(false); _testNonStandarBackslashQuoting(true); } // Test for [JACKSON-358] public void testLeadingZeroesUTF8() throws Exception { _testLeadingZeroes(true, false); _testLeadingZeroes(true, true); } public void testLeadingZeroesReader() throws Exception { _testLeadingZeroes(false, false); _testLeadingZeroes(false, true); } // [JACKSON-142]: allow NaN public void testAllowNaN() throws Exception { _testAllowNaN(false); _testAllowNaN(true); } // [JACKSON-142]: allow +Inf/-Inf public void testAllowInfinity() throws Exception { _testAllowInf(false); _testAllowInf(true); } /* /**************************************************************** /* Secondary test methods /**************************************************************** */ private void _testLargeUnquoted(boolean useStream) throws Exception { StringBuilder sb = new StringBuilder(5000); sb.append("[\n"); //final int REPS = 2000; final int REPS = 1050; for (int i = 0; i < REPS; ++i) { if (i > 0) { sb.append(','); if ((i & 7) == 0) { sb.append('\n'); } } sb.append("{"); sb.append("abc").append(i&127).append(':'); sb.append((i & 1) != 0); sb.append("}\n"); } sb.append("]"); String JSON = sb.toString(); JsonFactory f = new JsonFactory(); f.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON) ; assertToken(JsonToken.START_ARRAY, jp.nextToken()); for (int i = 0; i < REPS; ++i) { assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("abc"+(i&127), jp.getCurrentName()); assertToken(((i&1) != 0) ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE, jp.nextToken()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); } assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); } private void _testSimpleUnquoted(boolean useStream) throws Exception { final String JSON = "{ a : 1, _foo:true, $:\"money!\", \" \":null }"; JsonFactory f = new JsonFactory(); f.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON) ; assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("a", jp.getCurrentName()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("_foo", jp.getCurrentName()); assertToken(JsonToken.VALUE_TRUE, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("$", jp.getCurrentName()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("money!", jp.getText()); // and then regular quoted one should still work too: assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals(" ", jp.getCurrentName()); assertToken(JsonToken.VALUE_NULL, jp.nextToken()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); jp.close(); } /** * Test to verify that the default parser settings do not * accept single-quotes for String values (field names, * textual values) */ private void _testSingleQuotesDefault(boolean useStream) throws Exception { JsonFactory f = new JsonFactory(); // First, let's see that by default they are not allowed String JSON = "[ 'text' ]"; JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.START_ARRAY, jp.nextToken()); try { jp.nextToken(); fail("Expected exception"); } catch (JsonParseException e) { verifyException(e, "Unexpected character ('''"); } finally { jp.close(); } JSON = "{ 'a':1 }"; jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.START_OBJECT, jp.nextToken()); try { jp.nextToken(); fail("Expected exception"); } catch (JsonParseException e) { verifyException(e, "Unexpected character ('''"); } finally { jp.close(); } } /** * Test to verify [JACKSON-173], optional handling of * single quotes, to allow handling invalid (but, alas, common) * JSON. */ private void _testSingleQuotesEnabled(boolean useStream) throws Exception { JsonFactory f = new JsonFactory(); f.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); String JSON = "{ 'a' : 1, \"foobar\": 'b', '_abcde1234':'d', '\"' : '\"\"', '':'' }"; JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("a", jp.getText()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals("1", jp.getText()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("foobar", jp.getText()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("b", jp.getText()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("_abcde1234", jp.getText()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("d", jp.getText()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("\"", jp.getText()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); //assertEquals("\"\"", jp.getText()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("", jp.getText()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("", jp.getText()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); jp.close(); } // test to verify that we implicitly allow escaping of apostrophe [JACKSON-548] private void _testSingleQuotesEscaped(boolean useStream) throws Exception { JsonFactory f = new JsonFactory(); f.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); String JSON = "[ '16\\'' ]"; JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("16'", jp.getText()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); } private void _testNonStandardNameChars(boolean useStream) throws Exception { JsonFactory f = new JsonFactory(); f.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); String JSON = "{ @type : \"mytype\", #color : 123, *error* : true, " +" hyphen-ated : \"yes\", me+my : null" +"}"; JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("@type", jp.getText()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("mytype", jp.getText()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("#color", jp.getText()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(123, jp.getIntValue()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("*error*", jp.getText()); assertToken(JsonToken.VALUE_TRUE, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("hyphen-ated", jp.getText()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("yes", jp.getText()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("me+my", jp.getText()); assertToken(JsonToken.VALUE_NULL, jp.nextToken()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); jp.close(); } private void _testNonStandarBackslashQuoting(boolean useStream) throws Exception { // first: verify that we get an exception JsonFactory f = new JsonFactory(); assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)); final String JSON = quote("\\'"); JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); try { jp.nextToken(); jp.getText(); fail("Should have thrown an exception for doc <"+JSON+">"); } catch (JsonParseException e) { verifyException(e, "unrecognized character escape"); } finally { jp.close(); } // and then verify it's ok... f.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true); assertTrue(f.isEnabled(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)); jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("'", jp.getText()); jp.close(); } private void _testLeadingZeroes(boolean useStream, boolean appendSpace) throws Exception { // first: verify that we get an exception JsonFactory f = new JsonFactory(); assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS)); String JSON = "00003"; if (appendSpace) { JSON += " "; } JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); try { jp.nextToken(); jp.getText(); fail("Should have thrown an exception for doc <"+JSON+">"); } catch (JsonParseException e) { verifyException(e, "invalid numeric value"); } finally { jp.close(); } // and then verify it's ok when enabled f.configure(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS, true); assertTrue(f.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS)); jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(3, jp.getIntValue()); assertEquals("3", jp.getText()); jp.close(); // Plus, also: verify that leading zero magnitude is ok: JSON = "0"+Integer.MAX_VALUE; if (appendSpace) { JSON += " "; } jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(String.valueOf(Integer.MAX_VALUE), jp.getText()); assertEquals(Integer.MAX_VALUE, jp.getIntValue()); Number nr = jp.getNumberValue(); assertSame(Integer.class, nr.getClass()); jp.close(); } private void _testAllowNaN(boolean useStream) throws Exception { final String JSON = "[ NaN]"; JsonFactory f = new JsonFactory(); assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS)); // without enabling, should get an exception JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.START_ARRAY, jp.nextToken()); try { jp.nextToken(); fail("Expected exception"); } catch (Exception e) { verifyException(e, "non-standard"); } finally { jp.close(); } // we can enable it dynamically (impl detail) f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true); jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); double d = jp.getDoubleValue(); assertTrue(Double.isNaN(d)); assertEquals("NaN", jp.getText()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); // finally, should also work with skipping f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true); jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); } private void _testAllowInf(boolean useStream) throws Exception { final String JSON = "[ -INF, +INF, +Infinity,-Infinity ]"; JsonFactory f = new JsonFactory(); assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS)); // without enabling, should get an exception JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.START_ARRAY, jp.nextToken()); try { jp.nextToken(); fail("Expected exception"); } catch (Exception e) { verifyException(e, "Non-standard token '-INF'"); } finally { jp.close(); } f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true); jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); double d = jp.getDoubleValue(); assertEquals("-INF", jp.getText()); assertTrue(Double.isInfinite(d)); assertTrue(d == Double.NEGATIVE_INFINITY); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); d = jp.getDoubleValue(); assertEquals("+INF", jp.getText()); assertTrue(Double.isInfinite(d)); assertTrue(d == Double.POSITIVE_INFINITY); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); d = jp.getDoubleValue(); assertEquals("+Infinity", jp.getText()); assertTrue(Double.isInfinite(d)); assertTrue(d == Double.POSITIVE_INFINITY); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); d = jp.getDoubleValue(); assertEquals("-Infinity", jp.getText()); assertTrue(Double.isInfinite(d)); assertTrue(d == Double.NEGATIVE_INFINITY); assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); // finally, should also work with skipping f.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true); jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); } } TestParserOverrides.java000066400000000000000000000070241215056622200340210ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.test.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.getCurrentToken()); jp.clearCurrentToken(); assertNull(jp.getCurrentToken()); assertNull(jp.getEmbeddedObject()); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.START_ARRAY, jp.getCurrentToken()); jp.clearCurrentToken(); assertNull(jp.getCurrentToken()); // 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.getCurrentToken()); 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.getCurrentToken()); jp.close(); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/json/TestUtf8Generator.java000066400000000000000000000016661215056622200334640ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import java.io.ByteArrayOutputStream; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.json.UTF8JsonGenerator; import com.fasterxml.jackson.core.io.IOContext; import com.fasterxml.jackson.core.util.BufferRecycler; import com.fasterxml.jackson.test.BaseTest; public class TestUtf8Generator extends BaseTest { 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(); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/json/TestUtf8Parser.java000066400000000000000000000152451215056622200327700ustar00rootroot00000000000000package com.fasterxml.jackson.core.json; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.SerializedString; import com.fasterxml.jackson.test.BaseTest; import java.io.*; import java.util.Random; /** * Set of basic unit tests for verifying that the basic parser * functionality works as expected. */ public class TestUtf8Parser extends BaseTest { final static String[] UTF8_2BYTE_STRINGS = new String[] { /* This may look funny, but UTF8 scanner has fairly * elaborate decoding machinery, and it is indeed * necessary to try out various combinations... */ "b", "A\u00D8", "abc", "c3p0", "12345", "......", "Long\u00FAer", "Latin1-fully-\u00BE-develop\u00A8d", "Some very long name, ridiculously long actually to see that buffer expansion works: \u00BF?" }; final static String[] UTF8_3BYTE_STRINGS = new String[] { "\uC823?", "A\u400F", "1\u1234?", "Ab123\u4034", "Even-longer:\uC023" }; public void testEmptyName() throws Exception { final String DOC = "{ \"\" : \"\" }"; JsonParser jp = createParserUsingStream(DOC, "UTF-8"); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals("", jp.getCurrentName()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("", jp.getText()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); jp.close(); } public void testUtf8Name2Bytes() throws Exception { final String[] NAMES = UTF8_2BYTE_STRINGS; for (int i = 0; i < NAMES.length; ++i) { String NAME = NAMES[i]; String DOC = "{ \""+NAME+"\" : 0 }"; JsonParser jp = createParserUsingStream(DOC, "UTF-8"); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals(NAME, jp.getCurrentName()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); // should retain name during value entry, too assertEquals(NAME, jp.getCurrentName()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); jp.close(); } } public void testUtf8Name3Bytes() throws Exception { final String[] NAMES = UTF8_3BYTE_STRINGS; for (int i = 0; i < NAMES.length; ++i) { String NAME = NAMES[i]; String DOC = "{ \""+NAME+"\" : true }"; JsonParser jp = createParserUsingStream(DOC, "UTF-8"); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals(NAME, jp.getCurrentName()); assertToken(JsonToken.VALUE_TRUE, jp.nextToken()); assertEquals(NAME, jp.getCurrentName()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); jp.close(); } } // How about tests for Surrogate-Pairs? public void testUtf8StringTrivial() throws Exception { String[] VALUES = UTF8_2BYTE_STRINGS; for (int i = 0; i < VALUES.length; ++i) { String VALUE = VALUES[i]; String DOC = "[ \""+VALUE+"\" ]"; JsonParser jp = createParserUsingStream(DOC, "UTF-8"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); String act = getAndVerifyText(jp); if (act.length() != VALUE.length()) { fail("Failed for value #"+(i+1)+"/"+VALUES.length+": length was "+act.length()+", should be "+VALUE.length()); } assertEquals(VALUE, act); assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); } VALUES = UTF8_3BYTE_STRINGS; for (int i = 0; i < VALUES.length; ++i) { String VALUE = VALUES[i]; String DOC = "[ \""+VALUE+"\" ]"; JsonParser jp = createParserUsingStream(DOC, "UTF-8"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals(VALUE, getAndVerifyText(jp)); assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); } } public void testUtf8StringValue() throws Exception { Random r = new Random(13); //int LEN = 72000; int LEN = 720; StringBuilder sb = new StringBuilder(LEN + 20); while (sb.length() < LEN) { int c; if (r.nextBoolean()) { // ascii c = 32 + (r.nextInt() & 0x3F); if (c == '"' || c == '\\') { c = ' '; } } else if (r.nextBoolean()) { // 2-byte c = 160 + (r.nextInt() & 0x3FF); } else if (r.nextBoolean()) { // 3-byte (non-surrogate) c = 8000 + (r.nextInt() & 0x7FFF); } else { // surrogates (2 chars) int value = r.nextInt() & 0x3FFFF; // 20-bit, ~ 1 million sb.append((char) (0xD800 + (value >> 10))); c = (0xDC00 + (value & 0x3FF)); } sb.append((char) c); } ByteArrayOutputStream bout = new ByteArrayOutputStream(LEN); OutputStreamWriter out = new OutputStreamWriter(bout, "UTF-8"); out.write("[\""); String VALUE = sb.toString(); out.write(VALUE); out.write("\"]"); out.close(); byte[] data = bout.toByteArray(); JsonParser jp = new JsonFactory().createParser(new ByteArrayInputStream(data)); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); String act = jp.getText(); assertEquals(VALUE.length(), act.length()); assertEquals(VALUE, act); jp.close(); } // [JACKSON-889] public void testNextFieldName() throws IOException { JsonFactory f = new JsonFactory(); SerializedString id = new SerializedString("id"); ByteArrayOutputStream os = new ByteArrayOutputStream(); os.write('{'); for (int i = 0; i < 3994; i++) { os.write(' '); } os.write("\"id\":2".getBytes("UTF-8")); os.write('}'); JsonParser parser = f.createParser(new ByteArrayInputStream(os.toByteArray())); assertEquals(parser.nextToken(), JsonToken.START_OBJECT); assertTrue(parser.nextFieldName(id)); assertEquals(parser.nextToken(), JsonToken.VALUE_NUMBER_INT); assertEquals(parser.nextToken(), JsonToken.END_OBJECT); parser.close(); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/main/000077500000000000000000000000001215056622200272265ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/main/TestArrayParsing.java000066400000000000000000000041201215056622200333300ustar00rootroot00000000000000package com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; /** * Set of additional unit for verifying array parsing, specifically * edge cases. */ public class TestArrayParsing extends com.fasterxml.jackson.test.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"); } } 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"); } } 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"); } } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/main/TestCharEscaping.java000066400000000000000000000114221215056622200332600ustar00rootroot00000000000000package com.fasterxml.jackson.core.main; 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.test.BaseTest { // for [JACKSON-627] @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 /********************************************************** */ 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"); } } 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()); // 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()); // And then double sequence jp = createParserUsingReader("[\"\\u0041\\u0043\"]"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("AC", jp.getText()); } 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"); } } /** * 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()); } // for [JACKSON-627] 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()); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/main/TestComments.java000066400000000000000000000073451215056622200325270ustar00rootroot00000000000000package com.fasterxml.jackson.core.main; import java.io.*; import com.fasterxml.jackson.core.*; /** * Unit tests for verifying that support for (non-standard) comments * works as expected. */ public class TestComments extends com.fasterxml.jackson.test.BaseTest { final static String DOC_WITH_SLASHSTAR_COMMENT = "[ /* comment:\n ends here */ 1 /* one more ok to have \"unquoted\" */ ]" ; final static String DOC_WITH_SLASHSLASH_COMMENT = "[ // comment...\n 1 \r // one more, not array: [] \n ]" ; /* /********************************************************** /* Test method wrappers /********************************************************** */ /** * Unit test for verifying that by default comments are not * recognized. */ public void testDefaultSettings() throws Exception { JsonFactory jf = new JsonFactory(); assertFalse(jf.isEnabled(JsonParser.Feature.ALLOW_COMMENTS)); JsonParser jp = jf.createParser(new StringReader("[ 1 ]")); assertFalse(jp.isEnabled(JsonParser.Feature.ALLOW_COMMENTS)); } public void testCommentsDisabled() throws Exception { _testDisabled(DOC_WITH_SLASHSTAR_COMMENT, false); _testDisabled(DOC_WITH_SLASHSLASH_COMMENT, false); _testDisabled(DOC_WITH_SLASHSTAR_COMMENT, true); _testDisabled(DOC_WITH_SLASHSLASH_COMMENT, true); } public void testCommentsEnabled() throws Exception { _testEnabled(DOC_WITH_SLASHSTAR_COMMENT, false); _testEnabled(DOC_WITH_SLASHSLASH_COMMENT, false); _testEnabled(DOC_WITH_SLASHSTAR_COMMENT, true); _testEnabled(DOC_WITH_SLASHSLASH_COMMENT, true); } // for [JACKSON-779] public void testCommentsWithUTF8() throws Exception { final String JSON = "/* \u00a9 2099 Yoyodyne Inc. */\n [ \"bar? \u00a9\" ]\n"; _testWithUTF8Chars(JSON, false); _testWithUTF8Chars(JSON, true); } /* /********************************************************** /* Helper methods /********************************************************** */ private void _testWithUTF8Chars(String doc, boolean useStream) throws IOException { // should basically just stream through JsonParser jp = _createParser(doc, useStream, true); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertNull(jp.nextToken()); jp.close(); } private void _testDisabled(String doc, boolean useStream) throws IOException { JsonParser jp = _createParser(doc, useStream, false); try { jp.nextToken(); fail("Expected exception for unrecognized comment"); } catch (JsonParseException je) { // Should have something denoting that user may want to enable 'ALLOW_COMMENTS' verifyException(je, "ALLOW_COMMENTS"); } } private void _testEnabled(String doc, boolean useStream) throws IOException { JsonParser jp = _createParser(doc, useStream, true); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(1, jp.getIntValue()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); } private JsonParser _createParser(String doc, boolean useStream, boolean enabled) throws IOException { JsonFactory jf = new JsonFactory(); jf.configure(JsonParser.Feature.ALLOW_COMMENTS, enabled); JsonParser jp = useStream ? jf.createParser(doc.getBytes("UTF-8")) : jf.createParser(doc); assertToken(JsonToken.START_ARRAY, jp.nextToken()); return jp; } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/main/TestExceptions.java000066400000000000000000000007761215056622200330640ustar00rootroot00000000000000package com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.test.BaseTest; public class TestExceptions extends BaseTest { // For [Issue#10] public void testOriginalMesssage() { JsonProcessingException exc = new JsonParseException("Foobar", JsonLocation.NA); String msg = exc.getMessage(); String orig = exc.getOriginalMessage(); assertEquals("Foobar", orig); assertTrue(msg.length() > orig.length()); } } TestGeneratorArray.java000066400000000000000000000072131215056622200336020ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.test.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.getTypeDesc(), 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 an object"); } } 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.java000066400000000000000000000151431215056622200341230ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.test.BaseTest; 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 { /* /********************************************************** /* Helper classes /********************************************************** */ final static class MyWriter extends StringWriter { boolean mIsClosed = false; public MyWriter() { } @Override public void close() throws IOException { mIsClosed = true; super.close(); } public boolean isClosed() { return mIsClosed; } } final static class MyStream extends ByteArrayOutputStream { boolean mIsClosed = false; public MyStream() { } @Override public void close() throws IOException { mIsClosed = true; super.close(); } public boolean isClosed() { return mIsClosed; } } static class MyBytes extends ByteArrayOutputStream { public int flushed = 0; @Override public void flush() throws IOException { ++flushed; super.flush(); } } static class MyChars extends StringWriter { public int flushed = 0; @Override public void flush() { ++flushed; super.flush(); } } /* /********************************************************** /* 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)); MyWriter output = new MyWriter(); 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); MyWriter output = new MyWriter(); 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); MyStream output = new MyStream(); 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(JsonGenerator.Feature.AUTO_CLOSE_JSON_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 = new JsonFactory(); f.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); 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()); } // [JACKSON-401] public void testAutoFlushOrNot() throws Exception { JsonFactory f = new JsonFactory(); assertTrue(f.isEnabled(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM)); MyChars sw = new MyChars(); JsonGenerator jg = f.createGenerator(sw); jg.writeStartArray(); jg.writeEndArray(); assertEquals(0, sw.flushed); jg.flush(); assertEquals(1, sw.flushed); jg.close(); // ditto with stream MyBytes bytes = new MyBytes(); jg = f.createGenerator(bytes, JsonEncoding.UTF8); jg.writeStartArray(); jg.writeEndArray(); assertEquals(0, bytes.flushed); jg.flush(); assertEquals(1, bytes.flushed); assertEquals(2, bytes.toByteArray().length); jg.close(); // then disable and we should not see flushing again... f.disable(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM); // first with a Writer sw = new MyChars(); jg = f.createGenerator(sw); jg.writeStartArray(); jg.writeEndArray(); assertEquals(0, sw.flushed); jg.flush(); assertEquals(0, sw.flushed); jg.close(); assertEquals("[]", sw.toString()); // and then with OutputStream bytes = new MyBytes(); jg = f.createGenerator(bytes, JsonEncoding.UTF8); jg.writeStartArray(); jg.writeEndArray(); assertEquals(0, bytes.flushed); jg.flush(); assertEquals(0, bytes.flushed); jg.close(); assertEquals(2, bytes.toByteArray().length); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorCopy.java000066400000000000000000000051531215056622200335160ustar00rootroot00000000000000package com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.test.BaseTest; 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.getCurrentToken()); } 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.getCurrentToken()); assertEquals(123, jp.getIntValue()); // And then let's copy the array assertToken(JsonToken.START_ARRAY, jp.nextToken()); gen.copyCurrentStructure(jp); // which will advance parser to matching close Array assertToken(JsonToken.END_ARRAY, jp.getCurrentToken()); 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.getCurrentToken()); jp.close(); gen.close(); assertEquals("{\"a\":1,\"b\":[{\"c\":null}]}", sw.toString()); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorMisc.java000066400000000000000000000253151215056622200335010ustar00rootroot00000000000000package 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. */ public class TestGeneratorMisc extends com.fasterxml.jackson.test.BaseTest { /* /********************************************************** /* Tests for closing, status /********************************************************** */ public void testIsClosed() throws IOException { JsonFactory jf = new JsonFactory(); for (int i = 0; i < 2; ++i) { boolean stream = ((i & 1) == 0); JsonGenerator jg = stream ? jf.createGenerator(new StringWriter()) : jf.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 JsonFactory jf = new JsonFactory(); StringWriter sw = new StringWriter(); JsonGenerator gen = jf.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 = jf.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 = jf.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 { JsonFactory jf = new JsonFactory(); StringWriter sw = new StringWriter(); JsonGenerator gen = jf.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 { JsonFactory jf = new JsonFactory(); StringWriter sw = new StringWriter(); JsonGenerator gen = jf.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 binary data /********************************************************** */ /** * 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 testBinaryWrite() throws Exception { _testBinaryWrite(false); _testBinaryWrite(true); } private void _testBinaryWrite(boolean useCharBased) throws Exception { /* The usual sample input string, from Thomas Hobbes's "Leviathan" * (via Wikipedia) */ final String INPUT = "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[] INPUT_BYTES = INPUT.getBytes("US-ASCII"); // as per MIME variant, result minus lfs = final String OUTPUT = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz" +"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg" +"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu" +"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo" +"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=" ; /* 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, INPUT_BYTES, 0, INPUT_BYTES.length); break; case 1: // array gen.writeStartArray(); gen.writeBinary(b64v, INPUT_BYTES, 0, INPUT_BYTES.length); gen.writeEndArray(); break; default: // object gen.writeStartObject(); gen.writeFieldName("field"); gen.writeBinary(b64v, INPUT_BYTES, 0, INPUT_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(OUTPUT, actualValue); } } /* /********************************************************** /* Tests for object writing /********************************************************** */ /** * Unit test that tries to trigger buffer-boundary conditions */ public void testLongerObjects() throws Exception { JsonFactory jf = new JsonFactory(); for (int i = 0; i < 2; ++i) { boolean useChars = (i == 0); JsonGenerator jgen; ByteArrayOutputStream bout = new ByteArrayOutputStream(200); if (useChars) { jgen = jf.createGenerator(new OutputStreamWriter(bout, "UTF-8")); } else { jgen = jf.createGenerator(bout, JsonEncoding.UTF8); } jgen.writeStartObject(); for (int rounds = 0; rounds < 1500; ++rounds) { for (int letter = 'a'; letter <= 'z'; ++letter) { for (int index = 0; index < 20; ++index) { String name; if (letter > 'f') { name = "X"+letter+index; } else if (letter > 'p') { name = ""+letter+index; } else { name = "__"+index+letter; } jgen.writeFieldName(name); jgen.writeNumber(index-1); } jgen.writeRaw('\n'); } } jgen.writeEndObject(); jgen.close(); byte[] json = bout.toByteArray(); JsonParser jp = jf.createParser(json); assertToken(JsonToken.START_OBJECT, jp.nextToken()); for (int rounds = 0; rounds < 1500; ++rounds) { for (int 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()); } } } TestGeneratorObject.java000066400000000000000000000163761215056622200337440ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.test.BaseTest; 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()); } 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 an array"); } } 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()); } } TestGeneratorWithSerializedString.java000066400000000000000000000064271215056622200366500ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.SerializedString; public class TestGeneratorWithSerializedString extends com.fasterxml.jackson.test.BaseTest { final static String NAME_WITH_QUOTES = "\"name\""; final static String NAME_WITH_LATIN1 = "P\u00f6ll\u00f6"; private final SerializedString quotedName = new SerializedString(NAME_WITH_QUOTES); private final SerializedString latin1Name = new SerializedString(NAME_WITH_LATIN1); public void testSimple() throws Exception { JsonFactory jf = new JsonFactory(); // First using char-backed generator StringWriter sw = new StringWriter(); JsonGenerator jgen = jf.createGenerator(sw); _writeSimple(jgen); jgen.close(); String json = sw.toString(); _verifySimple(jf.createParser(json)); // then using UTF-8 ByteArrayOutputStream out = new ByteArrayOutputStream(); jgen = jf.createGenerator(out, JsonEncoding.UTF8); _writeSimple(jgen); jgen.close(); byte[] jsonB = out.toByteArray(); _verifySimple(jf.createParser(jsonB)); } /* /********************************************************** /* Helper methods /********************************************************** */ private void _writeSimple(JsonGenerator jgen) throws Exception { // Let's just write array of 2 objects jgen.writeStartArray(); jgen.writeStartObject(); jgen.writeFieldName(quotedName); jgen.writeString("a"); jgen.writeFieldName(latin1Name); jgen.writeString("b"); jgen.writeEndObject(); jgen.writeStartObject(); jgen.writeFieldName(latin1Name); jgen.writeString("c"); jgen.writeFieldName(quotedName); jgen.writeString("d"); jgen.writeEndObject(); jgen.writeEndArray(); } private void _verifySimple(JsonParser jp) throws Exception { assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals(NAME_WITH_QUOTES, jp.getText()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("a", jp.getText()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals(NAME_WITH_LATIN1, jp.getText()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("b", jp.getText()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals(NAME_WITH_LATIN1, jp.getText()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("c", jp.getText()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals(NAME_WITH_QUOTES, jp.getText()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals("d", jp.getText()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertNull(jp.nextToken()); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/main/TestJsonFactory.java000066400000000000000000000040471215056622200331770ustar00rootroot00000000000000package com.fasterxml.jackson.core.main; import java.io.*; import com.fasterxml.jackson.core.*; public class TestJsonFactory extends com.fasterxml.jackson.test.BaseTest { public void testGeneratorFeatures() throws Exception { JsonFactory f = new JsonFactory(); assertNull(f.getCodec()); f.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, true); assertTrue(f.isEnabled(JsonGenerator.Feature.QUOTE_FIELD_NAMES)); f.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false); assertFalse(f.isEnabled(JsonGenerator.Feature.QUOTE_FIELD_NAMES)); } public void testParserFeatures() throws Exception { JsonFactory f = new JsonFactory(); assertNull(f.getCodec()); f.configure(JsonFactory.Feature.INTERN_FIELD_NAMES, true); assertTrue(f.isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)); f.configure(JsonFactory.Feature.INTERN_FIELD_NAMES, false); assertFalse(f.isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES)); } 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(); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/main/TestJsonGenerator.java000066400000000000000000000176111215056622200335170ustar00rootroot00000000000000package com.fasterxml.jackson.core.main; 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 TestJsonGenerator extends com.fasterxml.jackson.test.BaseTest { // // // First, tests for primitive (non-structured) values public void testStringWrite() throws Exception { JsonFactory jf = new JsonFactory(); 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 = jf.createGenerator(new OutputStreamWriter(bout, "UTF-8")); } else { gen = jf.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 = jf.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 testIntWrite() throws Exception { doTestIntWrite(false); doTestIntWrite(true); } public void testLongWrite() throws Exception { doTestLongWrite(false); doTestLongWrite(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 = new JsonFactory().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 = new JsonFactory().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 = new JsonFactory().createGenerator(sw); gen.writeNumber(1); gen.writeNumber(2); gen.writeNumber(-13); gen.close(); String docStr = sw.toString(); JsonParser jp = createParserUsingReader(docStr); assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(1, jp.getIntValue()); assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(2, jp.getIntValue()); assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(-13, jp.getIntValue()); jp.close(); } // Convenience methods public void testFieldValueWrites() throws Exception { StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().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()); } /* /********************************************************** /* Internal methods /********************************************************** */ private void doTestIntWrite(boolean pad) throws Exception { int[] VALUES = new int[] { 0, 1, -9, 32, -32, 57, 13240, -9999, Integer.MAX_VALUE, Integer.MAX_VALUE }; for (int i = 0; i < VALUES.length; ++i) { int VALUE = VALUES[i]; StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); gen.writeNumber(VALUE); if (pad) { gen.writeRaw(" "); } gen.close(); String docStr = sw.toString(); JsonParser jp = createParserUsingReader(docStr); JsonToken t = jp.nextToken(); assertNotNull("Document \""+docStr+"\" yielded no tokens", t); // Number are always available as lexical representation too String exp = ""+VALUE; if (!exp.equals(jp.getText())) { fail("Expected '"+exp+"', got '"+jp.getText()); } assertEquals(JsonToken.VALUE_NUMBER_INT, t); assertEquals(VALUE, jp.getIntValue()); assertEquals(null, jp.nextToken()); jp.close(); } } private void doTestLongWrite(boolean pad) throws Exception { long[] VALUES = new long[] { 0L, 1L, -1L, -12005002294L, Long.MIN_VALUE, Long.MAX_VALUE }; for (int i = 0; i < VALUES.length; ++i) { long VALUE = VALUES[i]; StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createGenerator(sw); gen.writeNumber(VALUE); if (pad) { gen.writeRaw(" "); } gen.close(); String docStr = sw.toString(); JsonParser jp = createParserUsingReader(docStr); JsonToken t = jp.nextToken(); assertNotNull("Document \""+docStr+"\" yielded no tokens", t); String exp = ""+VALUE; if (!exp.equals(jp.getText())) { fail("Expected '"+exp+"', got '"+jp.getText()); } assertEquals(JsonToken.VALUE_NUMBER_INT, t); assertEquals(VALUE, jp.getLongValue()); assertEquals(null, jp.nextToken()); jp.close(); } } } TestJsonGeneratorFeatures.java000066400000000000000000000100601215056622200351260ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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 com.fasterxml.jackson.core.*; /** * Set of basic unit tests for verifying that the basic generator * functionality works as expected. */ public class TestJsonGeneratorFeatures extends com.fasterxml.jackson.test.BaseTest { public void testConfigDefaults() throws IOException { JsonFactory jf = new JsonFactory(); JsonGenerator jg = jf.createGenerator(new StringWriter()); assertFalse(jg.isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS)); } public void testFieldNameQuoting() throws IOException { JsonFactory jf = new JsonFactory(); // by default, quoting should be enabled _testFieldNameQuoting(jf, true); // can disable it jf.disable(JsonGenerator.Feature.QUOTE_FIELD_NAMES); _testFieldNameQuoting(jf, false); // and (re)enable: jf.enable(JsonGenerator.Feature.QUOTE_FIELD_NAMES); _testFieldNameQuoting(jf, true); } public void testNonNumericQuoting() throws IOException { JsonFactory jf = new JsonFactory(); // by default, quoting should be enabled _testNonNumericQuoting(jf, true); // can disable it jf.disable(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS); _testNonNumericQuoting(jf, false); // and (re)enable: jf.enable(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS); _testNonNumericQuoting(jf, true); } /** * Testing for [JACKSON-176], ability to force serializing numbers * as JSON Strings. */ public void testNumbersAsJSONStrings() throws IOException { JsonFactory jf = new JsonFactory(); // by default should output numbers as-is: assertEquals("[1,2,1.25,2.25,3001,0.5,-1]", _writeNumbers(jf)); // but if overridden, quotes as Strings jf.configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, true); assertEquals("[\"1\",\"2\",\"1.25\",\"2.25\",\"3001\",\"0.5\",\"-1\"]", _writeNumbers(jf)); } private String _writeNumbers(JsonFactory jf) throws IOException { StringWriter sw = new StringWriter(); JsonGenerator jg = jf.createGenerator(sw); jg.writeStartArray(); jg.writeNumber(1); jg.writeNumber(2L); jg.writeNumber(1.25); jg.writeNumber(2.25f); jg.writeNumber(BigInteger.valueOf(3001)); jg.writeNumber(BigDecimal.valueOf(0.5)); jg.writeNumber("-1"); jg.writeEndArray(); jg.close(); return sw.toString(); } /* /********************************************************** /* Helper methods /********************************************************** */ private void _testFieldNameQuoting(JsonFactory jf, boolean quoted) throws IOException { StringWriter sw = new StringWriter(); JsonGenerator jg = jf.createGenerator(sw); jg.writeStartObject(); jg.writeFieldName("foo"); jg.writeNumber(1); jg.writeEndObject(); jg.close(); String result = sw.toString(); if (quoted) { assertEquals("{\"foo\":1}", result); } else { assertEquals("{foo:1}", result); } } private void _testNonNumericQuoting(JsonFactory jf, boolean quoted) throws IOException { StringWriter sw = new StringWriter(); JsonGenerator jg = jf.createGenerator(sw); jg.writeStartObject(); jg.writeFieldName("double"); jg.writeNumber(Double.NaN); jg.writeEndObject(); jg.writeStartObject(); jg.writeFieldName("float"); jg.writeNumber(Float.NaN); jg.writeEndObject(); jg.close(); String result = sw.toString(); if (quoted) { assertEquals("{\"double\":\"NaN\"} {\"float\":\"NaN\"}", result); } else { assertEquals("{\"double\":NaN} {\"float\":NaN}", result); } } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/main/TestNumberParsing.java000066400000000000000000000070471215056622200335150ustar00rootroot00000000000000package com.fasterxml.jackson.core.main; 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.test.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)); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/main/TestNumericValues.java000066400000000000000000000332171215056622200335210ustar00rootroot00000000000000package com.fasterxml.jackson.core.main; import java.math.BigDecimal; import java.math.BigInteger; import com.fasterxml.jackson.core.*; /** * Set of basic unit tests for verifying that the basic parser * functionality works as expected. */ public class TestNumericValues extends com.fasterxml.jackson.test.BaseTest { public void testSimpleInt() throws Exception { int EXP_I = 1234; JsonParser jp = createParserUsingReader("[ "+EXP_I+" ]"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(JsonParser.NumberType.INT, jp.getNumberType()); assertEquals(""+EXP_I, jp.getText()); assertEquals(EXP_I, jp.getIntValue()); assertEquals((long) EXP_I, jp.getLongValue()); assertEquals((double) EXP_I, jp.getDoubleValue()); assertEquals(BigDecimal.valueOf((long) EXP_I), jp.getDecimalValue()); } public void testIntRange() throws Exception { // let's test with readers and streams, separate code paths: for (int i = 0; i < 2; ++i) { String input = "[ "+Integer.MAX_VALUE+","+Integer.MIN_VALUE+" ]"; JsonParser jp; if (i == 0) { jp = createParserUsingReader(input); } else { jp = this.createParserUsingStream(input, "UTF-8"); } assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(JsonParser.NumberType.INT, jp.getNumberType()); assertEquals(Integer.MAX_VALUE, jp.getIntValue()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(JsonParser.NumberType.INT, jp.getNumberType()); assertEquals(Integer.MIN_VALUE, jp.getIntValue()); } } public void testInvalidIntAccess() throws Exception { JsonParser jp = createParserUsingReader("[ \"abc\" ]"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); try { jp.getIntValue(); fail("Expected error trying to call getIntValue on non-numeric value"); } catch (JsonParseException e) { verifyException(e, "can not use numeric value accessors"); } } public void testSimpleLong() throws Exception { long EXP_L = 12345678907L; JsonParser jp = createParserUsingReader("[ "+EXP_L+" ]"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); // beyond int, should be long assertEquals(JsonParser.NumberType.LONG, jp.getNumberType()); assertEquals(""+EXP_L, jp.getText()); assertEquals(EXP_L, jp.getLongValue()); // Should get an exception if trying to convert to int try { jp.getIntValue(); } catch (JsonParseException jpe) { verifyException(jpe, "out of range"); } assertEquals((double) EXP_L, jp.getDoubleValue()); assertEquals(BigDecimal.valueOf((long) EXP_L), jp.getDecimalValue()); } public void testLongRange() throws Exception { for (int i = 0; i < 2; ++i) { long belowMinInt = -1L + Integer.MIN_VALUE; long aboveMaxInt = 1L + Integer.MAX_VALUE; String input = "[ "+Long.MAX_VALUE+","+Long.MIN_VALUE+","+aboveMaxInt+", "+belowMinInt+" ]"; JsonParser jp; if (i == 0) { jp = createParserUsingReader(input); } else { jp = this.createParserUsingStream(input, "UTF-8"); } assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(JsonParser.NumberType.LONG, jp.getNumberType()); assertEquals(Long.MAX_VALUE, jp.getLongValue()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(JsonParser.NumberType.LONG, jp.getNumberType()); assertEquals(Long.MIN_VALUE, jp.getLongValue()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(JsonParser.NumberType.LONG, jp.getNumberType()); assertEquals(aboveMaxInt, jp.getLongValue()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(JsonParser.NumberType.LONG, jp.getNumberType()); assertEquals(belowMinInt, jp.getLongValue()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); } } public void testBigDecimalRange() throws Exception { for (int i = 0; i < 2; ++i) { // let's test first values outside of Long range BigInteger small = new BigDecimal(Long.MIN_VALUE).toBigInteger(); small = small.subtract(BigInteger.ONE); BigInteger big = new BigDecimal(Long.MAX_VALUE).toBigInteger(); big = big.add(BigInteger.ONE); String input = "[ "+small+" , "+big+"]"; JsonParser jp; if (i == 0) { jp = createParserUsingReader(input); } else { jp = this.createParserUsingStream(input, "UTF-8"); } assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(JsonParser.NumberType.BIG_INTEGER, jp.getNumberType()); assertEquals(small, jp.getBigIntegerValue()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(JsonParser.NumberType.BIG_INTEGER, jp.getNumberType()); assertEquals(big, jp.getBigIntegerValue()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); } } public void testSimpleDouble() throws Exception { final String[] INPUTS = new String[] { "1234.00", "2.1101567E-16", "1.0e5", "2.5e+5", "9e4", "-12e-3", "0.25" }; for (int input = 0; input < 2; ++input) { for (int i = 0; i < INPUTS.length; ++i) { /* Testing double is more difficult, given the rounding * errors and such. But let's try anyways. */ String STR = INPUTS[i]; double EXP_D = Double.parseDouble(STR); String DOC = "["+STR+"]"; JsonParser jp; if (input == 0) { jp = createParserUsingStream(DOC, "UTF-8"); } else { jp = createParserUsingReader(DOC); } assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); assertEquals(STR, jp.getText()); assertEquals(EXP_D, jp.getDoubleValue()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); } } } public void testNumbers() throws Exception { final String DOC = "[ -13, 8100200300, 13.5, 0.00010, -2.033 ]"; for (int input = 0; input < 2; ++input) { JsonParser jp; if (input == 0) { jp = createParserUsingStream(DOC, "UTF-8"); } else { jp = createParserUsingReader(DOC); } assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(-13, jp.getIntValue()); assertEquals(-13L, jp.getLongValue()); assertEquals(-13., jp.getDoubleValue()); assertEquals("-13", jp.getText()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(8100200300L, jp.getLongValue()); // Should get exception for overflow: try { /*int x =*/ jp.getIntValue(); fail("Expected an exception for overflow"); } catch (Exception e) { verifyException(e, "out of range of int"); } assertEquals(8100200300., jp.getDoubleValue()); assertEquals("8100200300", jp.getText()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); assertEquals(13, jp.getIntValue()); assertEquals(13L, jp.getLongValue()); assertEquals(13.5, jp.getDoubleValue()); assertEquals("13.5", jp.getText()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); assertEquals(0, jp.getIntValue()); assertEquals(0L, jp.getLongValue()); assertEquals(0.00010, jp.getDoubleValue()); assertEquals("0.00010", jp.getText()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); assertEquals(-2, jp.getIntValue()); assertEquals(-2L, jp.getLongValue()); assertEquals(-2.033, jp.getDoubleValue()); assertEquals("-2.033", jp.getText()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); } } @SuppressWarnings("resource") public void testLongOverflow() throws Exception { BigInteger below = BigInteger.valueOf(Long.MIN_VALUE); below = below.subtract(BigInteger.ONE); BigInteger above = BigInteger.valueOf(Long.MAX_VALUE); above = above.add(BigInteger.ONE); String DOC_BELOW = below.toString() + " "; String DOC_ABOVE = below.toString() + " "; for (int input = 0; input < 2; ++input) { JsonParser jp; if (input == 0) { jp = createParserUsingStream(DOC_BELOW, "UTF-8"); } else { jp = createParserUsingReader(DOC_BELOW); } jp.nextToken(); try { long x = jp.getLongValue(); fail("Expected an exception for underflow (input "+jp.getText()+"): instead, got long value: "+x); } catch (JsonParseException e) { verifyException(e, "out of range of long"); } jp.close(); if (input == 0) { jp = createParserUsingStream(DOC_ABOVE, "UTF-8"); } else { jp = createParserUsingReader(DOC_ABOVE); } jp.nextToken(); try { long x = jp.getLongValue(); fail("Expected an exception for underflow (input "+jp.getText()+"): instead, got long value: "+x); } catch (JsonParseException e) { verifyException(e, "out of range of long"); } jp.close(); } } /** * Method that tries to test that number parsing works in cases where * input is split between buffer boundaries. */ public void testParsingOfLongerSequences() throws Exception { double[] values = new double[] { 0.01, -10.5, 2.1e9, 4.0e-8 }; StringBuilder sb = new StringBuilder(); for (int i = 0; i < values.length; ++i) { if (i > 0) { sb.append(','); } sb.append(values[i]); } String segment = sb.toString(); int COUNT = 1000; sb = new StringBuilder(COUNT * segment.length() + 20); sb.append("["); for (int i = 0; i < COUNT; ++i) { if (i > 0) { sb.append(','); } sb.append(segment); sb.append('\n'); // let's add somewhat arbitray number of spaces int x = (i & 3); if (i > 300) { x += i % 5; } while (--x > 0) { sb.append(' '); } } sb.append("]"); String DOC = sb.toString(); for (int input = 0; input < 2; ++input) { JsonParser jp; if (input == 0) { jp = createParserUsingStream(DOC, "UTF-8"); } else { jp = createParserUsingReader(DOC); } assertToken(JsonToken.START_ARRAY, jp.nextToken()); for (int i = 0; i < COUNT; ++i) { for (double d : values) { assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); assertEquals(d, jp.getDoubleValue()); } } assertToken(JsonToken.END_ARRAY, jp.nextToken()); } } /* /********************************************************** /* New tests for 1.3 features /********************************************************** */ public void testSimpleBoolean() throws Exception { JsonParser jp = createParserUsingReader("[ true ]"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_TRUE, jp.nextToken()); assertEquals(true, jp.getBooleanValue()); } public void testInvalidBooleanAccess() throws Exception { JsonParser jp = createParserUsingReader("[ \"abc\" ]"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); try { jp.getBooleanValue(); fail("Expected error trying to call getBooleanValue on non-boolean value"); } catch (JsonParseException e) { verifyException(e, "not of boolean type"); } } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/main/TestParserClosing.java000066400000000000000000000126171215056622200335130ustar00rootroot00000000000000package com.fasterxml.jackson.core.main; import static org.junit.Assert.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.test.BaseTest; 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 ]"; JsonFactory f = new JsonFactory(); // Check the default settings assertTrue(f.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); // then change f.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE); assertFalse(f.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); 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()); } public void testAutoCloseReader() throws Exception { final String DOC = "[ 1 ]"; JsonFactory f = new JsonFactory(); f.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE); assertTrue(f.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); 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()); } public void testNoAutoCloseInputStream() throws Exception { final String DOC = "[ 1 ]"; JsonFactory f = new JsonFactory(); f.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE); 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()); } 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()); } /* /********************************************************** /* 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.java000066400000000000000000000067301215056622200336130ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; /** * Unit tests for verifying that additional JsonParser.Feature * settings work as expected. */ public class TestParserFeatures extends com.fasterxml.jackson.test.BaseTest { public void testDefaultSettings() { JsonFactory f = new JsonFactory(); assertTrue(f.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE)); assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_COMMENTS)); assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES)); assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_SINGLE_QUOTES)); assertFalse(f.isEnabled(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS)); } 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 _testQuotesRequired(boolean useStream) throws Exception { final String JSON = "{ test : 3 }"; final String EXP_ERROR_FRAGMENT = "was expecting double-quote to start"; JsonFactory f = new JsonFactory(); JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON) ; assertToken(JsonToken.START_OBJECT, jp.nextToken()); try { jp.nextToken(); } catch (JsonParseException je) { verifyException(je, EXP_ERROR_FRAGMENT); } finally { jp.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 jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.START_ARRAY, jp.nextToken()); try { jp.nextToken(); jp.getText(); fail("Expected exception"); } catch (JsonParseException e) { verifyException(e, "Illegal unquoted character"); } finally { jp.close(); } } private void _testTabsEnabled(boolean useStream) throws Exception { JsonFactory f = new JsonFactory(); f.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); String FIELD = "a\tb"; String VALUE = "\t"; String JSON = "{ "+quote(FIELD)+" : "+quote(VALUE)+"}"; JsonParser jp = useStream ? createParserUsingStream(f, JSON, "UTF-8") : createParserUsingReader(f, JSON); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals(FIELD, jp.getText()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals(VALUE, jp.getText()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); jp.close(); } } TestParserLinefeeds.java000066400000000000000000000037111215056622200337270ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.test.BaseTest; 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,@-178@]"; 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(-178, jp.getIntValue()); assertEquals(3, jp.getCurrentLocation().getLineNr()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertEquals(4, jp.getCurrentLocation().getLineNr()); jp.close(); } } TestParserWithObjects.java000066400000000000000000000136571215056622200342700ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.test.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); } 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)); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/main/TestPrettyPrinter.java000066400000000000000000000166721215056622200336000ustar00rootroot00000000000000package com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.core.util.MinimalPrettyPrinter; 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.test.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); } 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); } // [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 [Issue#26] public void testCustomRootSeparatorWithFactory() throws Exception { JsonFactory jf = new JsonFactory(); jf.setRootValueSeparator("##"); StringWriter sw = new StringWriter(); JsonGenerator gen = jf.createGenerator(sw); gen.writeNumber(13); gen.writeBoolean(false); gen.writeNull(); gen.close(); assertEquals("13##false##null", sw.toString()); } /* /********************************************************** /* Helper methods /********************************************************** */ private String _verifyPrettyPrinter(JsonGenerator gen, StringWriter sw) throws Exception { 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(); 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; } 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.java000066400000000000000000000110351215056622200341360ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.*; /** * @since 1.7 */ public class TestRawStringWriting extends com.fasterxml.jackson.test.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()); } /** * 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()); } /* /********************************************************** /* 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.2.2/src/test/java/com/fasterxml/jackson/core/main/TestScopeMatching.java000066400000000000000000000113121215056622200334530ustar00rootroot00000000000000package com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.test.BaseTest; /** * Set of basic unit tests for verifying that Array/Object scopes * are properly matched. */ public class TestScopeMatching extends BaseTest { public void testUnclosedArray() throws Exception { JsonParser jp = createParserUsingReader("[ 1, 2"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); try { jp.nextToken(); fail("Expected an exception for unclosed ARRAY"); } catch (JsonParseException jpe) { verifyException(jpe, "expected close marker for ARRAY"); } } public void testUnclosedObject() throws Exception { JsonParser jp = createParserUsingReader("{ \"key\" : 3 "); assertToken(JsonToken.START_OBJECT, jp.nextToken()); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); try { jp.nextToken(); fail("Expected an exception for unclosed OBJECT"); } catch (JsonParseException jpe) { verifyException(jpe, "expected close marker for OBJECT"); } } public void testEOFInName() throws Exception { final String JSON = "{ \"abcd"; for (int i = 0; i < 2; ++i) { JsonParser jp = (i == 0) ? createParserUsingReader(JSON) : createParserUsingStream(JSON, "UTF-8"); assertToken(JsonToken.START_OBJECT, jp.nextToken()); try { jp.nextToken(); fail("Expected an exception for EOF"); } catch (JsonParseException jpe) { verifyException(jpe, "Unexpected end-of-input"); } jp.close(); } } public void testWeirdToken() throws Exception { final String JSON = "[ nil ]"; for (int i = 0; i < 2; ++i) { JsonParser jp = (i == 0) ? createParserUsingReader(JSON) : createParserUsingStream(JSON, "UTF-8"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); try { jp.nextToken(); fail("Expected an exception for weird token"); } catch (JsonParseException jpe) { verifyException(jpe, "Unrecognized token"); } jp.close(); } } public void testMismatchArrayToObject() throws Exception { final String JSON = "[ 1, 2 }"; for (int i = 0; i < 2; ++i) { JsonParser jp = (i == 0) ? createParserUsingReader(JSON) : createParserUsingStream(JSON, "UTF-8"); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); try { jp.nextToken(); fail("Expected an exception for incorrectly closed ARRAY"); } catch (JsonParseException jpe) { verifyException(jpe, "Unexpected close marker '}': expected ']'"); } jp.close(); } } public void testMismatchObjectToArray() throws Exception { final String JSON = "{ ]"; for (int i = 0; i < 2; ++i) { JsonParser jp = (i == 0) ? createParserUsingReader(JSON) : createParserUsingStream(JSON, "UTF-8"); assertToken(JsonToken.START_OBJECT, jp.nextToken()); try { jp.nextToken(); fail("Expected an exception for incorrectly closed OBJECT"); } catch (JsonParseException jpe) { verifyException(jpe, "Unexpected close marker ']': expected '}'"); } jp.close(); } } public void testMisssingColon() throws Exception { final String JSON = "{ \"a\" \"b\" }"; for (int i = 0; i < 2; ++i) { JsonParser jp = (i == 0) ? createParserUsingReader(JSON) : createParserUsingStream(JSON, "UTF-8"); assertToken(JsonToken.START_OBJECT, jp.nextToken()); try { // can be either here, or with next one... assertToken(JsonToken.FIELD_NAME, jp.nextToken()); jp.nextToken(); fail("Expected an exception for missing semicolon"); } catch (JsonParseException jpe) { verifyException(jpe, "was expecting a colon"); } jp.close(); } } } TestStringGeneration.java000066400000000000000000000201551215056622200341370ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.test.BaseTest; import java.util.Random; /** * Set of basic unit tests for verifying that the string * generation, including character escaping, works as expected. */ public class TestStringGeneration 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" }; public void testBasicEscaping() throws Exception { doTestBasicEscaping(false); doTestBasicEscaping(true); } public void testLongerRandomSingleChunk() throws Exception { /* Let's first generate 100k of pseudo-random characters, favoring * 7-bit ascii range */ for (int round = 0; round < 80; ++round) { String content = generateRandom(75000+round); doTestLongerRandom(content, false); doTestLongerRandom(content, true); } } public void testLongerRandomMultiChunk() throws Exception { /* Let's first generate 100k of pseudo-random characters, favoring * 7-bit ascii range */ for (int round = 0; round < 70; ++round) { String content = generateRandom(73000+round); doTestLongerRandomMulti(content, false, round); doTestLongerRandomMulti(content, true, round); } } /* /********************************************************** /* Internal methods /********************************************************** */ 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 doTestBasicEscaping(boolean charArray) throws Exception { for (int i = 0; i < SAMPLES.length; ++i) { String VALUE = SAMPLES[i]; StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().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 jp = createParserUsingReader(docStr); assertEquals(JsonToken.START_ARRAY, jp.nextToken()); JsonToken t = jp.nextToken(); assertEquals(JsonToken.VALUE_STRING, t); assertEquals(VALUE, jp.getText()); assertEquals(JsonToken.END_ARRAY, jp.nextToken()); assertEquals(null, jp.nextToken()); jp.close(); } } private void doTestLongerRandom(String text, boolean charArray) throws Exception { ByteArrayOutputStream bow = new ByteArrayOutputStream(text.length()); JsonGenerator gen = new JsonFactory().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 jp = new JsonFactory().createParser(new ByteArrayInputStream(docData)); assertEquals(JsonToken.START_ARRAY, jp.nextToken()); JsonToken t = jp.nextToken(); assertEquals(JsonToken.VALUE_STRING, t); String act = jp.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, jp.nextToken()); assertEquals(null, jp.nextToken()); jp.close(); } private void doTestLongerRandomMulti(String text, boolean charArray, int round) throws Exception { ByteArrayOutputStream bow = new ByteArrayOutputStream(text.length()); JsonGenerator gen = new JsonFactory().createGenerator(bow, JsonEncoding.UTF8); gen.writeStartArray(); gen.writeString(text); gen.writeEndArray(); gen.close(); gen = new JsonFactory().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 jp = new JsonFactory().createParser(new ByteArrayInputStream(docData)); assertEquals(JsonToken.START_ARRAY, jp.nextToken()); offset = 0; while (jp.nextToken() == JsonToken.VALUE_STRING) { // Let's verify, piece by piece String act = jp.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, jp.getCurrentToken()); jp.close(); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/main/TestUnicode.java000066400000000000000000000022211215056622200323140ustar00rootroot00000000000000package com.fasterxml.jackson.core.main; import java.io.IOException; import com.fasterxml.jackson.core.*; public class TestUnicode extends com.fasterxml.jackson.test.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()); } } TestValueConversions.java000066400000000000000000000200571215056622200341630ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; /** * @since 1.6 */ public class TestValueConversions extends com.fasterxml.jackson.test.BaseTest { public void testAsInt() throws Exception { final String input = "[ 1, -3, 4.98, true, false, null, \"-17\", \"foo\" ]"; for (int i = 0; i < 2; ++i) { JsonParser jp; if (i == 0) { jp = createParserUsingReader(input); } else { jp = this.createParserUsingStream(input, "UTF-8"); } assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertEquals(0, jp.getValueAsLong()); assertEquals(9, jp.getValueAsLong(9)); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(1, jp.getValueAsLong()); assertEquals(1, jp.getValueAsLong(-99)); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(-3, jp.getValueAsLong()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); assertEquals(4, jp.getValueAsLong()); assertEquals(4, jp.getValueAsLong(99)); assertToken(JsonToken.VALUE_TRUE, jp.nextToken()); assertEquals(1, jp.getValueAsLong()); assertToken(JsonToken.VALUE_FALSE, jp.nextToken()); assertEquals(0, jp.getValueAsLong()); assertToken(JsonToken.VALUE_NULL, jp.nextToken()); assertEquals(0, jp.getValueAsLong()); assertEquals(0, jp.getValueAsLong(27)); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals(-17, jp.getValueAsLong()); assertEquals(-17, jp.getValueAsLong(3)); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals(0, jp.getValueAsLong()); assertEquals(9, jp.getValueAsLong(9)); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertEquals(0, jp.getValueAsLong()); assertEquals(9, jp.getValueAsLong(9)); jp.close(); } } /** * @since 1.7 */ public void testAsBoolean() throws Exception { final String input = "[ true, false, null, 1, 0, \"true\", \"false\", \"foo\" ]"; for (int i = 0; i < 2; ++i) { JsonParser jp; if (i == 0) { jp = createParserUsingReader(input); } else { jp = this.createParserUsingStream(input, "UTF-8"); } assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertEquals(false, jp.getValueAsBoolean()); assertEquals(true, jp.getValueAsBoolean(true)); assertToken(JsonToken.VALUE_TRUE, jp.nextToken()); assertEquals(true, jp.getValueAsBoolean()); assertToken(JsonToken.VALUE_FALSE, jp.nextToken()); assertEquals(false, jp.getValueAsBoolean()); assertToken(JsonToken.VALUE_NULL, jp.nextToken()); assertEquals(false, jp.getValueAsBoolean()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(1, jp.getIntValue()); assertEquals(true, jp.getValueAsBoolean()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(0, jp.getIntValue()); assertEquals(false, jp.getValueAsBoolean()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals(true, jp.getValueAsBoolean()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals(false, jp.getValueAsBoolean()); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals(false, jp.getValueAsBoolean()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertEquals(false, jp.getValueAsBoolean()); assertEquals(true, jp.getValueAsBoolean(true)); jp.close(); } } public void testAsLong() throws Exception { final String input = "[ 1, -3, 4.98, true, false, null, \"-17\", \"foo\" ]"; for (int i = 0; i < 2; ++i) { JsonParser jp; if (i == 0) { jp = createParserUsingReader(input); } else { jp = this.createParserUsingStream(input, "UTF-8"); } assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertEquals(0L, jp.getValueAsLong()); assertEquals(9L, jp.getValueAsLong(9L)); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(1L, jp.getValueAsLong()); assertEquals(1L, jp.getValueAsLong(-99L)); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(-3L, jp.getValueAsLong()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); assertEquals(4L, jp.getValueAsLong()); assertEquals(4L, jp.getValueAsLong(99L)); assertToken(JsonToken.VALUE_TRUE, jp.nextToken()); assertEquals(1L, jp.getValueAsLong()); assertToken(JsonToken.VALUE_FALSE, jp.nextToken()); assertEquals(0L, jp.getValueAsLong()); assertToken(JsonToken.VALUE_NULL, jp.nextToken()); assertEquals(0L, jp.getValueAsLong()); assertEquals(0L, jp.getValueAsLong(27L)); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals(-17L, jp.getValueAsLong()); assertEquals(-17L, jp.getValueAsLong(3L)); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals(0L, jp.getValueAsLong()); assertEquals(9L, jp.getValueAsLong(9L)); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertEquals(0L, jp.getValueAsLong()); assertEquals(9L, jp.getValueAsLong(9L)); jp.close(); } } public void testAsDouble() throws Exception { final String input = "[ 1, -3, 4.98, true, false, null, \"-17.25\", \"foo\" ]"; for (int i = 0; i < 2; ++i) { JsonParser jp; if (i == 0) { jp = createParserUsingReader(input); } else { jp = this.createParserUsingStream(input, "UTF-8"); } assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertEquals(0.0, jp.getValueAsDouble()); assertEquals(9.0, jp.getValueAsDouble(9.0)); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(1., jp.getValueAsDouble()); assertEquals(1., jp.getValueAsDouble(-99.0)); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(-3., jp.getValueAsDouble()); assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken()); assertEquals(4.98, jp.getValueAsDouble()); assertEquals(4.98, jp.getValueAsDouble(12.5)); assertToken(JsonToken.VALUE_TRUE, jp.nextToken()); assertEquals(1.0, jp.getValueAsDouble()); assertToken(JsonToken.VALUE_FALSE, jp.nextToken()); assertEquals(0.0, jp.getValueAsDouble()); assertToken(JsonToken.VALUE_NULL, jp.nextToken()); assertEquals(0.0, jp.getValueAsDouble()); assertEquals(0.0, jp.getValueAsDouble(27.8)); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals(-17.25, jp.getValueAsDouble()); assertEquals(-17.25, jp.getValueAsDouble(1.9)); assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals(0.0, jp.getValueAsDouble()); assertEquals(1.25, jp.getValueAsDouble(1.25)); assertToken(JsonToken.END_ARRAY, jp.nextToken()); assertEquals(0.0, jp.getValueAsDouble()); assertEquals(7.5, jp.getValueAsDouble(7.5)); jp.close(); } } } TestWithTonsaSymbols.java000066400000000000000000000045551215056622200341540ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/mainpackage com.fasterxml.jackson.core.main; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.test.BaseTest; import java.io.*; /** * 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 jp = useStream ? jf.createParser(new ByteArrayInputStream(doc.getBytes("UTF-8"))) : jf.createParser(new StringReader(doc)); assertToken(JsonToken.START_OBJECT, jp.nextToken()); for (int i = 0; i < FIELD_COUNT; ++i) { assertToken(JsonToken.FIELD_NAME, jp.nextToken()); assertEquals(fieldNameFor(i), jp.getCurrentName()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(i, jp.getIntValue()); } assertToken(JsonToken.END_OBJECT, jp.nextToken()); jp.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.2.2/src/test/java/com/fasterxml/jackson/core/sym/000077500000000000000000000000001215056622200271125ustar00rootroot00000000000000TestByteBasedSymbols.java000066400000000000000000000112431215056622200337520ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/sympackage com.fasterxml.jackson.core.sym; import java.io.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.sym.BytesToNameCanonicalizer; import com.fasterxml.jackson.core.sym.Name; /** * Unit test(s) to verify that handling of (byte-based) symbol tables * is working. Created to verify fix to [JACKSON-5] (although not very * good at catching it...). */ public class TestByteBasedSymbols extends com.fasterxml.jackson.test.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(); } } public void testAuxMethods() throws Exception { final int A_BYTES = 0x41414141; // "AAAA" final int B_BYTES = 0x42424242; // "BBBB" BytesToNameCanonicalizer nc = BytesToNameCanonicalizer.createRoot() .makeChild(true, true); assertNull(nc.findName(A_BYTES)); assertNull(nc.findName(A_BYTES, B_BYTES)); nc.addName("AAAA", new int[] { A_BYTES }, 1); Name n1 = nc.findName(A_BYTES); assertNotNull(n1); assertEquals("AAAA", n1.getName()); nc.addName("AAAABBBB", new int[] { A_BYTES, B_BYTES }, 2); Name n2 = nc.findName(A_BYTES, B_BYTES); assertEquals("AAAABBBB", n2.getName()); assertNotNull(n2); /* and let's then just exercise this method so it gets covered; * it's only used for debugging. */ assertNotNull(nc.toString()); } /* /********************************************************** /* Helper methods /********************************************************** */ protected JsonParser createParser(JsonFactory jf, String input) throws IOException, JsonParseException { 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(); } } TestJsonParserSymbols.java000066400000000000000000000067061215056622200342060ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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 TestJsonParserSymbols extends com.fasterxml.jackson.test.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 _rootByteSymbols.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 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; } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/sym/TestSymbolTables.java000066400000000000000000000104721215056622200332210ustar00rootroot00000000000000package com.fasterxml.jackson.core.sym; import java.io.IOException; public class TestSymbolTables extends com.fasterxml.jackson.test.BaseTest { // 11 3-char snippets that hash to 0xFFFF (with default JDK hashCode() calc), // and which can be combined as // sequences, like, say, 11x11x11 (1331) 9-character thingies final static String[] CHAR_COLLISION_SNIPPETS_31 = { "@~}", "@\u007f^", "A_}", "A`^", "Aa?", "B@}", "BA^", "BB?", "C!}", "C\"^", "C#?" }; final static String[] CHAR_COLLISIONS; static { final String[] SNIPPETS = CHAR_COLLISION_SNIPPETS_31; final int len = SNIPPETS.length; CHAR_COLLISIONS = new String[len*len*len]; int ix = 0; for (int i1 = 0; i1 < len; ++i1) { for (int i2 = 0; i2 < len; ++i2) { for (int i3 = 0; i3 < len; ++i3) { CHAR_COLLISIONS[ix++] = SNIPPETS[i1]+SNIPPETS[i2] + SNIPPETS[i3]; } } } } /* public void testCharBasedCollisions() { CharsToNameCanonicalizer sym = CharsToNameCanonicalizer.createRoot(0); // first, verify that we'd get a few collisions... try { int firstHash = 0; for (String str : CHAR_COLLISIONS) { int hash = sym.calcHash(str); if (firstHash == 0) { firstHash = hash; } else { assertEquals(firstHash, hash); } sym.findSymbol(str.toCharArray(), 0, str.length(), hash); } fail("Should have thrown exception"); } catch (IllegalStateException e) { verifyException(e, "exceeds maximum"); // should fail right after addition: assertEquals(CharsToNameCanonicalizer.MAX_COLL_CHAIN_LENGTH+1, sym.maxCollisionLength()); assertEquals(CharsToNameCanonicalizer.MAX_COLL_CHAIN_LENGTH+1, sym.collisionCount()); // one "non-colliding" entry (head of collision chain), thus: assertEquals(CharsToNameCanonicalizer.MAX_COLL_CHAIN_LENGTH+2, sym.size()); } } */ // 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); final int COUNT = 6000; 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(8192, 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(1401, symbols.collisionCount()); // esp. with collisions; first got about 30 assertEquals(4, symbols.maxCollisionLength()); } // Test for verifying stability of hashCode, wrt collisions, using // synthetic field name generation and byte-based input (UTF-8) public void testSyntheticWithBytes() throws IOException { // pass seed, to keep results consistent: BytesToNameCanonicalizer symbols = BytesToNameCanonicalizer.createRoot(33333).makeChild(true, true); final int COUNT = 6000; for (int i = 0; i < COUNT; ++i) { String id = fieldNameFor(i); int[] quads = BytesToNameCanonicalizer.calcQuads(id.getBytes("UTF-8")); symbols.addName(id, quads, quads.length); } assertEquals(COUNT, symbols.size()); assertEquals(8192, symbols.bucketCount()); //System.out.printf("Byte stuff: collisions %d, max-coll %d\n", symbols.collisionCount(), symbols.maxCollisionLength()); // Fewer collisions than with chars, but still quite a few assertEquals(1686, symbols.collisionCount()); // but not super long collision chains: assertEquals(9, symbols.maxCollisionLength()); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/test/000077500000000000000000000000001215056622200272615ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/test/PackageVersion.java000066400000000000000000000010121215056622200330170ustar00rootroot00000000000000package com.fasterxml.jackson.core.test; import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.core.Versioned; import com.fasterxml.jackson.core.util.VersionUtil; /** * Helper class used for verifying that auto-generated PackageVersion * classes can be used for verification. */ public final class PackageVersion implements Versioned { @Override public Version version() { return VersionUtil.parseVersion( "23.42.64738-foobar", "foobar-group", "foobar-artifact"); } } TestPackageVersion.java000066400000000000000000000010101215056622200335760ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/testpackage com.fasterxml.jackson.core.test; import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.core.util.VersionUtil; public class TestPackageVersion extends com.fasterxml.jackson.test.BaseTest { public void testPackageVersion() { Version expected = new Version(23, 42, 64738, "foobar", "foobar-group", "foobar-artifact"); assertEquals(expected, VersionUtil.packageVersionFor(this.getClass())); assertEquals(expected, VersionUtil.versionFor(this.getClass())); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/util/000077500000000000000000000000001215056622200272575ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/util/TestCharTypes.java000066400000000000000000000007321215056622200326660ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import com.fasterxml.jackson.core.io.CharTypes; public class TestCharTypes extends com.fasterxml.jackson.test.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()); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/util/TestDelegates.java000066400000000000000000000025241215056622200326620ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import java.io.*; import com.fasterxml.jackson.core.*; public class TestDelegates extends com.fasterxml.jackson.test.BaseTest { /** * Test default, non-overridden parser delegate. */ public void testParserDelegate() throws IOException { JsonParser jp = new JsonFactory().createParser("[ 1, true ]"); assertNull(jp.getCurrentToken()); assertToken(JsonToken.START_ARRAY, jp.nextToken()); assertEquals("[", jp.getText()); assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); assertEquals(1, jp.getIntValue()); assertToken(JsonToken.VALUE_TRUE, jp.nextToken()); assertTrue(jp.getBooleanValue()); assertToken(JsonToken.END_ARRAY, jp.nextToken()); jp.close(); assertTrue(jp.isClosed()); } /** * Test default, non-overridden generator delegate. */ public void testGeneratorDelegate() throws IOException { StringWriter sw = new StringWriter(); JsonGenerator jg = new JsonFactory().createGenerator(sw); jg.writeStartArray(); jg.writeNumber(13); jg.writeNull(); jg.writeBoolean(false); jg.writeEndArray(); jg.close(); assertTrue(jg.isClosed()); assertEquals("[13,null,false]", sw.toString()); } } TestNumberPrinting.java000066400000000000000000000057041215056622200336540ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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 * printingg methods work as expected. */ public class TestNumberPrinting extends com.fasterxml.jackson.test.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); } } 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); } } 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.java000066400000000000000000000043071215056622200341710ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/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.test.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.2.2/src/test/java/com/fasterxml/jackson/core/util/TestTextBuffer.java000066400000000000000000000037271215056622200330510ustar00rootroot00000000000000package com.fasterxml.jackson.core.util; import com.fasterxml.jackson.core.util.BufferRecycler; import com.fasterxml.jackson.core.util.TextBuffer; public class TestTextBuffer extends com.fasterxml.jackson.test.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); assertEquals(3, tb.contentsAsArray().length); assertEquals("abc", tb.toString()); assertNotNull(tb.expandCurrentSegment()); } public void testLonger() { TextBuffer tb = new TextBuffer(new BufferRecycler()); 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()); } 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()); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/core/util/TestVersionUtil.java000066400000000000000000000021141215056622200332430ustar00rootroot00000000000000package 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.test.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")); } 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)); } } jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/test/000077500000000000000000000000001215056622200263315ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/com/fasterxml/jackson/test/BaseTest.java000066400000000000000000000351351215056622200307150ustar00rootroot00000000000000package com.fasterxml.jackson.test; import java.io.*; import java.util.Arrays; import junit.framework.TestCase; import com.fasterxml.jackson.core.*; //import static org.junit.Assert.*; public abstract class BaseTest extends TestCase { protected final static String FIELD_BASENAME = "f"; /* /********************************************************** /* 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; } } /* /********************************************************** /* High-level helpers /********************************************************** */ protected void verifyJsonSpecSampleDoc(JsonParser jp, boolean verifyContents) throws IOException { verifyJsonSpecSampleDoc(jp, verifyContents, true); } protected void verifyJsonSpecSampleDoc(JsonParser jp, boolean verifyContents, boolean requireNumbers) throws IOException { if (!jp.hasCurrentToken()) { jp.nextToken(); } assertToken(JsonToken.START_OBJECT, jp.getCurrentToken()); // main object assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Image' if (verifyContents) { verifyFieldName(jp, "Image"); } assertToken(JsonToken.START_OBJECT, jp.nextToken()); // 'image' object assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Width' if (verifyContents) { verifyFieldName(jp, "Width"); } verifyIntToken(jp.nextToken(), requireNumbers); if (verifyContents) { verifyIntValue(jp, SAMPLE_SPEC_VALUE_WIDTH); } assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Height' if (verifyContents) { verifyFieldName(jp, "Height"); } verifyIntToken(jp.nextToken(), requireNumbers); if (verifyContents) { verifyIntValue(jp, SAMPLE_SPEC_VALUE_HEIGHT); } assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Title' if (verifyContents) { verifyFieldName(jp, "Title"); } assertToken(JsonToken.VALUE_STRING, jp.nextToken()); assertEquals(SAMPLE_SPEC_VALUE_TITLE, getAndVerifyText(jp)); assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Thumbnail' if (verifyContents) { verifyFieldName(jp, "Thumbnail"); } assertToken(JsonToken.START_OBJECT, jp.nextToken()); // 'thumbnail' object assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Url' if (verifyContents) { verifyFieldName(jp, "Url"); } assertToken(JsonToken.VALUE_STRING, jp.nextToken()); if (verifyContents) { assertEquals(SAMPLE_SPEC_VALUE_TN_URL, getAndVerifyText(jp)); } assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Height' if (verifyContents) { verifyFieldName(jp, "Height"); } verifyIntToken(jp.nextToken(), requireNumbers); if (verifyContents) { verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_HEIGHT); } assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Width' if (verifyContents) { verifyFieldName(jp, "Width"); } // Width value is actually a String in the example assertToken(JsonToken.VALUE_STRING, jp.nextToken()); if (verifyContents) { assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, getAndVerifyText(jp)); } assertToken(JsonToken.END_OBJECT, jp.nextToken()); // 'thumbnail' object assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'IDs' assertToken(JsonToken.START_ARRAY, jp.nextToken()); // 'ids' array verifyIntToken(jp.nextToken(), requireNumbers); // ids[0] if (verifyContents) { verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID1); } verifyIntToken(jp.nextToken(), requireNumbers); // ids[1] if (verifyContents) { verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID2); } verifyIntToken(jp.nextToken(), requireNumbers); // ids[2] if (verifyContents) { verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID3); } verifyIntToken(jp.nextToken(), requireNumbers); // ids[3] if (verifyContents) { verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID4); } assertToken(JsonToken.END_ARRAY, jp.nextToken()); // 'ids' array assertToken(JsonToken.END_OBJECT, jp.nextToken()); // 'image' object assertToken(JsonToken.END_OBJECT, jp.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 jp, String expName) throws IOException { assertEquals(expName, jp.getText()); assertEquals(expName, jp.getCurrentName()); } protected void verifyIntValue(JsonParser jp, long expValue) throws IOException { // First, via textual assertEquals(String.valueOf(expValue), jp.getText()); } /* /********************************************************** /* Parser/generator construction /********************************************************** */ protected JsonParser createParserUsingReader(String input) throws IOException, JsonParseException { return createParserUsingReader(new JsonFactory(), input); } protected JsonParser createParserUsingReader(JsonFactory f, String input) throws IOException, JsonParseException { return f.createParser(new StringReader(input)); } protected JsonParser createParserUsingStream(String input, String encoding) throws IOException, JsonParseException { return createParserUsingStream(new JsonFactory(), input, encoding); } protected JsonParser createParserUsingStream(JsonFactory f, String input, String encoding) throws IOException, JsonParseException { /* 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); } /* /********************************************************** /* 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 jp) { assertToken(expToken, jp.getCurrentToken()); } 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 jp) throws IOException, JsonParseException { // Ok, let's verify other accessors int actLen = jp.getTextLength(); char[] ch = jp.getTextCharacters(); String str2 = new String(ch, jp.getTextOffset(), actLen); String str = jp.getText(); if (str.length() != actLen) { fail("Internal problem (jp.token == "+jp.getCurrentToken()+"): jp.getText().length() ['"+str+"'] == "+str.length()+"; jp.getTextLength() == "+actLen); } assertEquals("String access via getText(), getTextXxx() must be the same", str, str2); return str; } /* /********************************************************** /* And other helpers /********************************************************** */ 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; } public String quote(String str) { return '"'+str+'"'; } 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 } } } protected String fieldNameFor(int index) { StringBuilder sb = new StringBuilder(16); fieldNameFor(sb, index); return sb.toString(); } } jackson-core-jackson-core-2.2.2/src/test/java/perf/000077500000000000000000000000001215056622200220735ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/java/perf/ConcurrencyReadTest.java000066400000000000000000000042141215056622200266650ustar00rootroot00000000000000package 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.2.2/src/test/resources/000077500000000000000000000000001215056622200222305ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/resources/META-INF/000077500000000000000000000000001215056622200233705ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/resources/META-INF/maven/000077500000000000000000000000001215056622200244765ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/resources/META-INF/maven/foo/000077500000000000000000000000001215056622200252615ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/resources/META-INF/maven/foo/bar/000077500000000000000000000000001215056622200260255ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/resources/META-INF/maven/foo/bar/foo-bar/000077500000000000000000000000001215056622200273525ustar00rootroot00000000000000jackson-core-jackson-core-2.2.2/src/test/resources/META-INF/maven/foo/bar/foo-bar/pom.properties000066400000000000000000000000711215056622200322610ustar00rootroot00000000000000groupId=foo.bar artifactId=foo-bar version=1.2.3-SNAPSHOT