pax_global_header 0000666 0000000 0000000 00000000064 13561642473 0014525 g ustar 00root root 0000000 0000000 52 comment=60956fccd24d6bdb66abb3182cb2234122039b7a
jackson-core-jackson-core-2.10.1/ 0000775 0000000 0000000 00000000000 13561642473 0016540 5 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/.gitattributes 0000664 0000000 0000000 00000000137 13561642473 0021434 0 ustar 00root root 0000000 0000000 # Do not merge `pom.xml` from older version, as it will typically conflict
pom.xml merge=ours
jackson-core-jackson-core-2.10.1/.gitignore 0000664 0000000 0000000 00000000262 13561642473 0020530 0 ustar 00root root 0000000 0000000 # use glob syntax.
syntax: glob
*.class
*~
*.bak
*.off
*.old
*.java.orig
.DS_Store
# building
/target
# Eclipse
.classpath
.project
.settings
# IDEA
*.iml
*.ipr
*.iws
/.idea/
jackson-core-jackson-core-2.10.1/.travis.yml 0000664 0000000 0000000 00000002055 13561642473 0020653 0 ustar 00root root 0000000 0000000 language: java
# 08-Dec-2018, tatu: While it should be possible to run core streaming on Java 6,
# build won't work with anything below Java 8 now
- openjdk8
- oraclejdk11
# Below this line is configuration for deploying to the Sonatype OSS repo
# http://blog.xeiam.com/2013/05/configure-travis-ci-to-deploy-snapshots.html
before_install: "git clone -b travis `git config --get remote.origin.url` target/travis"
after_success: "mvn deploy --settings target/travis/settings.xml"
# whitelist
branches:
only:
- master
- "2.10"
# to make jdk6 work, as per: https://github.com/travis-ci/travis-ci/issues/8199
addons:
apt:
packages:
- openjdk-6-jdk
env:
global:
- secure: "YW0hrdsHvH41pb5uPJ2DGzXrBgOVT7nEyag/bAQoDcSlOQ/g55tnY6rIGkqE/aYD47IroTEhW4yLyM3tZpbrqwagX4dUX90ukjcUwUvFE1ePTSEfdBtuHVwl8f6HmLIIw2yK0dQ1gOJ21T+3g+wddvK+6sWBJJ+s3O1FePDh6X0="
- secure: "sGQxvyfg98BFcJcWHQ5BjvDNhbwdgD1yEfkE3qzH4/gzwD/ND1jKhkCX++Glt3DuyAKhENNzXlSkztdCE5wKfK3X6MVvOgzMgiV/BhHIf09EtAjZ35fe4pr+GZImfGZO3qkViooTz3FDJyKJBA3YyMTuo9/eWK8HlUFCZHTjKP8="
jackson-core-jackson-core-2.10.1/LICENSE 0000664 0000000 0000000 00000000475 13561642473 0017553 0 ustar 00root root 0000000 0000000 This copy of Jackson JSON processor annotations is licensed under the
Apache (Software) License, version 2.0 ("the License").
See the License for details about distribution rights, and the
specific rights regarding derivate works.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
jackson-core-jackson-core-2.10.1/README.md 0000664 0000000 0000000 00000011576 13561642473 0020031 0 ustar 00root root 0000000 0000000 # Overview
This project contains core low-level incremental ("streaming") parser and generator abstractions used by
[Jackson Data Processor](http://wiki.fasterxml.com/JacksonHome).
It also includes the default implementation of handler types (parser, generator) that handle JSON format.
The core abstractions are not JSON specific, although naming does contain 'JSON' in many places, due to historical reasons. Only packages that specifically contain word 'json' are JSON-specific.
This package is the base on which [Jackson data-binding](https://github.com/FasterXML/jackson-databind) package builds on.
It is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
For additional/alternative licensing questions, please contact `info@fasterxml.com`: affordable commercial licenses available for use cases like Android app development.
Alternate data format implementations (like
[Smile (binary JSON)](https://github.com/FasterXML/jackson-dataformat-smile),
[XML](https://github.com/FasterXML/jackson-dataformat-xml),
[CSV](https://github.com/FasterXML/jackson-dataformat-csv))
and [CBOR](https://github.com/FasterXML/jackson-dataformat-cbor)
also build on this base package, implementing the core interfaces,
making it possible to use standard [data-binding package](https://github.com/FasterXML/jackson-databind) regardless of underlying data format.
Project contains versions 2.0 and above: source code for earlier (1.x) versions is available from [Codehaus](http://jackson.codehaus.org) SVN repository.
[](https://travis-ci.org/FasterXML/jackson-core) [](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.jackson.core/jackson-core)
[](http://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-core)
[](https://coveralls.io/github/FasterXML/jackson-core?branch=master)
# Get it!
## Maven
Functionality of this package is contained in
Java package `com.fasterxml.jackson.core`.
To use the package, you need to use following Maven dependency:
```xml
* Note that this is the only non-transient field; used when reading * back from serialized state. *
* Also: must not be private, accessed from `BaseVariants` */ final String _name; /** * Whether this variant uses padding or not. */ private final transient boolean _usesPadding; /** * Character used for padding, if any ({@link #PADDING_CHAR_NONE} if not). */ private final transient char _paddingChar; /** * Maximum number of encoded base64 characters to output during encoding * before adding a linefeed, if line length is to be limited * ({@link java.lang.Integer#MAX_VALUE} if not limited). *
* Note: for some output modes (when writing attributes) linefeeds may * need to be avoided, and this value ignored. */ private final transient int _maxLineLength; /* /********************************************************** /* Life-cycle /********************************************************** */ public Base64Variant(String name, String base64Alphabet, boolean usesPadding, char paddingChar, int maxLineLength) { _name = name; _usesPadding = usesPadding; _paddingChar = paddingChar; _maxLineLength = maxLineLength; // Ok and then we need to create codec tables. // First the main encoding table: int alphaLen = base64Alphabet.length(); if (alphaLen != 64) { throw new IllegalArgumentException("Base64Alphabet length must be exactly 64 (was "+alphaLen+")"); } // And then secondary encoding table and decoding table: base64Alphabet.getChars(0, alphaLen, _base64ToAsciiC, 0); Arrays.fill(_asciiToBase64, BASE64_VALUE_INVALID); for (int i = 0; i < alphaLen; ++i) { char alpha = _base64ToAsciiC[i]; _base64ToAsciiB[i] = (byte) alpha; _asciiToBase64[alpha] = i; } // Plus if we use padding, add that in too if (usesPadding) { _asciiToBase64[(int) paddingChar] = BASE64_VALUE_PADDING; } } /** * "Copy constructor" that can be used when the base alphabet is identical * to one used by another variant except for the maximum line length * (and obviously, name). */ public Base64Variant(Base64Variant base, String name, int maxLineLength) { this(base, name, base._usesPadding, base._paddingChar, maxLineLength); } /** * "Copy constructor" that can be used when the base alphabet is identical * to one used by another variant, but other details (padding, maximum * line length) differ */ public Base64Variant(Base64Variant base, String name, boolean usesPadding, char paddingChar, int maxLineLength) { _name = name; byte[] srcB = base._base64ToAsciiB; System.arraycopy(srcB, 0, this._base64ToAsciiB, 0, srcB.length); char[] srcC = base._base64ToAsciiC; System.arraycopy(srcC, 0, this._base64ToAsciiC, 0, srcC.length); int[] srcV = base._asciiToBase64; System.arraycopy(srcV, 0, this._asciiToBase64, 0, srcV.length); _usesPadding = usesPadding; _paddingChar = paddingChar; _maxLineLength = maxLineLength; } /* /********************************************************** /* Serializable overrides /********************************************************** */ /** * Method used to "demote" deserialized instances back to * canonical ones */ protected Object readResolve() { return Base64Variants.valueOf(_name); } /* /********************************************************** /* Public accessors /********************************************************** */ public String getName() { return _name; } public boolean usesPadding() { return _usesPadding; } public boolean usesPaddingChar(char c) { return c == _paddingChar; } public boolean usesPaddingChar(int ch) { return ch == (int) _paddingChar; } public char getPaddingChar() { return _paddingChar; } public byte getPaddingByte() { return (byte)_paddingChar; } public int getMaxLineLength() { return _maxLineLength; } /* /********************************************************** /* Decoding support /********************************************************** */ /** * @return 6-bit decoded value, if valid character; */ public int decodeBase64Char(char c) { int ch = (int) c; return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID; } public int decodeBase64Char(int ch) { return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID; } public int decodeBase64Byte(byte b) { int ch = (int) b; // note: cast retains sign, so it's from -128 to +127 if (ch < 0) { return BASE64_VALUE_INVALID; } return _asciiToBase64[ch]; } /* /********************************************************** /* Encoding support /********************************************************** */ public char encodeBase64BitsAsChar(int value) { /* Let's assume caller has done necessary checks; this * method must be fast and inlinable */ return _base64ToAsciiC[value]; } /** * Method that encodes given right-aligned (LSB) 24-bit value * into 4 base64 characters, stored in given result buffer. */ public int encodeBase64Chunk(int b24, char[] buffer, int ptr) { buffer[ptr++] = _base64ToAsciiC[(b24 >> 18) & 0x3F]; buffer[ptr++] = _base64ToAsciiC[(b24 >> 12) & 0x3F]; buffer[ptr++] = _base64ToAsciiC[(b24 >> 6) & 0x3F]; buffer[ptr++] = _base64ToAsciiC[b24 & 0x3F]; return ptr; } public void encodeBase64Chunk(StringBuilder sb, int b24) { sb.append(_base64ToAsciiC[(b24 >> 18) & 0x3F]); sb.append(_base64ToAsciiC[(b24 >> 12) & 0x3F]); sb.append(_base64ToAsciiC[(b24 >> 6) & 0x3F]); sb.append(_base64ToAsciiC[b24 & 0x3F]); } /** * Method that outputs partial chunk (which only encodes one * or two bytes of data). Data given is still aligned same as if * it as full data; that is, missing data is at the "right end" * (LSB) of int. * * @param outputBytes Number of encoded bytes included (either 1 or 2) */ public int encodeBase64Partial(int bits, int outputBytes, char[] buffer, int outPtr) { buffer[outPtr++] = _base64ToAsciiC[(bits >> 18) & 0x3F]; buffer[outPtr++] = _base64ToAsciiC[(bits >> 12) & 0x3F]; if (_usesPadding) { buffer[outPtr++] = (outputBytes == 2) ? _base64ToAsciiC[(bits >> 6) & 0x3F] : _paddingChar; buffer[outPtr++] = _paddingChar; } else { if (outputBytes == 2) { buffer[outPtr++] = _base64ToAsciiC[(bits >> 6) & 0x3F]; } } return outPtr; } public void encodeBase64Partial(StringBuilder sb, int bits, int outputBytes) { sb.append(_base64ToAsciiC[(bits >> 18) & 0x3F]); sb.append(_base64ToAsciiC[(bits >> 12) & 0x3F]); if (_usesPadding) { sb.append((outputBytes == 2) ? _base64ToAsciiC[(bits >> 6) & 0x3F] : _paddingChar); sb.append(_paddingChar); } else { if (outputBytes == 2) { sb.append(_base64ToAsciiC[(bits >> 6) & 0x3F]); } } } public byte encodeBase64BitsAsByte(int value) { // As with above, assuming it is 6-bit value return _base64ToAsciiB[value]; } /** * Method that encodes given right-aligned (LSB) 24-bit value * into 4 base64 bytes (ascii), stored in given result buffer. */ public int encodeBase64Chunk(int b24, byte[] buffer, int ptr) { buffer[ptr++] = _base64ToAsciiB[(b24 >> 18) & 0x3F]; buffer[ptr++] = _base64ToAsciiB[(b24 >> 12) & 0x3F]; buffer[ptr++] = _base64ToAsciiB[(b24 >> 6) & 0x3F]; buffer[ptr++] = _base64ToAsciiB[b24 & 0x3F]; return ptr; } /** * Method that outputs partial chunk (which only encodes one * or two bytes of data). Data given is still aligned same as if * it as full data; that is, missing data is at the "right end" * (LSB) of int. * * @param outputBytes Number of encoded bytes included (either 1 or 2) */ public int encodeBase64Partial(int bits, int outputBytes, byte[] buffer, int outPtr) { buffer[outPtr++] = _base64ToAsciiB[(bits >> 18) & 0x3F]; buffer[outPtr++] = _base64ToAsciiB[(bits >> 12) & 0x3F]; if (_usesPadding) { byte pb = (byte) _paddingChar; buffer[outPtr++] = (outputBytes == 2) ? _base64ToAsciiB[(bits >> 6) & 0x3F] : pb; buffer[outPtr++] = pb; } else { if (outputBytes == 2) { buffer[outPtr++] = _base64ToAsciiB[(bits >> 6) & 0x3F]; } } return outPtr; } /* /********************************************************** /* Convenience conversion methods for String to/from bytes /* use case. /********************************************************** */ /** * Convenience method for converting given byte array as base64 encoded * String using this variant's settings. * Resulting value is "raw", that is, not enclosed in double-quotes. * * @param input Byte array to encode */ public String encode(byte[] input) { return encode(input, false); } /** * Convenience method for converting given byte array as base64 encoded String * using this variant's settings, optionally enclosed in double-quotes. * Linefeeds added, if needed, are expressed as 2-character JSON (and Java source) * escape sequence of backslash + `n`. * * @param input Byte array to encode * @param addQuotes Whether to surround resulting value in double quotes or not */ public String encode(byte[] input, boolean addQuotes) { final int inputEnd = input.length; final StringBuilder sb = new StringBuilder(inputEnd + (inputEnd >> 2) + (inputEnd >> 3)); if (addQuotes) { sb.append('"'); } int chunksBeforeLF = getMaxLineLength() >> 2; // Ok, first we loop through all full triplets of data: int inputPtr = 0; int safeInputEnd = inputEnd-3; // to get only full triplets while (inputPtr <= safeInputEnd) { // First, mash 3 bytes into lsb of 32-bit int int b24 = ((int) input[inputPtr++]) << 8; b24 |= ((int) input[inputPtr++]) & 0xFF; b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF); encodeBase64Chunk(sb, b24); if (--chunksBeforeLF <= 0) { // note: must quote in JSON value, so not really useful... sb.append('\\'); sb.append('n'); chunksBeforeLF = getMaxLineLength() >> 2; } } // And then we may have 1 or 2 leftover bytes to encode int inputLeft = inputEnd - inputPtr; // 0, 1 or 2 if (inputLeft > 0) { // yes, but do we have room for output? int b24 = ((int) input[inputPtr++]) << 16; if (inputLeft == 2) { b24 |= (((int) input[inputPtr++]) & 0xFF) << 8; } encodeBase64Partial(sb, b24, inputLeft); } if (addQuotes) { sb.append('"'); } return sb.toString(); } /** * Convenience method for converting given byte array as base64 encoded String * using this variant's settings, optionally enclosed in double-quotes. * Linefeed character to use is passed explicitly. * * @param input Byte array to encode * @param addQuotes Whether to surround resulting value in double quotes or not * * @since 2.10 */ public String encode(byte[] input, boolean addQuotes, String linefeed) { final int inputEnd = input.length; final StringBuilder sb = new StringBuilder(inputEnd + (inputEnd >> 2) + (inputEnd >> 3)); if (addQuotes) { sb.append('"'); } int chunksBeforeLF = getMaxLineLength() >> 2; int inputPtr = 0; int safeInputEnd = inputEnd-3; while (inputPtr <= safeInputEnd) { int b24 = ((int) input[inputPtr++]) << 8; b24 |= ((int) input[inputPtr++]) & 0xFF; b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF); encodeBase64Chunk(sb, b24); if (--chunksBeforeLF <= 0) { sb.append(linefeed); chunksBeforeLF = getMaxLineLength() >> 2; } } int inputLeft = inputEnd - inputPtr; if (inputLeft > 0) { int b24 = ((int) input[inputPtr++]) << 16; if (inputLeft == 2) { b24 |= (((int) input[inputPtr++]) & 0xFF) << 8; } encodeBase64Partial(sb, b24, inputLeft); } if (addQuotes) { sb.append('"'); } return sb.toString(); } /** * Convenience method for decoding contents of a Base64-encoded String, * using this variant's settings. * * @param input * * @since 2.3 * * @throws IllegalArgumentException if input is not valid base64 encoded data */ @SuppressWarnings("resource") public byte[] decode(String input) throws IllegalArgumentException { ByteArrayBuilder b = new ByteArrayBuilder(); decode(input, b); return b.toByteArray(); } /** * Convenience method for decoding contents of a Base64-encoded String, * using this variant's settings * and appending decoded binary data using provided {@link ByteArrayBuilder}. *
* NOTE: builder will NOT be reset before decoding (nor cleared afterwards); * assumption is that caller will ensure it is given in proper state, and * used as appropriate afterwards. * * @since 2.3 * * @throws IllegalArgumentException if input is not valid base64 encoded data */ public void decode(String str, ByteArrayBuilder builder) throws IllegalArgumentException { int ptr = 0; int len = str.length(); main_loop: while (true) { // first, we'll skip preceding white space, if any char ch; do { if (ptr >= len) { break main_loop; } ch = str.charAt(ptr++); } while (ch <= INT_SPACE); int bits = decodeBase64Char(ch); if (bits < 0) { _reportInvalidBase64(ch, 0, null); } int decodedData = bits; // then second base64 char; can't get padding yet, nor ws if (ptr >= len) { _reportBase64EOF(); } ch = str.charAt(ptr++); bits = decodeBase64Char(ch); if (bits < 0) { _reportInvalidBase64(ch, 1, null); } decodedData = (decodedData << 6) | bits; // third base64 char; can be padding, but not ws if (ptr >= len) { // but as per [JACKSON-631] can be end-of-input, iff not using padding if (!usesPadding()) { decodedData >>= 4; builder.append(decodedData); break; } _reportBase64EOF(); } ch = str.charAt(ptr++); bits = decodeBase64Char(ch); // First branch: can get padding (-> 1 byte) if (bits < 0) { if (bits != Base64Variant.BASE64_VALUE_PADDING) { _reportInvalidBase64(ch, 2, null); } // Ok, must get padding if (ptr >= len) { _reportBase64EOF(); } ch = str.charAt(ptr++); if (!usesPaddingChar(ch)) { _reportInvalidBase64(ch, 3, "expected padding character '"+getPaddingChar()+"'"); } // Got 12 bits, only need 8, need to shift decodedData >>= 4; builder.append(decodedData); continue; } // Nope, 2 or 3 bytes decodedData = (decodedData << 6) | bits; // fourth and last base64 char; can be padding, but not ws if (ptr >= len) { // but as per [JACKSON-631] can be end-of-input, iff not using padding if (!usesPadding()) { decodedData >>= 2; builder.appendTwoBytes(decodedData); break; } _reportBase64EOF(); } ch = str.charAt(ptr++); bits = decodeBase64Char(ch); if (bits < 0) { if (bits != Base64Variant.BASE64_VALUE_PADDING) { _reportInvalidBase64(ch, 3, null); } decodedData >>= 2; builder.appendTwoBytes(decodedData); } else { // otherwise, our triple is now complete decodedData = (decodedData << 6) | bits; builder.appendThreeBytes(decodedData); } } } /* /********************************************************** /* Overridden standard methods /********************************************************** */ @Override public String toString() { return _name; } @Override public boolean equals(Object o) { // identity comparison should be dine return (o == this); } @Override public int hashCode() { return _name.hashCode(); } /* /********************************************************** /* Internal helper methods /********************************************************** */ /** * @param bindex Relative index within base64 character unit; between 0 * and 3 (as unit has exactly 4 characters) */ protected void _reportInvalidBase64(char ch, int bindex, String msg) throws IllegalArgumentException { String base; if (ch <= INT_SPACE) { base = "Illegal white space character (code 0x"+Integer.toHexString(ch)+") as character #"+(bindex+1)+" of 4-char base64 unit: can only used between units"; } else if (usesPaddingChar(ch)) { base = "Unexpected padding character ('"+getPaddingChar()+"') as character #"+(bindex+1)+" of 4-char base64 unit: padding only legal as 3rd or 4th character"; } else if (!Character.isDefined(ch) || Character.isISOControl(ch)) { // Not sure if we can really get here... ? (most illegal xml chars are caught at lower level) base = "Illegal character (code 0x"+Integer.toHexString(ch)+") in base64 content"; } else { base = "Illegal character '"+ch+"' (code 0x"+Integer.toHexString(ch)+") in base64 content"; } if (msg != null) { base = base + ": " + msg; } throw new IllegalArgumentException(base); } protected void _reportBase64EOF() throws IllegalArgumentException { throw new IllegalArgumentException(missingPaddingMessage()); } /** * Helper method that will construct a message to use in exceptions for cases where input ends * prematurely in place where padding would be expected. * * @since 2.10 */ public String missingPaddingMessage() { return String.format("Unexpected end of base64-encoded String: base64 variant '%s' expects padding (one or more '%c' characters) at the end", getName(), getPaddingChar()); } } jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/Base64Variants.java 0000664 0000000 0000000 00000007750 13561642473 0031747 0 ustar 00root root 0000000 0000000 /* Jackson JSON-processor. * * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi */ package com.fasterxml.jackson.core; /** * Container for commonly used Base64 variants: *
* See wikipedia Base64 entry for details. *
* Note that although this can be thought of as the standard variant,
* it is not the default for Jackson: no-linefeeds alternative
* is because of JSON requirement of escaping all linefeeds.
*/
public final static Base64Variant MIME;
static {
MIME = new Base64Variant("MIME", STD_BASE64_ALPHABET, true, '=', 76);
}
/**
* Slightly non-standard modification of {@link #MIME} which does not
* use linefeeds (max line length set to infinite). Useful when linefeeds
* wouldn't work well (possibly in attributes), or for minor space savings
* (save 1 linefeed per 76 data chars, ie. ~1.4% savings).
*/
public final static Base64Variant MIME_NO_LINEFEEDS;
static {
MIME_NO_LINEFEEDS = new Base64Variant(MIME, "MIME-NO-LINEFEEDS", Integer.MAX_VALUE);
}
/**
* This variant is the one that predates {@link #MIME}: it is otherwise
* identical, except that it mandates shorter line length.
*/
public final static Base64Variant PEM = new Base64Variant(MIME, "PEM", true, '=', 64);
/**
* This non-standard variant is usually used when encoded data needs to be
* passed via URLs (such as part of GET request). It differs from the
* base {@link #MIME} variant in multiple ways.
* First, no padding is used: this also means that it generally can not
* be written in multiple separate but adjacent chunks (which would not
* be the usual use case in any case). Also, no linefeeds are used (max
* line length set to infinite). And finally, two characters (plus and
* slash) that would need quoting in URLs are replaced with more
* optimal alternatives (hyphen and underscore, respectively).
*/
public final static Base64Variant MODIFIED_FOR_URL;
static {
StringBuilder sb = new StringBuilder(STD_BASE64_ALPHABET);
// Replace plus with hyphen, slash with underscore (and no padding)
sb.setCharAt(sb.indexOf("+"), '-');
sb.setCharAt(sb.indexOf("/"), '_');
// And finally, let's not split lines either, wouldn't work too well with URLs
MODIFIED_FOR_URL = new Base64Variant("MODIFIED-FOR-URL", sb.toString(), false, Base64Variant.PADDING_CHAR_NONE, Integer.MAX_VALUE);
}
/**
* Method used to get the default variant ("MIME_NO_LINEFEEDS") for cases
* where caller does not explicitly specify the variant.
* We will prefer no-linefeed version because linefeeds in JSON values
* must be escaped, making linefeed-containing variants sub-optimal.
*/
public static Base64Variant getDefaultVariant() {
return MIME_NO_LINEFEEDS;
}
/**
* @since 2.1
*/
public static Base64Variant valueOf(String name) throws IllegalArgumentException
{
if (MIME._name.equals(name)) {
return MIME;
}
if (MIME_NO_LINEFEEDS._name.equals(name)) {
return MIME_NO_LINEFEEDS;
}
if (PEM._name.equals(name)) {
return PEM;
}
if (MODIFIED_FOR_URL._name.equals(name)) {
return MODIFIED_FOR_URL;
}
if (name == null) {
name = "
* Note that this type is only implemented by non-JSON formats:
* types {@link JsonParser.Feature} and {@link JsonGenerator.Feature} do NOT
* implement it. This is to make it easier to avoid ambiguity with method
* calls.
*
* @since 2.6 (to be fully used in 2.7 and beyond)
*/
public interface FormatFeature
{
/**
* Accessor for checking whether this feature is enabled by default.
*/
public boolean enabledByDefault();
/**
* Returns bit mask for this feature instance; must be a single bit,
* that is of form
* Since there is little commonality between schemas for different data formats,
* this interface does not define much meaningful functionality for accessing
* schema details; rather, specific parser and generator implementations need
* to cast to schema implementations they use. This marker interface is mostly
* used for tagging "some kind of schema" -- instead of passing opaque
* {@link java.lang.Object} -- for documentation purposes.
*/
public interface FormatSchema
{
/**
* Method that can be used to get an identifier that can be used for diagnostics
* purposes, to indicate what kind of data format this schema is used for: typically
* it is a short name of format itself, but it can also contain additional information
* in cases where data format supports multiple types of schemas.
*/
String getSchemaType();
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonEncoding.java 0000664 0000000 0000000 00000003262 13561642473 0031565 0 ustar 00root root 0000000 0000000 /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*/
package com.fasterxml.jackson.core;
/**
* Enumeration that defines legal encodings that can be used
* for JSON content, based on list of allowed encodings from
* JSON specification.
*
* Note: if application want to explicitly disregard Encoding
* limitations (to read in JSON encoded using an encoding not
* listed as allowed), they can use {@link java.io.Reader} /
* {@link java.io.Writer} instances as input
*/
public enum JsonEncoding {
UTF8("UTF-8", false, 8), // N/A for big-endian, really
UTF16_BE("UTF-16BE", true, 16),
UTF16_LE("UTF-16LE", false, 16),
UTF32_BE("UTF-32BE", true, 32),
UTF32_LE("UTF-32LE", false, 32)
;
private final String _javaName;
private final boolean _bigEndian;
private final int _bits;
JsonEncoding(String javaName, boolean bigEndian, int bits)
{
_javaName = javaName;
_bigEndian = bigEndian;
_bits = bits;
}
/**
* Method for accessing encoding name that JDK will support.
*
* @return Matching encoding name that JDK will support.
*/
public String getJavaName() { return _javaName; }
/**
* Whether encoding is big-endian (if encoding supports such
* notion). If no such distinction is made (as is the case for
* {@link #UTF8}), return value is undefined.
*
* @return True for big-endian encodings; false for little-endian
* (or if not applicable)
*/
public boolean isBigEndian() { return _bigEndian; }
public int bits() { return _bits; }
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonFactory.java 0000664 0000000 0000000 00000173747 13561642473 0031466 0 ustar 00root root 0000000 0000000 /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*/
package com.fasterxml.jackson.core;
import java.io.*;
import java.lang.ref.SoftReference;
import java.net.URL;
import com.fasterxml.jackson.core.format.InputAccessor;
import com.fasterxml.jackson.core.format.MatchStrength;
import com.fasterxml.jackson.core.io.*;
import com.fasterxml.jackson.core.json.*;
import com.fasterxml.jackson.core.json.async.NonBlockingJsonParser;
import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
import com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer;
import com.fasterxml.jackson.core.util.BufferRecycler;
import com.fasterxml.jackson.core.util.BufferRecyclers;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
/**
* The main factory class of Jackson package, used to configure and
* construct reader (aka parser, {@link JsonParser})
* and writer (aka generator, {@link JsonGenerator})
* instances.
*
* Factory instances are thread-safe and reusable after configuration
* (if any). Typically applications and services use only a single
* globally shared factory instance, unless they need differently
* configured factories. Factory reuse is important if efficiency matters;
* most recycling of expensive construct is done on per-factory basis.
*
* Creation of a factory instance is a light-weight operation,
* and since there is no need for pluggable alternative implementations
* (as there is no "standard" JSON processor API to implement),
* the default constructor is used for constructing factory
* instances.
*
* @author Tatu Saloranta
*/
@SuppressWarnings("resource")
public class JsonFactory
extends TokenStreamFactory
implements Versioned,
java.io.Serializable // since 2.1 (for Android, mostly)
{
private static final long serialVersionUID = 2;
/*
/**********************************************************
/* Helper types
/**********************************************************
*/
/**
* Enumeration that defines all on/off features that can only be
* changed for {@link JsonFactory}.
*/
public enum Feature {
// // // Symbol handling (interning etc)
/**
* Feature that determines whether JSON object field names are
* to be canonicalized using {@link String#intern} or not:
* if enabled, all field names will be intern()ed (and caller
* can count on this being true for all such names); if disabled,
* no intern()ing is done. There may still be basic
* canonicalization (that is, same String will be used to represent
* all identical object property names for a single document).
*
* Note: this setting only has effect if
* {@link #CANONICALIZE_FIELD_NAMES} is true -- otherwise no
* canonicalization of any sort is done.
*
* This setting is enabled by default.
*/
INTERN_FIELD_NAMES(true),
/**
* Feature that determines whether JSON object field names are
* to be canonicalized (details of how canonicalization is done
* then further specified by
* {@link #INTERN_FIELD_NAMES}).
*
* This setting is enabled by default.
*/
CANONICALIZE_FIELD_NAMES(true),
/**
* Feature that determines what happens if we encounter a case in symbol
* handling where number of hash collisions exceeds a safety threshold
* -- which almost certainly means a denial-of-service attack via generated
* duplicate hash codes.
* If feature is enabled, an {@link IllegalStateException} is
* thrown to indicate the suspected denial-of-service attack; if disabled, processing continues but
* canonicalization (and thereby
* This setting is enabled by default.
*
* @since 2.4
*/
FAIL_ON_SYMBOL_HASH_OVERFLOW(true),
/**
* Feature that determines whether we will use {@link BufferRecycler} with
* {@link ThreadLocal} and {@link SoftReference}, for efficient reuse of
* underlying input/output buffers.
* This usually makes sense on normal J2SE/J2EE server-side processing;
* but may not make sense on platforms where {@link SoftReference} handling
* is broken (like Android), or if there are retention issues due to
* {@link ThreadLocal} (see
* Issue #189
* for a possible case)
*
* This setting is enabled by default.
*
* @since 2.6
*/
USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING(true)
;
/**
* Whether feature is enabled or disabled by default.
*/
private final boolean _defaultState;
/**
* Method that calculates bit set (flags) of all features that
* are enabled by default.
*/
public static int collectDefaults() {
int flags = 0;
for (Feature f : values()) {
if (f.enabledByDefault()) { flags |= f.getMask(); }
}
return flags;
}
private Feature(boolean defaultState) { _defaultState = defaultState; }
public boolean enabledByDefault() { return _defaultState; }
public boolean enabledIn(int flags) { return (flags & getMask()) != 0; }
public int getMask() { return (1 << ordinal()); }
}
/*
/**********************************************************
/* Constants
/**********************************************************
*/
/**
* Name used to identify JSON format
* (and returned by {@link #getFormatName()}
*/
public final static String FORMAT_NAME_JSON = "JSON";
/**
* Bitfield (set of flags) of all factory features that are enabled by default.
*/
protected final static int DEFAULT_FACTORY_FEATURE_FLAGS = JsonFactory.Feature.collectDefaults();
/**
* Bitfield (set of flags) of all parser features that are enabled
* by default.
*/
protected final static int DEFAULT_PARSER_FEATURE_FLAGS = JsonParser.Feature.collectDefaults();
/**
* Bitfield (set of flags) of all generator features that are enabled
* by default.
*/
protected final static int DEFAULT_GENERATOR_FEATURE_FLAGS = JsonGenerator.Feature.collectDefaults();
public final static SerializableString DEFAULT_ROOT_VALUE_SEPARATOR = DefaultPrettyPrinter.DEFAULT_ROOT_VALUE_SEPARATOR;
/**
* @since 2.10
*/
public final static char DEFAULT_QUOTE_CHAR = '"';
/*
/**********************************************************
/* Buffer, symbol table management
/**********************************************************
*/
/**
* Each factory comes equipped with a shared root symbol table.
* It should not be linked back to the original blueprint, to
* avoid contents from leaking between factories.
*/
protected final transient CharsToNameCanonicalizer _rootCharSymbols = CharsToNameCanonicalizer.createRoot();
/**
* Alternative to the basic symbol table, some stream-based
* parsers use different name canonicalization method.
*
* TODO: should clean up this; looks messy having 2 alternatives
* with not very clear differences.
*
* @since 2.6
*/
protected final transient ByteQuadsCanonicalizer _byteSymbolCanonicalizer = ByteQuadsCanonicalizer.createRoot();
/*
/**********************************************************
/* Configuration, simple feature flags
/**********************************************************
*/
/**
* Currently enabled factory features.
*/
protected int _factoryFeatures = DEFAULT_FACTORY_FEATURE_FLAGS;
/**
* Currently enabled parser features.
*/
protected int _parserFeatures = DEFAULT_PARSER_FEATURE_FLAGS;
/**
* Currently enabled generator features.
*/
protected int _generatorFeatures = DEFAULT_GENERATOR_FEATURE_FLAGS;
/*
/**********************************************************
/* Configuration, helper objects
/**********************************************************
*/
/**
* Object that implements conversion functionality between
* Java objects and JSON content. For base JsonFactory implementation
* usually not set by default, but can be explicitly set.
* Sub-classes (like @link org.codehaus.jackson.map.MappingJsonFactory}
* usually provide an implementation.
*/
protected ObjectCodec _objectCodec;
/**
* Definition of custom character escapes to use for generators created
* by this factory, if any. If null, standard data format specific
* escapes are used.
*/
protected CharacterEscapes _characterEscapes;
/**
* Optional helper object that may decorate input sources, to do
* additional processing on input during parsing.
*/
protected InputDecorator _inputDecorator;
/**
* Optional helper object that may decorate output object, to do
* additional processing on output during content generation.
*/
protected OutputDecorator _outputDecorator;
/**
* Separator used between root-level values, if any; null indicates
* "do not add separator".
* Default separator is a single space character.
*
* @since 2.1
*/
protected SerializableString _rootValueSeparator = DEFAULT_ROOT_VALUE_SEPARATOR;
/**
* Optional threshold used for automatically escaping character above certain character
* code value: either {@code 0} to indicate that no threshold is specified, or value
* at or above 127 to indicate last character code that is NOT automatically escaped
* (but depends on other configuration rules for checking).
*
* @since 2.10
*/
protected int _maximumNonEscapedChar;
/**
* Character used for quoting field names (if field name quoting has not
* been disabled with {@link JsonWriteFeature#QUOTE_FIELD_NAMES})
* and JSON String values.
*/
protected final char _quoteChar;
/*
/**********************************************************
/* Construction
/**********************************************************
*/
/**
* Default constructor used to create factory instances.
* Creation of a factory instance is a light-weight operation,
* but it is still a good idea to reuse limited number of
* factory instances (and quite often just a single instance):
* factories are used as context for storing some reused
* processing objects (such as symbol tables parsers use)
* and this reuse only works within context of a single
* factory instance.
*/
public JsonFactory() { this((ObjectCodec) null); }
public JsonFactory(ObjectCodec oc) {
_objectCodec = oc;
_quoteChar = DEFAULT_QUOTE_CHAR;
}
/**
* Constructor used when copy()ing a factory instance.
*
* @since 2.2.1
*/
protected JsonFactory(JsonFactory src, ObjectCodec codec)
{
_objectCodec = codec;
// General
_factoryFeatures = src._factoryFeatures;
_parserFeatures = src._parserFeatures;
_generatorFeatures = src._generatorFeatures;
_inputDecorator = src._inputDecorator;
_outputDecorator = src._outputDecorator;
// JSON-specific
_characterEscapes = src._characterEscapes;
_rootValueSeparator = src._rootValueSeparator;
_maximumNonEscapedChar = src._maximumNonEscapedChar;
_quoteChar = src._quoteChar;
}
/**
* Constructor used by {@link JsonFactoryBuilder} for instantiation.
*
* @since 2.10
*/
public JsonFactory(JsonFactoryBuilder b) {
_objectCodec = null;
// General
_factoryFeatures = b._factoryFeatures;
_parserFeatures = b._streamReadFeatures;
_generatorFeatures = b._streamWriteFeatures;
_inputDecorator = b._inputDecorator;
_outputDecorator = b._outputDecorator;
// JSON-specific
_characterEscapes = b._characterEscapes;
_rootValueSeparator = b._rootValueSeparator;
_maximumNonEscapedChar = b._maximumNonEscapedChar;
_quoteChar = b._quoteChar;
}
/**
* Constructor for subtypes; needed to work around the fact that before 3.0,
* this factory has cumbersome dual role as generic type as well as actual
* implementation for json.
*
* @param b Builder that contains information
* @param bogus Argument only needed to separate constructor signature; ignored
*/
protected JsonFactory(TSFBuilder,?> b, boolean bogus) {
_objectCodec = null;
_factoryFeatures = b._factoryFeatures;
_parserFeatures = b._streamReadFeatures;
_generatorFeatures = b._streamWriteFeatures;
_inputDecorator = b._inputDecorator;
_outputDecorator = b._outputDecorator;
// JSON-specific: need to assign even if not really used
_characterEscapes = null;
_rootValueSeparator = null;
_maximumNonEscapedChar = 0;
_quoteChar = DEFAULT_QUOTE_CHAR;
}
/**
* Method that allows construction of differently configured factory, starting
* with settings of this factory.
*
* @since 2.10
*/
public TSFBuilder,?> rebuild() {
// 13-Jun-2018, tatu: Verify sub-classing to prevent strange bugs in format impls
_requireJSONFactory("Factory implementation for format (%s) MUST override `rebuild()` method");
return new JsonFactoryBuilder(this);
}
/**
* Main factory method to use for constructing {@link JsonFactory} instances with
* different configuration: creates and returns a builder for collecting configuration
* settings; instance created by calling {@code build()} after all configuration
* set.
*
* NOTE: signature unfortunately does not expose true implementation type; this
* will be fixed in 3.0.
*/
public static TSFBuilder,?> builder() {
return new JsonFactoryBuilder();
}
/**
* Method for constructing a new {@link JsonFactory} that has
* the same settings as this instance, but is otherwise
* independent (i.e. nothing is actually shared, symbol tables
* are separate).
* Note that {@link ObjectCodec} reference is not copied but is
* set to null; caller typically needs to set it after calling
* this method. Reason for this is that the codec is used for
* callbacks, and assumption is that there is strict 1-to-1
* mapping between codec, factory. Caller has to, then, explicitly
* set codec after making the copy.
*
* @since 2.1
*/
public JsonFactory copy()
{
_checkInvalidCopy(JsonFactory.class);
// as per above, do clear ObjectCodec
return new JsonFactory(this, null);
}
/**
* @since 2.1
*/
protected void _checkInvalidCopy(Class> exp)
{
if (getClass() != exp) {
throw new IllegalStateException("Failed copy(): "+getClass().getName()
+" (version: "+version()+") does not override copy(); it has to");
}
}
/*
/**********************************************************
/* Serializable overrides
/**********************************************************
*/
/**
* Method that we need to override to actually make restoration go
* through constructors etc.
* Also: must be overridden by sub-classes as well.
*/
protected Object readResolve() {
return new JsonFactory(this, _objectCodec);
}
/*
/**********************************************************
/* Capability introspection
/**********************************************************
*/
/**
* Introspection method that higher-level functionality may call
* to see whether underlying data format requires a stable ordering
* of object properties or not.
* This is usually used for determining
* whether to force a stable ordering (like alphabetic ordering by name)
* if no ordering if explicitly specified.
*
* Default implementation returns
* Default implementation returns
* Note: sub-classes should override this method; default
* implementation will return null for all sub-classes
*/
@Override
public String getFormatName()
{
/* Somewhat nasty check: since we can't make this abstract
* (due to backwards compatibility concerns), need to prevent
* format name "leakage"
*/
if (getClass() == JsonFactory.class) {
return FORMAT_NAME_JSON;
}
return null;
}
/**
* Convenience method for trying to determine whether input via given accessor
* is of format type supported by this factory.
*/
public MatchStrength hasFormat(InputAccessor acc) throws IOException
{
// since we can't keep this abstract, only implement for "vanilla" instance
if (getClass() == JsonFactory.class) {
return hasJSONFormat(acc);
}
return null;
}
/**
* Method that can be called to determine if a custom
* {@link ObjectCodec} is needed for binding data parsed
* using {@link JsonParser} constructed by this factory
* (which typically also implies the same for serialization
* with {@link JsonGenerator}).
*
* @return True if custom codec is needed with parsers and
* generators created by this factory; false if a general
* {@link ObjectCodec} is enough
*
* @since 2.1
*/
public boolean requiresCustomCodec() {
return false;
}
/**
* Helper method that can be called to determine if content accessed
* using given accessor seems to be JSON content.
*/
protected MatchStrength hasJSONFormat(InputAccessor acc) throws IOException
{
return ByteSourceJsonBootstrapper.hasJSONFormat(acc);
}
/*
/**********************************************************
/* Versioned
/**********************************************************
*/
@Override
public Version version() {
return PackageVersion.VERSION;
}
/*
/**********************************************************
/* Configuration, factory features
/**********************************************************
*/
/**
* Method for enabling or disabling specified parser feature
* (check {@link JsonParser.Feature} for list of features)
*
* @deprecated since 2.10 use {@link JsonFactoryBuilder#configure(JsonFactory.Feature, boolean)} instead
*/
@Deprecated
public final JsonFactory configure(JsonFactory.Feature f, boolean state) {
return state ? enable(f) : disable(f);
}
/**
* Method for enabling specified parser feature
* (check {@link JsonFactory.Feature} for list of features)
*
* @deprecated since 2.10 use {@link JsonFactoryBuilder#configure(JsonFactory.Feature, boolean)} instead
*/
@Deprecated
public JsonFactory enable(JsonFactory.Feature f) {
_factoryFeatures |= f.getMask();
return this;
}
/**
* Method for disabling specified parser features
* (check {@link JsonFactory.Feature} for list of features)
*
* @deprecated since 2.10 use {@link JsonFactoryBuilder#configure(JsonFactory.Feature, boolean)} instead
*/
@Deprecated
public JsonFactory disable(JsonFactory.Feature f) {
_factoryFeatures &= ~f.getMask();
return this;
}
/**
* Checked whether specified parser feature is enabled.
*/
public final boolean isEnabled(JsonFactory.Feature f) {
return (_factoryFeatures & f.getMask()) != 0;
}
@Override
public final int getParserFeatures() {
return _parserFeatures;
}
@Override
public final int getGeneratorFeatures() {
return _generatorFeatures;
}
// MUST be overridden by sub-classes that support format-specific parser features
@Override
public int getFormatParserFeatures() {
return 0;
}
// MUST be overridden by sub-classes that support format-specific generator features
@Override
public int getFormatGeneratorFeatures() {
return 0;
}
/*
/**********************************************************
/* Configuration, parser configuration
/**********************************************************
*/
/**
* Method for enabling or disabling specified parser feature
* (check {@link JsonParser.Feature} for list of features)
*/
public final JsonFactory configure(JsonParser.Feature f, boolean state) {
return state ? enable(f) : disable(f);
}
/**
* Method for enabling specified parser feature
* (check {@link JsonParser.Feature} for list of features)
*/
public JsonFactory enable(JsonParser.Feature f) {
_parserFeatures |= f.getMask();
return this;
}
/**
* Method for disabling specified parser features
* (check {@link JsonParser.Feature} for list of features)
*/
public JsonFactory disable(JsonParser.Feature f) {
_parserFeatures &= ~f.getMask();
return this;
}
/**
* Checked whether specified parser feature is enabled.
*/
@Override
public final boolean isEnabled(JsonParser.Feature f) {
return (_parserFeatures & f.getMask()) != 0;
}
/**
* @since 2.10
*/
public final boolean isEnabled(StreamReadFeature f) {
return (_parserFeatures & f.mappedFeature().getMask()) != 0;
}
/**
* Method for getting currently configured input decorator (if any;
* there is no default decorator).
*/
public InputDecorator getInputDecorator() {
return _inputDecorator;
}
/**
* Method for overriding currently configured input decorator
*
* @deprecated Since 2.10 use {@link JsonFactoryBuilder#inputDecorator(InputDecorator)} instead
*/
@Deprecated
public JsonFactory setInputDecorator(InputDecorator d) {
_inputDecorator = d;
return this;
}
/*
/**********************************************************
/* Configuration, generator settings
/**********************************************************
*/
/**
* Method for enabling or disabling specified generator feature
* (check {@link JsonGenerator.Feature} for list of features)
*/
public final JsonFactory configure(JsonGenerator.Feature f, boolean state) {
return state ? enable(f) : disable(f);
}
/**
* Method for enabling specified generator features
* (check {@link JsonGenerator.Feature} for list of features)
*/
public JsonFactory enable(JsonGenerator.Feature f) {
_generatorFeatures |= f.getMask();
return this;
}
/**
* Method for disabling specified generator feature
* (check {@link JsonGenerator.Feature} for list of features)
*/
public JsonFactory disable(JsonGenerator.Feature f) {
_generatorFeatures &= ~f.getMask();
return this;
}
/**
* Check whether specified generator feature is enabled.
*/
@Override
public final boolean isEnabled(JsonGenerator.Feature f) {
return (_generatorFeatures & f.getMask()) != 0;
}
/**
* @since 2.10
*/
public final boolean isEnabled(StreamWriteFeature f) {
return (_generatorFeatures & f.mappedFeature().getMask()) != 0;
}
/**
* Method for accessing custom escapes factory uses for {@link JsonGenerator}s
* it creates.
*/
public CharacterEscapes getCharacterEscapes() { return _characterEscapes; }
/**
* Method for defining custom escapes factory uses for {@link JsonGenerator}s
* it creates.
*/
public JsonFactory setCharacterEscapes(CharacterEscapes esc) {
_characterEscapes = esc;
return this;
}
/**
* Method for getting currently configured output decorator (if any;
* there is no default decorator).
*/
public OutputDecorator getOutputDecorator() {
return _outputDecorator;
}
/**
* Method for overriding currently configured output decorator
*
* @deprecated Since 2.10 use {@link JsonFactoryBuilder#inputDecorator(InputDecorator)} instead
*/
@Deprecated
public JsonFactory setOutputDecorator(OutputDecorator d) {
_outputDecorator = d;
return this;
}
/**
* Method that allows overriding String used for separating root-level
* JSON values (default is single space character)
*
* @param sep Separator to use, if any; null means that no separator is
* automatically added
*
* @since 2.1
*/
public JsonFactory setRootValueSeparator(String sep) {
_rootValueSeparator = (sep == null) ? null : new SerializedString(sep);
return this;
}
/**
* @since 2.1
*/
public String getRootValueSeparator() {
return (_rootValueSeparator == null) ? null : _rootValueSeparator.getValue();
}
/*
/**********************************************************
/* Configuration, other
/**********************************************************
*/
/**
* Method for associating a {@link ObjectCodec} (typically
* a
* Encoding is auto-detected from contents according to JSON
* specification recommended mechanism. Json specification
* supports only UTF-8, UTF-16 and UTF-32 as valid encodings,
* so auto-detection implemented only for this charsets.
* For other charsets use {@link #createParser(java.io.Reader)}.
*
*
* Underlying input stream (needed for reading contents)
* will be owned (and managed, i.e. closed as need be) by
* the parser, since caller has no access to it.
*
* @param f File that contains JSON content to parse
*
* @since 2.1
*/
@Override
public JsonParser createParser(File f) throws IOException, JsonParseException {
// true, since we create InputStream from File
IOContext ctxt = _createContext(f, true);
InputStream in = new FileInputStream(f);
return _createParser(_decorate(in, ctxt), ctxt);
}
/**
* Method for constructing JSON parser instance to parse
* contents of resource reference by given URL.
*
*
* Encoding is auto-detected from contents according to JSON
* specification recommended mechanism. Json specification
* supports only UTF-8, UTF-16 and UTF-32 as valid encodings,
* so auto-detection implemented only for this charsets.
* For other charsets use {@link #createParser(java.io.Reader)}.
*
*
* Underlying input stream (needed for reading contents)
* will be owned (and managed, i.e. closed as need be) by
* the parser, since caller has no access to it.
*
* @param url URL pointing to resource that contains JSON content to parse
*
* @since 2.1
*/
@Override
public JsonParser createParser(URL url) throws IOException, JsonParseException {
// true, since we create InputStream from URL
IOContext ctxt = _createContext(url, true);
InputStream in = _optimizedStreamFromURL(url);
return _createParser(_decorate(in, ctxt), ctxt);
}
/**
* Method for constructing JSON parser instance to parse
* the contents accessed via specified input stream.
*
* The input stream will not be owned by
* the parser, it will still be managed (i.e. closed if
* end-of-stream is reacher, or parser close method called)
* if (and only if) {@link com.fasterxml.jackson.core.StreamReadFeature#AUTO_CLOSE_SOURCE}
* is enabled.
*
*
* Note: no encoding argument is taken since it can always be
* auto-detected as suggested by JSON RFC. Json specification
* supports only UTF-8, UTF-16 and UTF-32 as valid encodings,
* so auto-detection implemented only for this charsets.
* For other charsets use {@link #createParser(java.io.Reader)}.
*
* @param in InputStream to use for reading JSON content to parse
*
* @since 2.1
*/
@Override
public JsonParser createParser(InputStream in) throws IOException, JsonParseException {
IOContext ctxt = _createContext(in, false);
return _createParser(_decorate(in, ctxt), ctxt);
}
/**
* Method for constructing parser for parsing
* the contents accessed via specified Reader.
* The read stream will not be owned by
* the parser, it will still be managed (i.e. closed if
* end-of-stream is reacher, or parser close method called)
* if (and only if) {@link com.fasterxml.jackson.core.StreamReadFeature#AUTO_CLOSE_SOURCE}
* is enabled.
*
* @param r Reader to use for reading JSON content to parse
*
* @since 2.1
*/
@Override
public JsonParser createParser(Reader r) throws IOException, JsonParseException {
// false -> we do NOT own Reader (did not create it)
IOContext ctxt = _createContext(r, false);
return _createParser(_decorate(r, ctxt), ctxt);
}
/**
* Method for constructing parser for parsing
* the contents of given byte array.
*
* @since 2.1
*/
@Override
public JsonParser createParser(byte[] data) throws IOException, JsonParseException {
IOContext ctxt = _createContext(data, true);
if (_inputDecorator != null) {
InputStream in = _inputDecorator.decorate(ctxt, data, 0, data.length);
if (in != null) {
return _createParser(in, ctxt);
}
}
return _createParser(data, 0, data.length, ctxt);
}
/**
* Method for constructing parser for parsing
* the contents of given byte array.
*
* @param data Buffer that contains data to parse
* @param offset Offset of the first data byte within buffer
* @param len Length of contents to parse within buffer
*
* @since 2.1
*/
@Override
public JsonParser createParser(byte[] data, int offset, int len) throws IOException, JsonParseException {
IOContext ctxt = _createContext(data, true);
// [JACKSON-512]: allow wrapping with InputDecorator
if (_inputDecorator != null) {
InputStream in = _inputDecorator.decorate(ctxt, data, offset, len);
if (in != null) {
return _createParser(in, ctxt);
}
}
return _createParser(data, offset, len, ctxt);
}
/**
* Method for constructing parser for parsing
* contents of given String.
*
* @since 2.1
*/
@Override
public JsonParser createParser(String content) throws IOException, JsonParseException {
final int strLen = content.length();
// Actually, let's use this for medium-sized content, up to 64kB chunk (32kb char)
if ((_inputDecorator != null) || (strLen > 0x8000) || !canUseCharArrays()) {
// easier to just wrap in a Reader than extend InputDecorator; or, if content
// is too long for us to copy it over
return createParser(new StringReader(content));
}
IOContext ctxt = _createContext(content, true);
char[] buf = ctxt.allocTokenBuffer(strLen);
content.getChars(0, strLen, buf, 0);
return _createParser(buf, 0, strLen, ctxt, true);
}
/**
* Method for constructing parser for parsing
* contents of given char array.
*
* @since 2.4
*/
@Override
public JsonParser createParser(char[] content) throws IOException {
return createParser(content, 0, content.length);
}
/**
* Method for constructing parser for parsing contents of given char array.
*
* @since 2.4
*/
@Override
public JsonParser createParser(char[] content, int offset, int len) throws IOException {
if (_inputDecorator != null) { // easier to just wrap in a Reader than extend InputDecorator
return createParser(new CharArrayReader(content, offset, len));
}
return _createParser(content, offset, len, _createContext(content, true),
// important: buffer is NOT recyclable, as it's from caller
false);
}
/**
* Optional method for constructing parser for reading contents from specified {@link DataInput}
* instance.
*
* If this factory does not support {@link DataInput} as source,
* will throw {@link UnsupportedOperationException}
*
* @since 2.8
*/
@Override
public JsonParser createParser(DataInput in) throws IOException {
IOContext ctxt = _createContext(in, false);
return _createParser(_decorate(in, ctxt), ctxt);
}
/*
/**********************************************************
/* Parser factories, non-blocking (async) sources
/**********************************************************
*/
/**
* Optional method for constructing parser for non-blocking parsing
* via {@link com.fasterxml.jackson.core.async.ByteArrayFeeder}
* interface (accessed using {@link JsonParser#getNonBlockingInputFeeder()}
* from constructed instance).
*
* If this factory does not support non-blocking parsing (either at all,
* or from byte array),
* will throw {@link UnsupportedOperationException}
*
* @since 2.9
*/
@Override
public JsonParser createNonBlockingByteArrayParser() throws IOException
{
// 17-May-2017, tatu: Need to take care not to accidentally create JSON parser
// for non-JSON input:
_requireJSONFactory("Non-blocking source not (yet?) supported for this format (%s)");
IOContext ctxt = _createNonBlockingContext(null);
ByteQuadsCanonicalizer can = _byteSymbolCanonicalizer.makeChild(_factoryFeatures);
return new NonBlockingJsonParser(ctxt, _parserFeatures, can);
}
/*
/**********************************************************
/* Generator factories
/**********************************************************
*/
/**
* Method for constructing JSON generator for writing JSON content
* using specified output stream.
* Encoding to use must be specified, and needs to be one of available
* types (as per JSON specification).
*
* Underlying stream is NOT owned by the generator constructed,
* so that generator will NOT close the output stream when
* {@link JsonGenerator#close} is called (unless auto-closing
* feature,
* {@link com.fasterxml.jackson.core.JsonGenerator.Feature#AUTO_CLOSE_TARGET}
* is enabled).
* Using application needs to close it explicitly if this is the case.
*
* Note: there are formats that use fixed encoding (like most binary data formats)
* and that ignore passed in encoding.
*
* @param out OutputStream to use for writing JSON content
* @param enc Character encoding to use
*
* @since 2.1
*/
@Override
public JsonGenerator createGenerator(OutputStream out, JsonEncoding enc)
throws IOException
{
// false -> we won't manage the stream unless explicitly directed to
IOContext ctxt = _createContext(out, false);
ctxt.setEncoding(enc);
if (enc == JsonEncoding.UTF8) {
return _createUTF8Generator(_decorate(out, ctxt), ctxt);
}
Writer w = _createWriter(out, enc, ctxt);
return _createGenerator(_decorate(w, ctxt), ctxt);
}
/**
* Convenience method for constructing generator that uses default
* encoding of the format (UTF-8 for JSON and most other data formats).
*
* Note: there are formats that use fixed encoding (like most binary data formats).
*
* @since 2.1
*/
@Override
public JsonGenerator createGenerator(OutputStream out) throws IOException {
return createGenerator(out, JsonEncoding.UTF8);
}
/**
* Method for constructing JSON generator for writing JSON content
* using specified Writer.
*
* Underlying stream is NOT owned by the generator constructed,
* so that generator will NOT close the Reader when
* {@link JsonGenerator#close} is called (unless auto-closing
* feature,
* {@link com.fasterxml.jackson.core.JsonGenerator.Feature#AUTO_CLOSE_TARGET} is enabled).
* Using application needs to close it explicitly.
*
* @since 2.1
*
* @param w Writer to use for writing JSON content
*/
@Override
public JsonGenerator createGenerator(Writer w) throws IOException {
IOContext ctxt = _createContext(w, false);
return _createGenerator(_decorate(w, ctxt), ctxt);
}
/**
* Method for constructing JSON generator for writing JSON content
* to specified file, overwriting contents it might have (or creating
* it if such file does not yet exist).
* Encoding to use must be specified, and needs to be one of available
* types (as per JSON specification).
*
* Underlying stream is owned by the generator constructed,
* i.e. generator will handle closing of file when
* {@link JsonGenerator#close} is called.
*
* @param f File to write contents to
* @param enc Character encoding to use
*
* @since 2.1
*/
@Override
public JsonGenerator createGenerator(File f, JsonEncoding enc) throws IOException
{
OutputStream out = new FileOutputStream(f);
// true -> yes, we have to manage the stream since we created it
IOContext ctxt = _createContext(out, true);
ctxt.setEncoding(enc);
if (enc == JsonEncoding.UTF8) {
return _createUTF8Generator(_decorate(out, ctxt), ctxt);
}
Writer w = _createWriter(out, enc, ctxt);
return _createGenerator(_decorate(w, ctxt), ctxt);
}
/**
* Method for constructing generator for writing content using specified
* {@link DataOutput} instance.
*
* @since 2.8
*/
@Override
public JsonGenerator createGenerator(DataOutput out, JsonEncoding enc) throws IOException {
return createGenerator(_createDataOutputWrapper(out), enc);
}
/**
* Convenience method for constructing generator that uses default
* encoding of the format (UTF-8 for JSON and most other data formats).
*
* Note: there are formats that use fixed encoding (like most binary data formats).
*
* @since 2.8
*/
@Override
public JsonGenerator createGenerator(DataOutput out) throws IOException {
return createGenerator(_createDataOutputWrapper(out), JsonEncoding.UTF8);
}
/*
/**********************************************************
/* Deprecated parser factory methods: to be removed from 3.x
/**********************************************************
*/
/**
* Method for constructing JSON parser instance to parse
* contents of specified file.
*
* Encoding is auto-detected from contents according to JSON
* specification recommended mechanism. Json specification
* supports only UTF-8, UTF-16 and UTF-32 as valid encodings,
* so auto-detection implemented only for this charsets.
* For other charsets use {@link #createParser(java.io.Reader)}.
*
*
* Underlying input stream (needed for reading contents)
* will be owned (and managed, i.e. closed as need be) by
* the parser, since caller has no access to it.
*
* @param f File that contains JSON content to parse
*
* @deprecated Since 2.2, use {@link #createParser(File)} instead.
*/
@Deprecated
public JsonParser createJsonParser(File f) throws IOException, JsonParseException {
return createParser(f);
}
/**
* Method for constructing JSON parser instance to parse
* contents of resource reference by given URL.
*
*
* Encoding is auto-detected from contents according to JSON
* specification recommended mechanism. Json specification
* supports only UTF-8, UTF-16 and UTF-32 as valid encodings,
* so auto-detection implemented only for this charsets.
* For other charsets use {@link #createParser(java.io.Reader)}.
*
*
* Underlying input stream (needed for reading contents)
* will be owned (and managed, i.e. closed as need be) by
* the parser, since caller has no access to it.
*
* @param url URL pointing to resource that contains JSON content to parse
*
* @deprecated Since 2.2, use {@link #createParser(URL)} instead.
*/
@Deprecated
public JsonParser createJsonParser(URL url) throws IOException, JsonParseException {
return createParser(url);
}
/**
* Method for constructing JSON parser instance to parse
* the contents accessed via specified input stream.
*
* The input stream will not be owned by
* the parser, it will still be managed (i.e. closed if
* end-of-stream is reacher, or parser close method called)
* if (and only if) {@link com.fasterxml.jackson.core.JsonParser.Feature#AUTO_CLOSE_SOURCE}
* is enabled.
*
*
* Note: no encoding argument is taken since it can always be
* auto-detected as suggested by JSON RFC. Json specification
* supports only UTF-8, UTF-16 and UTF-32 as valid encodings,
* so auto-detection implemented only for this charsets.
* For other charsets use {@link #createParser(java.io.Reader)}.
*
* @param in InputStream to use for reading JSON content to parse
*
* @deprecated Since 2.2, use {@link #createParser(InputStream)} instead.
*/
@Deprecated
public JsonParser createJsonParser(InputStream in) throws IOException, JsonParseException {
return createParser(in);
}
/**
* Method for constructing parser for parsing
* the contents accessed via specified Reader.
* The read stream will not be owned by
* the parser, it will still be managed (i.e. closed if
* end-of-stream is reacher, or parser close method called)
* if (and only if) {@link com.fasterxml.jackson.core.JsonParser.Feature#AUTO_CLOSE_SOURCE}
* is enabled.
*
* @param r Reader to use for reading JSON content to parse
*
* @deprecated Since 2.2, use {@link #createParser(Reader)} instead.
*/
@Deprecated
public JsonParser createJsonParser(Reader r) throws IOException, JsonParseException {
return createParser(r);
}
/**
* Method for constructing parser for parsing the contents of given byte array.
*
* @deprecated Since 2.2, use {@link #createParser(byte[])} instead.
*/
@Deprecated
public JsonParser createJsonParser(byte[] data) throws IOException, JsonParseException {
return createParser(data);
}
/**
* Method for constructing parser for parsing
* the contents of given byte array.
*
* @param data Buffer that contains data to parse
* @param offset Offset of the first data byte within buffer
* @param len Length of contents to parse within buffer
*
* @deprecated Since 2.2, use {@link #createParser(byte[],int,int)} instead.
*/
@Deprecated
public JsonParser createJsonParser(byte[] data, int offset, int len) throws IOException, JsonParseException {
return createParser(data, offset, len);
}
/**
* Method for constructing parser for parsing
* contents of given String.
*
* @deprecated Since 2.2, use {@link #createParser(String)} instead.
*/
@Deprecated
public JsonParser createJsonParser(String content) throws IOException, JsonParseException {
return createParser(content);
}
/*
/**********************************************************
/* Deprecated generator factory methods: to be removed from 3.x
/**********************************************************
*/
/**
* Method for constructing JSON generator for writing JSON content
* using specified output stream.
* Encoding to use must be specified, and needs to be one of available
* types (as per JSON specification).
*
* Underlying stream is NOT owned by the generator constructed,
* so that generator will NOT close the output stream when
* {@link JsonGenerator#close} is called (unless auto-closing
* feature,
* {@link com.fasterxml.jackson.core.JsonGenerator.Feature#AUTO_CLOSE_TARGET}
* is enabled).
* Using application needs to close it explicitly if this is the case.
*
* Note: there are formats that use fixed encoding (like most binary data formats)
* and that ignore passed in encoding.
*
* @param out OutputStream to use for writing JSON content
* @param enc Character encoding to use
*
* @deprecated Since 2.2, use {@link #createGenerator(OutputStream, JsonEncoding)} instead.
*/
@Deprecated
public JsonGenerator createJsonGenerator(OutputStream out, JsonEncoding enc) throws IOException {
return createGenerator(out, enc);
}
/**
* Method for constructing JSON generator for writing JSON content
* using specified Writer.
*
* Underlying stream is NOT owned by the generator constructed,
* so that generator will NOT close the Reader when
* {@link JsonGenerator#close} is called (unless auto-closing
* feature,
* {@link com.fasterxml.jackson.core.JsonGenerator.Feature#AUTO_CLOSE_TARGET} is enabled).
* Using application needs to close it explicitly.
*
* @param out Writer to use for writing JSON content
*
* @deprecated Since 2.2, use {@link #createGenerator(Writer)} instead.
*/
@Deprecated
public JsonGenerator createJsonGenerator(Writer out) throws IOException {
return createGenerator(out);
}
/**
* Convenience method for constructing generator that uses default
* encoding of the format (UTF-8 for JSON and most other data formats).
*
* Note: there are formats that use fixed encoding (like most binary data formats).
*
* @deprecated Since 2.2, use {@link #createGenerator(OutputStream)} instead.
*/
@Deprecated
public JsonGenerator createJsonGenerator(OutputStream out) throws IOException {
return createGenerator(out, JsonEncoding.UTF8);
}
/*
/**********************************************************
/* Factory methods used by factory for creating parser instances,
/* overridable by sub-classes
/**********************************************************
*/
/**
* Overridable factory method that actually instantiates desired parser
* given {@link InputStream} and context object.
*
* This method is specifically designed to remain
* compatible between minor versions so that sub-classes can count
* on it being called as expected. That is, it is part of official
* interface from sub-class perspective, although not a public
* method available to users of factory implementations.
*
* @since 2.1
*/
protected JsonParser _createParser(InputStream in, IOContext ctxt) throws IOException {
// As per [JACKSON-259], may want to fully disable canonicalization:
return new ByteSourceJsonBootstrapper(ctxt, in).constructParser(_parserFeatures,
_objectCodec, _byteSymbolCanonicalizer, _rootCharSymbols, _factoryFeatures);
}
/**
* Overridable factory method that actually instantiates parser
* using given {@link Reader} object for reading content.
*
* This method is specifically designed to remain
* compatible between minor versions so that sub-classes can count
* on it being called as expected. That is, it is part of official
* interface from sub-class perspective, although not a public
* method available to users of factory implementations.
*
* @since 2.1
*/
protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException {
return new ReaderBasedJsonParser(ctxt, _parserFeatures, r, _objectCodec,
_rootCharSymbols.makeChild(_factoryFeatures));
}
/**
* Overridable factory method that actually instantiates parser
* using given
* This method is specifically designed to remain
* compatible between minor versions so that sub-classes can count
* on it being called as expected. That is, it is part of official
* interface from sub-class perspective, although not a public
* method available to users of factory implementations.
*/
protected JsonParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException
{
return new ByteSourceJsonBootstrapper(ctxt, data, offset, len).constructParser(_parserFeatures,
_objectCodec, _byteSymbolCanonicalizer, _rootCharSymbols, _factoryFeatures);
}
/**
* Optional factory method, expected to be overridden
*
* @since 2.8
*/
protected JsonParser _createParser(DataInput input, IOContext ctxt) throws IOException
{
// 13-May-2016, tatu: Need to take care not to accidentally create JSON parser for
// non-JSON input.
_requireJSONFactory("InputData source not (yet?) supported for this format (%s)");
// Also: while we can't do full bootstrapping (due to read-ahead limitations), should
// at least handle possible UTF-8 BOM
int firstByte = ByteSourceJsonBootstrapper.skipUTF8BOM(input);
ByteQuadsCanonicalizer can = _byteSymbolCanonicalizer.makeChild(_factoryFeatures);
return new UTF8DataInputJsonParser(ctxt, _parserFeatures, input,
_objectCodec, can, firstByte);
}
/*
/**********************************************************
/* Factory methods used by factory for creating generator instances,
/* overridable by sub-classes
/**********************************************************
*/
/**
* Overridable factory method that actually instantiates generator for
* given {@link Writer} and context object.
*
* This method is specifically designed to remain
* compatible between minor versions so that sub-classes can count
* on it being called as expected. That is, it is part of official
* interface from sub-class perspective, although not a public
* method available to users of factory implementations.
*/
protected JsonGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException
{
WriterBasedJsonGenerator gen = new WriterBasedJsonGenerator(ctxt,
_generatorFeatures, _objectCodec, out, _quoteChar);
if (_maximumNonEscapedChar > 0) {
gen.setHighestNonEscapedChar(_maximumNonEscapedChar);
}
if (_characterEscapes != null) {
gen.setCharacterEscapes(_characterEscapes);
}
SerializableString rootSep = _rootValueSeparator;
if (rootSep != DEFAULT_ROOT_VALUE_SEPARATOR) {
gen.setRootValueSeparator(rootSep);
}
return gen;
}
/**
* Overridable factory method that actually instantiates generator for
* given {@link OutputStream} and context object, using UTF-8 encoding.
*
* This method is specifically designed to remain
* compatible between minor versions so that sub-classes can count
* on it being called as expected. That is, it is part of official
* interface from sub-class perspective, although not a public
* method available to users of factory implementations.
*/
protected JsonGenerator _createUTF8Generator(OutputStream out, IOContext ctxt) throws IOException {
UTF8JsonGenerator gen = new UTF8JsonGenerator(ctxt,
_generatorFeatures, _objectCodec, out, _quoteChar);
if (_maximumNonEscapedChar > 0) {
gen.setHighestNonEscapedChar(_maximumNonEscapedChar);
}
if (_characterEscapes != null) {
gen.setCharacterEscapes(_characterEscapes);
}
SerializableString rootSep = _rootValueSeparator;
if (rootSep != DEFAULT_ROOT_VALUE_SEPARATOR) {
gen.setRootValueSeparator(rootSep);
}
return gen;
}
protected Writer _createWriter(OutputStream out, JsonEncoding enc, IOContext ctxt) throws IOException
{
// note: this should not get called any more (caller checks, dispatches)
if (enc == JsonEncoding.UTF8) { // We have optimized writer for UTF-8
return new UTF8Writer(ctxt, out);
}
// not optimal, but should do unless we really care about UTF-16/32 encoding speed
return new OutputStreamWriter(out, enc.getJavaName());
}
/*
/**********************************************************
/* Internal factory methods, decorator handling
/**********************************************************
*/
/**
* @since 2.4
*/
protected final InputStream _decorate(InputStream in, IOContext ctxt) throws IOException {
if (_inputDecorator != null) {
InputStream in2 = _inputDecorator.decorate(ctxt, in);
if (in2 != null) {
return in2;
}
}
return in;
}
/**
* @since 2.4
*/
protected final Reader _decorate(Reader in, IOContext ctxt) throws IOException {
if (_inputDecorator != null) {
Reader in2 = _inputDecorator.decorate(ctxt, in);
if (in2 != null) {
return in2;
}
}
return in;
}
/**
* @since 2.8
*/
protected final DataInput _decorate(DataInput in, IOContext ctxt) throws IOException {
if (_inputDecorator != null) {
DataInput in2 = _inputDecorator.decorate(ctxt, in);
if (in2 != null) {
return in2;
}
}
return in;
}
/**
* @since 2.4
*/
protected final OutputStream _decorate(OutputStream out, IOContext ctxt) throws IOException {
if (_outputDecorator != null) {
OutputStream out2 = _outputDecorator.decorate(ctxt, out);
if (out2 != null) {
return out2;
}
}
return out;
}
/**
* @since 2.4
*/
protected final Writer _decorate(Writer out, IOContext ctxt) throws IOException {
if (_outputDecorator != null) {
Writer out2 = _outputDecorator.decorate(ctxt, out);
if (out2 != null) {
return out2;
}
}
return out;
}
/*
/**********************************************************
/* Internal factory methods, other
/**********************************************************
*/
/**
* Method used by factory to create buffer recycler instances
* for parsers and generators.
*
* Note: only public to give access for
* NOTE: as of Jackson 2.x, use of JSON-specific builder is bit cumbersome
* since {@link JsonFactory} serves dual duty of base class AND actual
* implementation for JSON backend. This will be fixed in Jackson 3.0.
*
* @since 2.10
*/
public class JsonFactoryBuilder extends TSFBuilder
* NOTE! Lowest legal value (aside from marker 0) is 127: for ASCII range, other checks apply
* and this threshold is ignored. If value between [1, 126] is specified, 127 will be
* used instead.
*
* @param maxNonEscaped Highest character code that is NOT automatically escaped; if
* positive value above 0, or 0 to indicate that no automatic escaping is applied
* beside from what JSON specification requires (and possible custom escape settings).
* Values between 1 and 127 are all taken to behave as if 127 is specified: that is,
* no automatic escaping is applied in ASCII range.
*/
public JsonFactoryBuilder highestNonEscapedChar(int maxNonEscaped) {
_maximumNonEscapedChar = (maxNonEscaped <= 0) ? 0 : Math.max(127, maxNonEscaped);
return this;
}
/**
* Method that allows specifying an alternate
* character used for quoting field names (if field name quoting has not
* been disabled with {@link JsonWriteFeature#QUOTE_FIELD_NAMES})
* and JSON String values.
*
* Default value is double-quote ({@code "}); typical alternative is
* single-quote/apostrophe ({@code '}).
*
* @param ch Character to use for quoting field names and JSON String values.
*/
public JsonFactoryBuilder quoteChar(char ch) {
// 12-Aug-2019, tatu: Due to implementation details, escaping characters beyond
// 7-bit ASCII set has deep overhead so let's limit set. If we absolutely
// must it is possible of course, but leads to problems combining with
// custom escaping aspects.
if (ch > 0x7F) {
throw new IllegalArgumentException("Can only use Unicode characters up to 0x7F as quote characters");
}
_quoteChar = ch;
return this;
}
// // // Accessors for JSON-specific settings
public CharacterEscapes characterEscapes() { return _characterEscapes; }
public SerializableString rootValueSeparator() { return _rootValueSeparator; }
public int highestNonEscapedChar() { return _maximumNonEscapedChar; }
public char quoteChar() { return _quoteChar; }
@Override
public JsonFactory build() {
// 28-Dec-2017, tatu: No special settings beyond base class ones, so:
return new JsonFactory(this);
}
}
JsonGenerationException.java 0000664 0000000 0000000 00000003447 13561642473 0033737 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*/
package com.fasterxml.jackson.core;
/**
* Exception type for exceptions during JSON writing, such as trying
* to output content in wrong context (non-matching end-array or end-object,
* for example).
*/
public class JsonGenerationException
extends JsonProcessingException
{
private final static long serialVersionUID = 123; // Stupid eclipse...
// transient since 2.7.4
protected transient JsonGenerator _processor;
@Deprecated // since 2.7
public JsonGenerationException(Throwable rootCause) {
super(rootCause);
}
@Deprecated // since 2.7
public JsonGenerationException(String msg) {
super(msg, (JsonLocation)null);
}
@Deprecated // since 2.7
public JsonGenerationException(String msg, Throwable rootCause) {
super(msg, null, rootCause);
}
/**
* @since 2.7
*/
public JsonGenerationException(Throwable rootCause, JsonGenerator g) {
super(rootCause);
_processor = g;
}
/**
* @since 2.7
*/
public JsonGenerationException(String msg, JsonGenerator g) {
super(msg, (JsonLocation) null);
_processor = g;
}
/**
* @since 2.7
*/
public JsonGenerationException(String msg, Throwable rootCause, JsonGenerator g) {
super(msg, null, rootCause);
_processor = g;
}
/**
* Fluent method that may be used to assign originating {@link JsonGenerator},
* to be accessed using {@link #getProcessor()}.
*
* @since 2.7
*/
public JsonGenerationException withGenerator(JsonGenerator g) {
_processor = g;
return this;
}
@Override
public JsonGenerator getProcessor() { return _processor; }
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java 0000664 0000000 0000000 00000240403 13561642473 0031765 0 ustar 00root root 0000000 0000000 /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*/
package com.fasterxml.jackson.core;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import com.fasterxml.jackson.core.JsonParser.NumberType;
import com.fasterxml.jackson.core.io.CharacterEscapes;
import com.fasterxml.jackson.core.type.WritableTypeId;
import com.fasterxml.jackson.core.type.WritableTypeId.Inclusion;
import com.fasterxml.jackson.core.util.VersionUtil;
import static com.fasterxml.jackson.core.JsonTokenId.*;
/**
* Base class that defines public API for writing JSON content.
* Instances are created using factory methods of
* a {@link JsonFactory} instance.
*
* @author Tatu Saloranta
*/
public abstract class JsonGenerator
implements Closeable, Flushable, Versioned
{
/**
* Enumeration that defines all togglable features for generators.
*/
public enum Feature {
// // Low-level I/O / content features
/**
* Feature that determines whether generator will automatically
* close underlying output target that is NOT owned by the
* generator.
* If disabled, calling application has to separately
* close the underlying {@link OutputStream} and {@link Writer}
* instances used to create the generator. If enabled, generator
* will handle closing, as long as generator itself gets closed:
* this happens when end-of-input is encountered, or generator
* is closed by a call to {@link JsonGenerator#close}.
*
* Feature is enabled by default.
*/
AUTO_CLOSE_TARGET(true),
/**
* Feature that determines what happens when the generator is
* closed while there are still unmatched
* {@link JsonToken#START_ARRAY} or {@link JsonToken#START_OBJECT}
* entries in output content. If enabled, such Array(s) and/or
* Object(s) are automatically closed; if disabled, nothing
* specific is done.
*
* Feature is enabled by default.
*/
AUTO_CLOSE_JSON_CONTENT(true),
/**
* Feature that specifies that calls to {@link #flush} will cause
* matching
* Feature is enabled by default.
*/
FLUSH_PASSED_TO_STREAM(true),
// // Quoting-related features
/**
* Feature that determines whether JSON Object field names are
* quoted using double-quotes, as specified by JSON specification
* or not. Ability to disable quoting was added to support use
* cases where they are not usually expected, which most commonly
* occurs when used straight from Javascript.
*
* Feature is enabled by default (since it is required by JSON specification).
*
* @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#QUOTE_FIELD_NAMES} instead
*/
@Deprecated
QUOTE_FIELD_NAMES(true),
/**
* Feature that determines whether "exceptional" (not real number)
* float/double values are output as quoted strings.
* The values checked are Double.Nan,
* Double.POSITIVE_INFINITY and Double.NEGATIVE_INIFINTY (and
* associated Float values).
* If feature is disabled, these numbers are still output using
* associated literal values, resulting in non-conformant
* output.
*
* Feature is enabled by default.
*
* @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#WRITE_NAN_AS_STRINGS} instead
*/
@Deprecated
QUOTE_NON_NUMERIC_NUMBERS(true),
// // Character escaping features
/**
* Feature that specifies that all characters beyond 7-bit ASCII
* range (i.e. code points of 128 and above) need to be output
* using format-specific escapes (for JSON, backslash escapes),
* if format uses escaping mechanisms (which is generally true
* for textual formats but not for binary formats).
*
* Note that this setting may not necessarily make sense for all
* data formats (for example, binary formats typically do not use
* any escaping mechanisms; and some textual formats do not have
* general-purpose escaping); if so, settings is simply ignored.
* Put another way, effects of this feature are data-format specific.
*
* Feature is disabled by default.
*
* @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#ESCAPE_NON_ASCII} instead
*/
@Deprecated
ESCAPE_NON_ASCII(false),
// // Datatype coercion features
/**
* Feature that forces all Java numbers to be written as Strings,
* even if the underlying data format has non-textual representation
* (which is the case for JSON as well as all binary formats).
* Default state is 'false', meaning that Java numbers are to
* be serialized using basic numeric serialization (as JSON
* numbers, integral or floating point, for example).
* If enabled, all such numeric values are instead written out as
* textual values (which for JSON means quoted in double-quotes).
*
* One use case is to avoid problems with Javascript limitations:
* since Javascript standard specifies that all number handling
* should be done using 64-bit IEEE 754 floating point values,
* result being that some 64-bit integer values can not be
* accurately represent (as mantissa is only 51 bit wide).
*
* Feature is disabled by default.
*
* @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonWriteFeature#WRITE_NUMBERS_AS_STRINGS} instead
*/
@Deprecated
WRITE_NUMBERS_AS_STRINGS(false),
/**
* Feature that determines whether {@link java.math.BigDecimal} entries are
* serialized using {@link java.math.BigDecimal#toPlainString()} to prevent
* values to be written using scientific notation.
*
* NOTE: only affects generators that serialize {@link java.math.BigDecimal}s
* using textual representation (textual formats but potentially some binary
* formats).
*
* Feature is disabled by default, so default output mode is used; this generally
* depends on how {@link BigDecimal} has been created.
*
* @since 2.3
*/
WRITE_BIGDECIMAL_AS_PLAIN(false),
// // Schema/Validity support features
/**
* Feature that determines whether {@link JsonGenerator} will explicitly
* check that no duplicate JSON Object field names are written.
* If enabled, generator will check all names within context and report
* duplicates by throwing a {@link JsonGenerationException}; if disabled,
* no such checking will be done. Assumption in latter case is
* that caller takes care of not trying to write duplicate names.
*
* Note that enabling this feature will incur performance overhead
* due to having to store and check additional information.
*
* Feature is disabled by default.
*
* @since 2.3
*/
STRICT_DUPLICATE_DETECTION(false),
/**
* Feature that determines what to do if the underlying data format requires knowledge
* of all properties to output, and if no definition is found for a property that
* caller tries to write. If enabled, such properties will be quietly ignored;
* if disabled, a {@link JsonProcessingException} will be thrown to indicate the
* problem.
* Typically most textual data formats do NOT require schema information (although
* some do, such as CSV), whereas many binary data formats do require definitions
* (such as Avro, protobuf), although not all (Smile, CBOR, BSON and MessagePack do not).
*
* Note that support for this feature is implemented by individual data format
* module, if (and only if) it makes sense for the format in question. For JSON,
* for example, this feature has no effect as properties need not be pre-defined.
*
* Feature is disabled by default, meaning that if the underlying data format
* requires knowledge of all properties to output, attempts to write an unknown
* property will result in a {@link JsonProcessingException}
*
* @since 2.5
*/
IGNORE_UNKNOWN(false),
;
private final boolean _defaultState;
private final int _mask;
/**
* Method that calculates bit set (flags) of all features that
* are enabled by default.
*/
public static int collectDefaults()
{
int flags = 0;
for (Feature f : values()) {
if (f.enabledByDefault()) {
flags |= f.getMask();
}
}
return flags;
}
private Feature(boolean defaultState) {
_defaultState = defaultState;
_mask = (1 << ordinal());
}
public boolean enabledByDefault() { return _defaultState; }
/**
* @since 2.3
*/
public boolean enabledIn(int flags) { return (flags & _mask) != 0; }
public int getMask() { return _mask; }
}
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Object that handles pretty-printing (usually additional
* white space to make results more human-readable) during
* output. If null, no pretty-printing is done.
*/
protected PrettyPrinter _cfgPrettyPrinter;
/*
/**********************************************************
/* Construction, initialization
/**********************************************************
*/
protected JsonGenerator() { }
/**
* Method that can be called to set or reset the object to
* use for writing Java objects as JsonContent
* (using method {@link #writeObject}).
*
* @return Generator itself (this), to allow chaining
*/
public abstract JsonGenerator setCodec(ObjectCodec oc);
/**
* Method for accessing the object used for writing Java
* object as JSON content
* (using method {@link #writeObject}).
*/
public abstract ObjectCodec getCodec();
/**
* Accessor for finding out version of the bundle that provided this generator instance.
*/
@Override
public abstract Version version();
/*
/**********************************************************
/* Public API, Feature configuration
/**********************************************************
*/
/**
* Method for enabling specified parser features:
* check {@link Feature} for list of available features.
*
* @return Generator itself (this), to allow chaining
*/
public abstract JsonGenerator enable(Feature f);
/**
* Method for disabling specified features
* (check {@link Feature} for list of features)
*
* @return Generator itself (this), to allow chaining
*/
public abstract JsonGenerator disable(Feature f);
/**
* Method for enabling or disabling specified feature:
* check {@link Feature} for list of available features.
*
* @return Generator itself (this), to allow chaining
*/
public final JsonGenerator configure(Feature f, boolean state) {
if (state) enable(f); else disable(f);
return this;
}
/**
* Method for checking whether given feature is enabled.
* Check {@link Feature} for list of available features.
*/
public abstract boolean isEnabled(Feature f);
/**
* @since 2.10
*/
public boolean isEnabled(StreamWriteFeature f) {
return isEnabled(f.mappedFeature());
}
/**
* Bulk access method for getting state of all standard (non-dataformat-specific)
* {@link JsonGenerator.Feature}s.
*
* @return Bit mask that defines current states of all standard {@link JsonGenerator.Feature}s.
*
* @since 2.3
*/
public abstract int getFeatureMask();
/**
* Bulk set method for (re)setting states of all standard {@link Feature}s
*
* @since 2.3
*
* @param values Bitmask that defines which {@link Feature}s are enabled
* and which disabled
*
* @return This parser object, to allow chaining of calls
*
* @deprecated Since 2.7, use {@link #overrideStdFeatures(int, int)} instead -- remove from 2.9
*/
@Deprecated
public abstract JsonGenerator setFeatureMask(int values);
/**
* Bulk set method for (re)setting states of features specified by
* Default implementation will simply throw an exception to indicate that
* the generator implementation does not support any {@link FormatFeature}s.
*
* @param values Bit mask of set/clear state for features to change
* @param mask Bit mask of features to change
*
* @since 2.6
*/
public JsonGenerator overrideFormatFeatures(int values, int mask) {
// 08-Oct-2018, tatu: For 2.10 we actually do get `JsonWriteFeature`s, although they
// are (for 2.x only, not for 3.x) mapper to legacy settings. So do not freak out:
// throw new IllegalArgumentException("No FormatFeatures defined for generator of type "+getClass().getName());
return this;
}
/*
/**********************************************************
/* Public API, Schema configuration
/**********************************************************
*/
/**
* Method to call to make this generator use specified schema.
* Method must be called before generating any content, right after instance
* has been created.
* Note that not all generators support schemas; and those that do usually only
* accept specific types of schemas: ones defined for data format this generator
* produces.
*
* If generator does not support specified schema, {@link UnsupportedOperationException}
* is thrown.
*
* @param schema Schema to use
*
* @throws UnsupportedOperationException if generator does not support schema
*/
public void setSchema(FormatSchema schema) {
throw new UnsupportedOperationException("Generator of type "+getClass().getName()+" does not support schema of type '"
+schema.getSchemaType()+"'");
}
/**
* Method for accessing Schema that this parser uses, if any.
* Default implementation returns null.
*
* @since 2.1
*/
public FormatSchema getSchema() { return null; }
/*
/**********************************************************
/* Public API, other configuration
/**********************************************************
*/
/**
* Method for setting a custom pretty printer, which is usually
* used to add indentation for improved human readability.
* By default, generator does not do pretty printing.
*
* To use the default pretty printer that comes with core
* Jackson distribution, call {@link #useDefaultPrettyPrinter}
* instead.
*
* @return Generator itself (this), to allow chaining
*/
public JsonGenerator setPrettyPrinter(PrettyPrinter pp) {
_cfgPrettyPrinter = pp;
return this;
}
/**
* Accessor for checking whether this generator has a configured
* {@link PrettyPrinter}; returns it if so, null if none configured.
*
* @since 2.1
*/
public PrettyPrinter getPrettyPrinter() {
return _cfgPrettyPrinter;
}
/**
* Convenience method for enabling pretty-printing using
* the default pretty printer
* ({@link com.fasterxml.jackson.core.util.DefaultPrettyPrinter}).
*
* @return Generator itself (this), to allow chaining
*/
public abstract JsonGenerator useDefaultPrettyPrinter();
/**
* Method that can be called to request that generator escapes
* all character codes above specified code point (if positive value);
* or, to not escape any characters except for ones that must be
* escaped for the data format (if -1).
* To force escaping of all non-ASCII characters, for example,
* this method would be called with value of 127.
*
* Note that generators are NOT required to support setting of value
* higher than 127, because there are other ways to affect quoting
* (or lack thereof) of character codes between 0 and 127.
* Not all generators support concept of escaping, either; if so,
* calling this method will have no effect.
*
* Default implementation does nothing; sub-classes need to redefine
* it according to rules of supported data format.
*
* @param charCode Either -1 to indicate that no additional escaping
* is to be done; or highest code point not to escape (meaning higher
* ones will be), if positive value.
*/
public JsonGenerator setHighestNonEscapedChar(int charCode) { return this; }
/**
* Accessor method for testing what is the highest unescaped character
* configured for this generator. This may be either positive value
* (when escaping configuration has been set and is in effect), or
* 0 to indicate that no additional escaping is in effect.
* Some generators may not support additional escaping: for example,
* generators for binary formats that do not use escaping should
* simply return 0.
*
* @return Currently active limitation for highest non-escaped character,
* if defined; or 0 to indicate no additional escaping is performed.
*/
public int getHighestEscapedChar() { return 0; }
/**
* Method for accessing custom escapes factory uses for {@link JsonGenerator}s
* it creates.
*/
public CharacterEscapes getCharacterEscapes() { return null; }
/**
* Method for defining custom escapes factory uses for {@link JsonGenerator}s
* it creates.
*
* Default implementation does nothing and simply returns this instance.
*/
public JsonGenerator setCharacterEscapes(CharacterEscapes esc) { return this; }
/**
* Method that allows overriding String used for separating root-level
* JSON values (default is single space character)
*
* Default implementation throws {@link UnsupportedOperationException}.
*
* @param sep Separator to use, if any; null means that no separator is
* automatically added
*
* @since 2.1
*/
public JsonGenerator setRootValueSeparator(SerializableString sep) {
throw new UnsupportedOperationException();
}
/*
/**********************************************************
/* Public API, output state access
/**********************************************************
*/
/**
* Method that can be used to get access to object that is used
* as target for generated output; this is usually either
* {@link OutputStream} or {@link Writer}, depending on what
* generator was constructed with.
* Note that returned value may be null in some cases; including
* case where implementation does not want to exposed raw
* source to caller.
* In cases where output has been decorated, object returned here
* is the decorated version; this allows some level of interaction
* between users of generator and decorator object.
*
* In general use of this accessor should be considered as
* "last effort", i.e. only used if no other mechanism is applicable.
*/
public Object getOutputTarget() {
return null;
}
/**
* Method for verifying amount of content that is buffered by generator
* but not yet flushed to the underlying target (stream, writer),
* in units (byte, char) that the generator implementation uses for buffering;
* or -1 if this information is not available.
* Unit used is often the same as the unit of underlying target (that is,
* `byte` for {@link java.io.OutputStream}, `char` for {@link java.io.Writer}),
* but may differ if buffering is done before encoding.
* Default JSON-backed implementations do use matching units.
*
* Note: non-JSON implementations will be retrofitted for 2.6 and beyond;
* please report if you see -1 (missing override)
*
* @return Amount of content buffered in internal units, if amount known and
* accessible; -1 if not accessible.
*
* @since 2.6
*/
public int getOutputBuffered() {
return -1;
}
/**
* Helper method, usually equivalent to:
*
* Note that "current value" is NOT populated (or used) by Streaming parser;
* it is only used by higher-level data-binding functionality.
* The reason it is included here is that it can be stored and accessed hierarchically,
* and gets passed through data-binding.
*
* @since 2.5
*/
public Object getCurrentValue() {
JsonStreamContext ctxt = getOutputContext();
return (ctxt == null) ? null : ctxt.getCurrentValue();
}
/**
* Helper method, usually equivalent to:
*
* Default implementation returns false; overridden by data formats
* that do support native Object Ids. Caller is expected to either
* use a non-native notation (explicit property or such), or fail,
* in case it can not use native object ids.
*
* @since 2.3
*/
public boolean canWriteObjectId() { return false; }
/**
* Introspection method that may be called to see if the underlying
* data format supports some kind of Type Ids natively (many do not;
* for example, JSON doesn't).
* This method must be called prior to calling
* {@link #writeTypeId}.
*
* Default implementation returns false; overridden by data formats
* that do support native Type Ids. Caller is expected to either
* use a non-native notation (explicit property or such), or fail,
* in case it can not use native type ids.
*
* @since 2.3
*/
public boolean canWriteTypeId() { return false; }
/**
* Introspection method that may be called to see if the underlying
* data format supports "native" binary data; that is, an efficient
* output of binary content without encoding.
*
* Default implementation returns false; overridden by data formats
* that do support native binary content.
*
* @since 2.3
*/
public boolean canWriteBinaryNatively() { return false; }
/**
* Introspection method to call to check whether it is ok to omit
* writing of Object fields or not. Most formats do allow omission,
* but certain positional formats (such as CSV) require output of
* placeholders, even if no real values are to be emitted.
*
* @since 2.3
*/
public boolean canOmitFields() { return true; }
/**
* Introspection method to call to check whether it is possible
* to write numbers using {@link #writeNumber(java.lang.String)}
* using possible custom format, or not. Typically textual formats
* allow this (and JSON specifically does), whereas binary formats
* do not allow this (except by writing them as Strings).
* Usual reason for calling this method is to check whether custom
* formatting of numbers may be applied by higher-level code (databinding)
* or not.
*
* @since 2.8
*/
public boolean canWriteFormattedNumbers() { return false; }
/*
/**********************************************************
/* Public API, write methods, structural
/**********************************************************
*/
/**
* Method for writing starting marker of a Array value
* (for JSON this is character '['; plus possible white space decoration
* if pretty-printing is enabled).
*
* Array values can be written in any context where values
* are allowed: meaning everywhere except for when
* a field name is expected.
*/
public abstract void writeStartArray() throws IOException;
// TODO: deprecate in 2.11 (remove from 3.0)
/**
* Method for writing start marker of an Array value, similar
* to {@link #writeStartArray()}, but also specifying how many
* elements will be written for the array before calling
* {@link #writeEndArray()}.
*
* Default implementation simply calls {@link #writeStartArray()}.
*
* @param size Number of elements this array will have: actual
* number of values written (before matching call to
* {@link #writeEndArray()} MUST match; generator MAY verify
* this is the case.
*
* @since 2.4
*/
public void writeStartArray(int size) throws IOException {
writeStartArray();
}
/**
* @since 2.10
*/
public void writeStartArray(Object forValue) throws IOException {
writeStartArray();
setCurrentValue(forValue);
}
/**
* @since 2.10
*/
public void writeStartArray(Object forValue, int size) throws IOException {
writeStartArray(size);
setCurrentValue(forValue);
}
/**
* Method for writing closing marker of a JSON Array value
* (character ']'; plus possible white space decoration
* if pretty-printing is enabled).
*
* Marker can be written if the innermost structured type
* is Array.
*/
public abstract void writeEndArray() throws IOException;
/**
* Method for writing starting marker of an Object value
* (character '{'; plus possible white space decoration
* if pretty-printing is enabled).
*
* Object values can be written in any context where values
* are allowed: meaning everywhere except for when
* a field name is expected.
*/
public abstract void writeStartObject() throws IOException;
/**
* Method for writing starting marker of an Object value
* to represent the given Java Object value.
* Argument is offered as metadata, but more
* importantly it should be assigned as the "current value"
* for the Object content that gets constructed and initialized.
*
* Object values can be written in any context where values
* are allowed: meaning everywhere except for when
* a field name is expected.
*
* @since 2.8
*/
public void writeStartObject(Object forValue) throws IOException
{
writeStartObject();
setCurrentValue(forValue);
}
/**
* Method for writing starting marker of an Object value
* to represent the given Java Object value.
* Argument is offered as metadata, but more
* importantly it should be assigned as the "current value"
* for the Object content that gets constructed and initialized.
* In addition, caller knows number of key/value pairs ("properties")
* that will get written for the Object value: this is relevant for
* some format backends (but not, as an example, for JSON).
*
* Object values can be written in any context where values
* are allowed: meaning everywhere except for when
* a field name is expected.
*
* @since 2.10
*/
public void writeStartObject(Object forValue, int size) throws IOException
{
writeStartObject();
setCurrentValue(forValue);
}
/**
* Method for writing closing marker of an Object value
* (character '}'; plus possible white space decoration
* if pretty-printing is enabled).
*
* Marker can be written if the innermost structured type
* is Object, and the last written event was either a
* complete value, or START-OBJECT marker (see JSON specification
* for more details).
*/
public abstract void writeEndObject() throws IOException;
/**
* Method for writing a field name (JSON String surrounded by
* double quotes: syntactically identical to a JSON String value),
* possibly decorated by white space if pretty-printing is enabled.
*
* Field names can only be written in Object context (check out
* JSON specification for details), when field name is expected
* (field names alternate with values).
*/
public abstract void writeFieldName(String name) throws IOException;
/**
* Method similar to {@link #writeFieldName(String)}, main difference
* being that it may perform better as some of processing (such as
* quoting of certain characters, or encoding into external encoding
* if supported by generator) can be done just once and reused for
* later calls.
*
* Default implementation simple uses unprocessed name container in
* serialized String; implementations are strongly encouraged to make
* use of more efficient methods argument object has.
*/
public abstract void writeFieldName(SerializableString name) throws IOException;
/**
* Alternative to {@link #writeFieldName(String)} that may be used
* in cases where property key is of numeric type; either where
* underlying format supports such notion (some binary formats do,
* unlike JSON), or for convenient conversion into String presentation.
* Default implementation will simply convert id into
* Default implementation just calls {@link #writeString(String)};
* sub-classes should override it with more efficient implementation
* if possible.
*/
public abstract void writeString(SerializableString text) throws IOException;
/**
* Method similar to {@link #writeString(String)} but that takes as
* its input a UTF-8 encoded String that is to be output as-is, without additional
* escaping (type of which depends on data format; backslashes for JSON).
* However, quoting that data format requires (like double-quotes for JSON) will be added
* around the value if and as necessary.
*
* Note that some backends may choose not to support this method: for
* example, if underlying destination is a {@link java.io.Writer}
* using this method would require UTF-8 decoding.
* If so, implementation may instead choose to throw a
* {@link UnsupportedOperationException} due to ineffectiveness
* of having to decode input.
*/
public abstract void writeRawUTF8String(byte[] text, int offset, int length)
throws IOException;
/**
* Method similar to {@link #writeString(String)} but that takes as its input
* a UTF-8 encoded String which has not been escaped using whatever
* escaping scheme data format requires (for JSON that is backslash-escaping
* for control characters and double-quotes; for other formats something else).
* This means that textual JSON backends need to check if value needs
* JSON escaping, but otherwise can just be copied as is to output.
* Also, quoting that data format requires (like double-quotes for JSON) will be added
* around the value if and as necessary.
*
* Note that some backends may choose not to support this method: for
* example, if underlying destination is a {@link java.io.Writer}
* using this method would require UTF-8 decoding.
* In this case
* generator implementation may instead choose to throw a
* {@link UnsupportedOperationException} due to ineffectiveness
* of having to decode input.
*/
public abstract void writeUTF8String(byte[] text, int offset, int length)
throws IOException;
/*
/**********************************************************
/* Public API, write methods, binary/raw content
/**********************************************************
*/
/**
* Method that will force generator to copy
* input text verbatim with no modifications (including
* that no escaping is done and no separators are added even
* if context [array, object] would otherwise require such).
* If such separators are desired, use
* {@link #writeRawValue(String)} instead.
*
* Note that not all generator implementations necessarily support
* such by-pass methods: those that do not will throw
* {@link UnsupportedOperationException}.
*/
public abstract void writeRaw(String text) throws IOException;
/**
* Method that will force generator to copy
* input text verbatim with no modifications (including
* that no escaping is done and no separators are added even
* if context [array, object] would otherwise require such).
* If such separators are desired, use
* {@link #writeRawValue(String)} instead.
*
* Note that not all generator implementations necessarily support
* such by-pass methods: those that do not will throw
* {@link UnsupportedOperationException}.
*/
public abstract void writeRaw(String text, int offset, int len) throws IOException;
/**
* Method that will force generator to copy
* input text verbatim with no modifications (including
* that no escaping is done and no separators are added even
* if context [array, object] would otherwise require such).
* If such separators are desired, use
* {@link #writeRawValue(String)} instead.
*
* Note that not all generator implementations necessarily support
* such by-pass methods: those that do not will throw
* {@link UnsupportedOperationException}.
*/
public abstract void writeRaw(char[] text, int offset, int len) throws IOException;
/**
* Method that will force generator to copy
* input text verbatim with no modifications (including
* that no escaping is done and no separators are added even
* if context [array, object] would otherwise require such).
* If such separators are desired, use
* {@link #writeRawValue(String)} instead.
*
* Note that not all generator implementations necessarily support
* such by-pass methods: those that do not will throw
* {@link UnsupportedOperationException}.
*/
public abstract void writeRaw(char c) throws IOException;
/**
* Method that will force generator to copy
* input text verbatim with no modifications (including
* that no escaping is done and no separators are added even
* if context [array, object] would otherwise require such).
* If such separators are desired, use
* {@link #writeRawValue(String)} instead.
*
* Note that not all generator implementations necessarily support
* such by-pass methods: those that do not will throw
* {@link UnsupportedOperationException}.
*
* The default implementation delegates to {@link #writeRaw(String)};
* other backends that support raw inclusion of text are encouraged
* to implement it in more efficient manner (especially if they
* use UTF-8 encoding).
*
* @since 2.1
*/
// public abstract void writeRaw(SerializableString raw) throws IOException;
public void writeRaw(SerializableString raw) throws IOException {
writeRaw(raw.getValue());
}
/**
* Method that will force generator to copy
* input text verbatim without any modifications, but assuming
* it must constitute a single legal JSON value (number, string,
* boolean, null, Array or List). Assuming this, proper separators
* are added if and as needed (comma or colon), and generator
* state updated to reflect this.
*/
public abstract void writeRawValue(String text) throws IOException;
public abstract void writeRawValue(String text, int offset, int len) throws IOException;
public abstract void writeRawValue(char[] text, int offset, int len) throws IOException;
/**
* Method similar to {@link #writeRawValue(String)}, but potentially more
* efficient as it may be able to use pre-encoded content (similar to
* {@link #writeRaw(SerializableString)}.
*
* @since 2.5
*/
public void writeRawValue(SerializableString raw) throws IOException {
writeRawValue(raw.getValue());
}
/**
* Method that will output given chunk of binary data as base64
* encoded, as a complete String value (surrounded by double quotes).
* This method defaults
*
* Note: because JSON Strings can not contain unescaped linefeeds,
* if linefeeds are included (as per last argument), they must be
* escaped. This adds overhead for decoding without improving
* readability.
* Alternatively if linefeeds are not included,
* resulting String value may violate the requirement of base64
* RFC which mandates line-length of 76 characters and use of
* linefeeds. However, all {@link JsonParser} implementations
* are required to accept such "long line base64"; as do
* typical production-level base64 decoders.
*
* @param bv Base64 variant to use: defines details such as
* whether padding is used (and if so, using which character);
* what is the maximum line length before adding linefeed,
* and also the underlying alphabet to use.
*/
public abstract void writeBinary(Base64Variant bv,
byte[] data, int offset, int len) throws IOException;
/**
* Similar to {@link #writeBinary(Base64Variant,byte[],int,int)},
* but default to using the Jackson default Base64 variant
* (which is {@link Base64Variants#MIME_NO_LINEFEEDS}).
*/
public void writeBinary(byte[] data, int offset, int len) throws IOException {
writeBinary(Base64Variants.getDefaultVariant(), data, offset, len);
}
/**
* Similar to {@link #writeBinary(Base64Variant,byte[],int,int)},
* but assumes default to using the Jackson default Base64 variant
* (which is {@link Base64Variants#MIME_NO_LINEFEEDS}). Also
* assumes that whole byte array is to be output.
*/
public void writeBinary(byte[] data) throws IOException {
writeBinary(Base64Variants.getDefaultVariant(), data, 0, data.length);
}
/**
* Similar to {@link #writeBinary(Base64Variant,InputStream,int)},
* but assumes default to using the Jackson default Base64 variant
* (which is {@link Base64Variants#MIME_NO_LINEFEEDS}).
*
* @param data InputStream to use for reading binary data to write.
* Will not be closed after successful write operation
* @param dataLength (optional) number of bytes that will be available;
* or -1 to be indicate it is not known. Note that implementations
* need not support cases where length is not known in advance; this
* depends on underlying data format: JSON output does NOT require length,
* other formats may
*/
public int writeBinary(InputStream data, int dataLength)
throws IOException {
return writeBinary(Base64Variants.getDefaultVariant(), data, dataLength);
}
/**
* Method similar to {@link #writeBinary(Base64Variant,byte[],int,int)},
* but where input is provided through a stream, allowing for incremental
* writes without holding the whole input in memory.
*
* @param bv Base64 variant to use
* @param data InputStream to use for reading binary data to write.
* Will not be closed after successful write operation
* @param dataLength (optional) number of bytes that will be available;
* or -1 to be indicate it is not known.
* If a positive length is given,
* Note: because of lack of type safety, some generator
* implementations may not be able to implement this
* method. For example, if a binary JSON format is used,
* it may require type information for encoding; similarly
* for generator-wrappers around Java objects or JSON nodes.
* If implementation does not implement this method,
* it needs to throw {@link UnsupportedOperationException}.
*
* @throws UnsupportedOperationException If underlying data format does not
* support numbers serialized textually AND if generator is not allowed
* to just output a String instead (Schema-based formats may require actual
* number, for example)
*/
public abstract void writeNumber(String encodedValue) throws IOException;
/*
/**********************************************************
/* Public API, write methods, other value types
/**********************************************************
*/
/**
* Method for outputting literal JSON boolean value (one of
* Strings 'true' and 'false').
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*/
public abstract void writeBoolean(boolean state) throws IOException;
/**
* Method for outputting literal JSON null value.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*/
public abstract void writeNull() throws IOException;
/**
* Method that can be called on backends that support passing opaque datatypes of
* non-JSON formats
*
* @since 2.8
*/
public void writeEmbeddedObject(Object object) throws IOException {
// 01-Sep-2016, tatu: As per [core#318], handle small number of cases
if (object == null) {
writeNull();
return;
}
if (object instanceof byte[]) {
writeBinary((byte[]) object);
return;
}
throw new JsonGenerationException("No native support for writing embedded objects of type "
+object.getClass().getName(),
this);
}
/*
/**********************************************************
/* Public API, write methods, Native Ids (type, object)
/**********************************************************
*/
/**
* Method that can be called to output so-called native Object Id.
* Note that it may only be called after ensuring this is legal
* (with {@link #canWriteObjectId()}), as not all data formats
* have native type id support; and some may only allow them in
* certain positions or locations.
* If output is not allowed by the data format in this position,
* a {@link JsonGenerationException} will be thrown.
*
* @since 2.3
*/
public void writeObjectId(Object id) throws IOException {
throw new JsonGenerationException("No native support for writing Object Ids", this);
}
/**
* Method that can be called to output references to native Object Ids.
* Note that it may only be called after ensuring this is legal
* (with {@link #canWriteObjectId()}), as not all data formats
* have native type id support; and some may only allow them in
* certain positions or locations.
* If output is not allowed by the data format in this position,
* a {@link JsonGenerationException} will be thrown.
*/
public void writeObjectRef(Object id) throws IOException {
throw new JsonGenerationException("No native support for writing Object Ids", this);
}
/**
* Method that can be called to output so-called native Type Id.
* Note that it may only be called after ensuring this is legal
* (with {@link #canWriteTypeId()}), as not all data formats
* have native type id support; and some may only allow them in
* certain positions or locations.
* If output is not allowed by the data format in this position,
* a {@link JsonGenerationException} will be thrown.
*
* @since 2.3
*/
public void writeTypeId(Object id) throws IOException {
throw new JsonGenerationException("No native support for writing Type Ids", this);
}
/*
* Replacement method for {@link #writeTypeId(Object)} which is called
* regardless of whether format has native type ids. If it does have native
* type ids, those are to be used (if configuration allows this), if not,
* structural type id inclusion is to be used. For JSON, for example, no
* native type ids exist and structural inclusion is always used.
*
* NOTE: databind may choose to skip calling this method for some special cases
* (and instead included type id via regular write methods and/or {@link #writeTypeId}
* -- this is discouraged, but not illegal, and may be necessary as a work-around
* in some cases.
*
* @since 2.9
*/
public WritableTypeId writeTypePrefix(WritableTypeId typeIdDef) throws IOException
{
Object id = typeIdDef.id;
final JsonToken valueShape = typeIdDef.valueShape;
if (canWriteTypeId()) {
typeIdDef.wrapperWritten = false;
// just rely on native type output method (sub-classes likely to override)
writeTypeId(id);
} else {
// No native type id; write wrappers
// Normally we only support String type ids (non-String reserved for native type ids)
String idStr = (id instanceof String) ? (String) id : String.valueOf(id);
typeIdDef.wrapperWritten = true;
Inclusion incl = typeIdDef.include;
// first: can not output "as property" if value not Object; if so, must do "as array"
if ((valueShape != JsonToken.START_OBJECT)
&& incl.requiresObjectContext()) {
typeIdDef.include = incl = WritableTypeId.Inclusion.WRAPPER_ARRAY;
}
switch (incl) {
case PARENT_PROPERTY:
// nothing to do here, as it has to be written in suffix...
break;
case PAYLOAD_PROPERTY:
// only output as native type id; otherwise caller must handle using some
// other mechanism, so...
break;
case METADATA_PROPERTY:
// must have Object context by now, so simply write as field name
// Note, too, that it's bit tricky, since we must print START_OBJECT that is part
// of value first -- and then NOT output it later on: hence return "early"
writeStartObject(typeIdDef.forValue);
writeStringField(typeIdDef.asProperty, idStr);
return typeIdDef;
case WRAPPER_OBJECT:
// NOTE: this is wrapper, not directly related to value to output, so don't pass
writeStartObject();
writeFieldName(idStr);
break;
case WRAPPER_ARRAY:
default: // should never occur but translate as "as-array"
writeStartArray(); // wrapper, not actual array object to write
writeString(idStr);
}
}
// and finally possible start marker for value itself:
if (valueShape == JsonToken.START_OBJECT) {
writeStartObject(typeIdDef.forValue);
} else if (valueShape == JsonToken.START_ARRAY) {
// should we now set the current object?
writeStartArray();
}
return typeIdDef;
}
/*
* @since 2.9
*/
public WritableTypeId writeTypeSuffix(WritableTypeId typeIdDef) throws IOException
{
final JsonToken valueShape = typeIdDef.valueShape;
// First: does value need closing?
if (valueShape == JsonToken.START_OBJECT) {
writeEndObject();
} else if (valueShape == JsonToken.START_ARRAY) {
writeEndArray();
}
if (typeIdDef.wrapperWritten) {
switch (typeIdDef.include) {
case WRAPPER_ARRAY:
writeEndArray();
break;
case PARENT_PROPERTY:
// unusually, need to output AFTER value. And no real wrapper...
{
Object id = typeIdDef.id;
String idStr = (id instanceof String) ? (String) id : String.valueOf(id);
writeStringField(typeIdDef.asProperty, idStr);
}
break;
case METADATA_PROPERTY:
case PAYLOAD_PROPERTY:
// no actual wrapper; included within Object itself
break;
case WRAPPER_OBJECT:
default: // should never occur but...
writeEndObject();
break;
}
}
return typeIdDef;
}
/*
/**********************************************************
/* Public API, write methods, serializing Java objects
/**********************************************************
*/
/**
* Method for writing given Java object (POJO) as Json.
* Exactly how the object gets written depends on object
* in question (ad on codec, its configuration); for most
* beans it will result in JSON Object, but for others JSON
* Array, or String or numeric value (and for nulls, JSON
* null literal.
* NOTE: generator must have its object codec
* set to non-null value; for generators created by a mapping
* factory this is the case, for others not.
*/
public abstract void writeObject(Object pojo) throws IOException;
/**
* Method for writing given JSON tree (expressed as a tree
* where given JsonNode is the root) using this generator.
* This will generally just call
* {@link #writeObject} with given node, but is added
* for convenience and to make code more explicit in cases
* where it deals specifically with trees.
*/
public abstract void writeTree(TreeNode rootNode) throws IOException;
/*
/**********************************************************
/* Public API, convenience field write methods
/**********************************************************
*/
/**
* Convenience method for outputting a field entry ("member")
* that has a String value. Equivalent to:
*
* Note: many performance-sensitive implementations override this method
*/
public void writeStringField(String fieldName, String value) throws IOException {
writeFieldName(fieldName);
writeString(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has a boolean value. Equivalent to:
*
* Note: caller still has to take care to close the array
* (by calling {#link #writeEndArray}) after writing all values
* of the value Array.
*/
public final void writeArrayFieldStart(String fieldName) throws IOException {
writeFieldName(fieldName);
writeStartArray();
}
/**
* Convenience method for outputting a field entry ("member")
* (that will contain an Object value), and the START_OBJECT marker.
* Equivalent to:
*
* Note: caller still has to take care to close the Object
* (by calling {#link #writeEndObject}) after writing all
* entries of the value Object.
*/
public final void writeObjectFieldStart(String fieldName) throws IOException {
writeFieldName(fieldName);
writeStartObject();
}
/**
* Convenience method for outputting a field entry ("member")
* that has contents of specific Java object as its value.
* Equivalent to:
*
* Default implementation does nothing.
*
* @since 2.3
*/
public void writeOmittedField(String fieldName) throws IOException { }
/*
/**********************************************************
/* Public API, copy-through methods
/**********************************************************
*/
/**
* Method for copying contents of the current event that
* the given parser instance points to.
* Note that the method will not copy any other events,
* such as events contained within JSON Array or Object structures.
*
* Calling this method will not advance the given
* parser, although it may cause parser to internally process
* more data (if it lazy loads contents of value events, for example)
*/
public void copyCurrentEvent(JsonParser p) throws IOException
{
JsonToken t = p.currentToken();
final int token = (t == null) ? ID_NOT_AVAILABLE : t.id();
switch (token) {
case ID_NOT_AVAILABLE:
_reportError("No current event to copy");
break; // never gets here
case ID_START_OBJECT:
writeStartObject();
break;
case ID_END_OBJECT:
writeEndObject();
break;
case ID_START_ARRAY:
writeStartArray();
break;
case ID_END_ARRAY:
writeEndArray();
break;
case ID_FIELD_NAME:
writeFieldName(p.getCurrentName());
break;
case ID_STRING:
if (p.hasTextCharacters()) {
writeString(p.getTextCharacters(), p.getTextOffset(), p.getTextLength());
} else {
writeString(p.getText());
}
break;
case ID_NUMBER_INT:
{
NumberType n = p.getNumberType();
if (n == NumberType.INT) {
writeNumber(p.getIntValue());
} else if (n == NumberType.BIG_INTEGER) {
writeNumber(p.getBigIntegerValue());
} else {
writeNumber(p.getLongValue());
}
break;
}
case ID_NUMBER_FLOAT:
{
NumberType n = p.getNumberType();
if (n == NumberType.BIG_DECIMAL) {
writeNumber(p.getDecimalValue());
} else if (n == NumberType.FLOAT) {
writeNumber(p.getFloatValue());
} else {
writeNumber(p.getDoubleValue());
}
break;
}
case ID_TRUE:
writeBoolean(true);
break;
case ID_FALSE:
writeBoolean(false);
break;
case ID_NULL:
writeNull();
break;
case ID_EMBEDDED_OBJECT:
writeObject(p.getEmbeddedObject());
break;
default:
throw new IllegalStateException("Internal error: unknown current token, "+t);
}
}
/**
* Method for copying contents of the current event
* and following events that it encloses
* the given parser instance points to.
*
* So what constitutes enclosing? Here is the list of
* events that have associated enclosed events that will
* get copied:
*
* After calling this method, parser will point to the
* last event that was copied. This will either be
* the event parser already pointed to (if there were no
* enclosed events), or the last enclosed event copied.
*/
public void copyCurrentStructure(JsonParser p) throws IOException
{
JsonToken t = p.currentToken();
// Let's handle field-name separately first
int id = (t == null) ? ID_NOT_AVAILABLE : t.id();
if (id == ID_FIELD_NAME) {
writeFieldName(p.getCurrentName());
t = p.nextToken();
id = (t == null) ? ID_NOT_AVAILABLE : t.id();
// fall-through to copy the associated value
}
switch (id) {
case ID_START_OBJECT:
writeStartObject();
_copyCurrentContents(p);
return;
case ID_START_ARRAY:
writeStartArray();
_copyCurrentContents(p);
return;
default:
copyCurrentEvent(p);
}
}
/**
* @since 2.10
*/
protected void _copyCurrentContents(JsonParser p) throws IOException
{
int depth = 1;
JsonToken t;
// Mostly copied from `copyCurrentEvent()`, but with added nesting counts
while ((t = p.nextToken()) != null) {
switch (t.id()) {
case ID_FIELD_NAME:
writeFieldName(p.getCurrentName());
break;
case ID_START_ARRAY:
writeStartArray();
++depth;
break;
case ID_START_OBJECT:
writeStartObject();
++depth;
break;
case ID_END_ARRAY:
writeEndArray();
if (--depth == 0) {
return;
}
break;
case ID_END_OBJECT:
writeEndObject();
if (--depth == 0) {
return;
}
break;
case ID_STRING:
if (p.hasTextCharacters()) {
writeString(p.getTextCharacters(), p.getTextOffset(), p.getTextLength());
} else {
writeString(p.getText());
}
break;
case ID_NUMBER_INT:
{
NumberType n = p.getNumberType();
if (n == NumberType.INT) {
writeNumber(p.getIntValue());
} else if (n == NumberType.BIG_INTEGER) {
writeNumber(p.getBigIntegerValue());
} else {
writeNumber(p.getLongValue());
}
break;
}
case ID_NUMBER_FLOAT:
{
NumberType n = p.getNumberType();
if (n == NumberType.BIG_DECIMAL) {
writeNumber(p.getDecimalValue());
} else if (n == NumberType.FLOAT) {
writeNumber(p.getFloatValue());
} else {
writeNumber(p.getDoubleValue());
}
break;
}
case ID_TRUE:
writeBoolean(true);
break;
case ID_FALSE:
writeBoolean(false);
break;
case ID_NULL:
writeNull();
break;
case ID_EMBEDDED_OBJECT:
writeObject(p.getEmbeddedObject());
break;
default:
throw new IllegalStateException("Internal error: unknown current token, "+t);
}
}
}
/*
/**********************************************************
/* Public API, context access
/**********************************************************
*/
/**
* @return Context object that can give information about logical
* position within generated json content.
*/
public abstract JsonStreamContext getOutputContext();
/*
/**********************************************************
/* Public API, buffer handling
/**********************************************************
*/
/**
* Method called to flush any buffered content to the underlying
* target (output stream, writer), and to flush the target itself
* as well.
*/
@Override
public abstract void flush() throws IOException;
/**
* Method that can be called to determine whether this generator
* is closed or not. If it is closed, no more output can be done.
*/
public abstract boolean isClosed();
/*
/**********************************************************
/* Closeable implementation
/**********************************************************
*/
/**
* Method called to close this generator, so that no more content
* can be written.
*
* Whether the underlying target (stream, writer) gets closed depends
* on whether this generator either manages the target (i.e. is the
* only one with access to the target -- case if caller passes a
* reference to the resource such as File, but not stream); or
* has feature {@link Feature#AUTO_CLOSE_TARGET} enabled.
* If either of above is true, the target is also closed. Otherwise
* (not managing, feature not enabled), target is not closed.
*/
@Override
public abstract void close() throws IOException;
/*
/**********************************************************
/* Helper methods for sub-classes
/**********************************************************
*/
/**
* Helper method used for constructing and throwing
* {@link JsonGenerationException} with given base message.
*
* Note that sub-classes may override this method to add more detail
* or use a {@link JsonGenerationException} sub-class.
*/
protected void _reportError(String msg) throws JsonGenerationException {
throw new JsonGenerationException(msg, this);
}
protected final void _throwInternal() { VersionUtil.throwInternal(); }
protected void _reportUnsupportedOperation() {
throw new UnsupportedOperationException("Operation not supported by generator of type "+getClass().getName());
}
/**
* @since 2.8
*/
protected final void _verifyOffsets(int arrayLength, int offset, int length)
{
if ((offset < 0) || (offset + length) > arrayLength) {
throw new IllegalArgumentException(String.format(
"invalid argument(s) (offset=%d, length=%d) for input array of %d element",
offset, length, arrayLength));
}
}
/**
* Helper method to try to call appropriate write method for given
* untyped Object. At this point, no structural conversions should be done,
* only simple basic types are to be coerced as necessary.
*
* @param value Non-null value to write
*/
protected void _writeSimpleObject(Object value) throws IOException
{
/* 31-Dec-2009, tatu: Actually, we could just handle some basic
* types even without codec. This can improve interoperability,
* and specifically help with TokenBuffer.
*/
if (value == null) {
writeNull();
return;
}
if (value instanceof String) {
writeString((String) value);
return;
}
if (value instanceof Number) {
Number n = (Number) value;
if (n instanceof Integer) {
writeNumber(n.intValue());
return;
} else if (n instanceof Long) {
writeNumber(n.longValue());
return;
} else if (n instanceof Double) {
writeNumber(n.doubleValue());
return;
} else if (n instanceof Float) {
writeNumber(n.floatValue());
return;
} else if (n instanceof Short) {
writeNumber(n.shortValue());
return;
} else if (n instanceof Byte) {
writeNumber(n.byteValue());
return;
} else if (n instanceof BigInteger) {
writeNumber((BigInteger) n);
return;
} else if (n instanceof BigDecimal) {
writeNumber((BigDecimal) n);
return;
// then Atomic types
} else if (n instanceof AtomicInteger) {
writeNumber(((AtomicInteger) n).get());
return;
} else if (n instanceof AtomicLong) {
writeNumber(((AtomicLong) n).get());
return;
}
} else if (value instanceof byte[]) {
writeBinary((byte[]) value);
return;
} else if (value instanceof Boolean) {
writeBoolean((Boolean) value);
return;
} else if (value instanceof AtomicBoolean) {
writeBoolean(((AtomicBoolean) value).get());
return;
}
throw new IllegalStateException("No ObjectCodec defined for the generator, can only serialize simple wrapper types (type passed "
+value.getClass().getName()+")");
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonLocation.java 0000664 0000000 0000000 00000015650 13561642473 0031613 0 ustar 00root root 0000000 0000000 /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*/
package com.fasterxml.jackson.core;
import java.nio.charset.Charset;
/**
* Object that encapsulates Location information used for reporting
* parsing (or potentially generation) errors, as well as current location
* within input streams.
*/
public class JsonLocation
implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
/**
* Include at most first 500 characters/bytes from contents; should be enough
* to give context, but not cause unfortunate side effects in things like
* logs.
*
* @since 2.9
*/
public static final int MAX_CONTENT_SNIPPET = 500;
/**
* Shared immutable "N/A location" that can be returned to indicate
* that no location information is available.
*
* NOTE: before 2.9, Location was given as String "N/A"; with 2.9 it was
* removed so that source should be indicated as "UNKNOWN".
*/
public final static JsonLocation NA = new JsonLocation(null, -1L, -1L, -1, -1);
protected final long _totalBytes;
protected final long _totalChars;
protected final int _lineNr;
protected final int _columnNr;
/**
* Displayable description for input source: file path, URL.
*
* NOTE:
* NOTE: not added as a "getter" to prevent it from getting serialized.
*
* @since 2.9
*/
public String sourceDescription() {
return _appendSourceDesc(new StringBuilder(100)).toString();
}
/*
/**********************************************************
/* Std method overrides
/**********************************************************
*/
@Override
public int hashCode()
{
int hash = (_sourceRef == null) ? 1 : _sourceRef.hashCode();
hash ^= _lineNr;
hash += _columnNr;
hash ^= (int) _totalChars;
hash += (int) _totalBytes;
return hash;
}
@Override
public boolean equals(Object other)
{
if (other == this) return true;
if (other == null) return false;
if (!(other instanceof JsonLocation)) return false;
JsonLocation otherLoc = (JsonLocation) other;
if (_sourceRef == null) {
if (otherLoc._sourceRef != null) return false;
} else if (!_sourceRef.equals(otherLoc._sourceRef)) return false;
return (_lineNr == otherLoc._lineNr)
&& (_columnNr == otherLoc._columnNr)
&& (_totalChars == otherLoc._totalChars)
&& (getByteOffset() == otherLoc.getByteOffset())
;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder(80);
sb.append("[Source: ");
_appendSourceDesc(sb);
sb.append("; line: ");
sb.append(_lineNr);
sb.append(", column: ");
sb.append(_columnNr);
sb.append(']');
return sb.toString();
}
protected StringBuilder _appendSourceDesc(StringBuilder sb)
{
final Object srcRef = _sourceRef;
if (srcRef == null) {
sb.append("UNKNOWN");
return sb;
}
// First, figure out what name to use as source type
Class> srcType = (srcRef instanceof Class>) ?
((Class>) srcRef) : srcRef.getClass();
String tn = srcType.getName();
// standard JDK types without package
if (tn.startsWith("java.")) {
tn = srcType.getSimpleName();
} else if (srcRef instanceof byte[]) { // then some other special cases
tn = "byte[]";
} else if (srcRef instanceof char[]) {
tn = "char[]";
}
sb.append('(').append(tn).append(')');
// and then, include (part of) contents for selected types:
int len;
String charStr = " chars";
if (srcRef instanceof CharSequence) {
CharSequence cs = (CharSequence) srcRef;
len = cs.length();
len -= _append(sb, cs.subSequence(0, Math.min(len, MAX_CONTENT_SNIPPET)).toString());
} else if (srcRef instanceof char[]) {
char[] ch = (char[]) srcRef;
len = ch.length;
len -= _append(sb, new String(ch, 0, Math.min(len, MAX_CONTENT_SNIPPET)));
} else if (srcRef instanceof byte[]) {
byte[] b = (byte[]) srcRef;
int maxLen = Math.min(b.length, MAX_CONTENT_SNIPPET);
_append(sb, new String(b, 0, maxLen, Charset.forName("UTF-8")));
len = b.length - maxLen;
charStr = " bytes";
} else {
len = 0;
}
if (len > 0) {
sb.append("[truncated ").append(len).append(charStr).append(']');
}
return sb;
}
private int _append(StringBuilder sb, String content) {
sb.append('"').append(content).append('"');
return content.length();
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonParseException.java 0000664 0000000 0000000 00000006116 13561642473 0032771 0 ustar 00root root 0000000 0000000 /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*/
package com.fasterxml.jackson.core;
import com.fasterxml.jackson.core.exc.StreamReadException;
import com.fasterxml.jackson.core.util.RequestPayload;
/**
* Exception type for parsing problems, used when non-well-formed content
* (content that does not conform to JSON syntax as per specification)
* is encountered.
*/
public class JsonParseException extends StreamReadException
{
private static final long serialVersionUID = 2L; // 2.7
@Deprecated // since 2.7
public JsonParseException(String msg, JsonLocation loc) {
super(msg, loc, null);
}
@Deprecated // since 2.7
public JsonParseException(String msg, JsonLocation loc, Throwable root) {
super(msg, loc, root);
}
/**
* Constructor that uses current parsing location as location, and
* sets processor (accessible via {@link #getProcessor()}) to
* specified parser.
*
* @since 2.7
*/
public JsonParseException(JsonParser p, String msg) {
super(p, msg);
}
/**
* @since 2.7
*/
public JsonParseException(JsonParser p, String msg, Throwable root) {
super(p, msg, root);
}
/**
* @since 2.7
*/
public JsonParseException(JsonParser p, String msg, JsonLocation loc) {
super(p, msg, loc);
}
/**
* @since 2.7
*/
public JsonParseException(JsonParser p, String msg, JsonLocation loc, Throwable root) {
super(msg, loc, root);
}
/**
* Fluent method that may be used to assign originating {@link JsonParser},
* to be accessed using {@link #getProcessor()}.
*
* NOTE: `this` instance is modified and no new instance is constructed.
*
* @since 2.7
*/
@Override
public JsonParseException withParser(JsonParser p) {
_processor = p;
return this;
}
/**
* Fluent method that may be used to assign payload to this exception,
* to let recipient access it for diagnostics purposes.
*
* NOTE: `this` instance is modified and no new instance is constructed.
*
* @since 2.8
*/
@Override
public JsonParseException withRequestPayload(RequestPayload p) {
_requestPayload = p;
return this;
}
// NOTE: overloaded in 2.10 just to retain binary compatibility with 2.9 (remove from 3.0)
@Override
public JsonParser getProcessor() {
return super.getProcessor();
}
// NOTE: overloaded in 2.10 just to retain binary compatibility with 2.9 (remove from 3.0)
@Override
public RequestPayload getRequestPayload() {
return super.getRequestPayload();
}
// NOTE: overloaded in 2.10 just to retain binary compatibility with 2.9 (remove from 3.0)
@Override
public String getRequestPayloadAsString() {
return super.getRequestPayloadAsString();
}
// NOTE: overloaded in 2.10 just to retain binary compatibility with 2.9 (remove from 3.0)
@Override
public String getMessage() {
return super.getMessage();
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonParser.java 0000664 0000000 0000000 00000217211 13561642473 0031274 0 ustar 00root root 0000000 0000000 /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*/
package com.fasterxml.jackson.core;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Iterator;
import com.fasterxml.jackson.core.async.NonBlockingInputFeeder;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.core.util.RequestPayload;
/**
* Base class that defines public API for reading JSON content.
* Instances are created using factory methods of
* a {@link JsonFactory} instance.
*
* @author Tatu Saloranta
*/
public abstract class JsonParser
implements Closeable, Versioned
{
private final static int MIN_BYTE_I = (int) Byte.MIN_VALUE;
// as per [JACKSON-804], allow range up to and including 255
private final static int MAX_BYTE_I = (int) 255;
private final static int MIN_SHORT_I = (int) Short.MIN_VALUE;
private final static int MAX_SHORT_I = (int) Short.MAX_VALUE;
/**
* Enumeration of possible "native" (optimal) types that can be
* used for numbers.
*/
public enum NumberType {
INT, LONG, BIG_INTEGER, FLOAT, DOUBLE, BIG_DECIMAL
};
/**
* Enumeration that defines all on/off features for parsers.
*/
public enum Feature {
// // // Low-level I/O handling features:
/**
* Feature that determines whether parser will automatically
* close underlying input source that is NOT owned by the
* parser. If disabled, calling application has to separately
* close the underlying {@link InputStream} and {@link Reader}
* instances used to create the parser. If enabled, parser
* will handle closing, as long as parser itself gets closed:
* this happens when end-of-input is encountered, or parser
* is closed by a call to {@link JsonParser#close}.
*
* Feature is enabled by default.
*/
AUTO_CLOSE_SOURCE(true),
// // // Support for non-standard data format constructs
/**
* Feature that determines whether parser will allow use
* of Java/C++ style comments (both '/'+'*' and
* '//' varieties) within parsed content or not.
*
* Since JSON specification does not mention comments as legal
* construct,
* this is a non-standard feature; however, in the wild
* this is extensively used. As such, feature is
* disabled by default for parsers and must be
* explicitly enabled.
*
* NOTE: while not technically deprecated, since 2.10 recommended to use
* {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_JAVA_COMMENTS} instead.
*/
ALLOW_COMMENTS(false),
/**
* Feature that determines whether parser will allow use
* of YAML comments, ones starting with '#' and continuing
* until the end of the line. This commenting style is common
* with scripting languages as well.
*
* Since JSON specification does not mention comments as legal
* construct,
* this is a non-standard feature. As such, feature is
* disabled by default for parsers and must be
* explicitly enabled.
*
* NOTE: while not technically deprecated, since 2.10 recommended to use
* {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_YAML_COMMENTS} instead.
*/
ALLOW_YAML_COMMENTS(false),
/**
* Feature that determines whether parser will allow use
* of unquoted field names (which is allowed by Javascript,
* but not by JSON specification).
*
* Since JSON specification requires use of double quotes for
* field names,
* this is a non-standard feature, and as such disabled by default.
*
* NOTE: while not technically deprecated, since 2.10 recommended to use
* {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_UNQUOTED_FIELD_NAMES} instead.
*/
ALLOW_UNQUOTED_FIELD_NAMES(false),
/**
* Feature that determines whether parser will allow use
* of single quotes (apostrophe, character '\'') for
* quoting Strings (names and String values). If so,
* this is in addition to other acceptable markers.
* but not by JSON specification).
*
* Since JSON specification requires use of double quotes for
* field names,
* this is a non-standard feature, and as such disabled by default.
*
* NOTE: while not technically deprecated, since 2.10 recommended to use
* {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_SINGLE_QUOTES} instead.
*/
ALLOW_SINGLE_QUOTES(false),
/**
* Feature that determines whether parser will allow
* JSON Strings to contain unquoted control characters
* (ASCII characters with value less than 32, including
* tab and line feed characters) or not.
* If feature is set false, an exception is thrown if such a
* character is encountered.
*
* Since JSON specification requires quoting for all control characters,
* this is a non-standard feature, and as such disabled by default.
*
* @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_UNESCAPED_CONTROL_CHARS} instead
*/
@Deprecated
ALLOW_UNQUOTED_CONTROL_CHARS(false),
/**
* Feature that can be enabled to accept quoting of all character
* using backslash quoting mechanism: if not enabled, only characters
* that are explicitly listed by JSON specification can be thus
* escaped (see JSON spec for small list of these characters)
*
* Since JSON specification requires quoting for all control characters,
* this is a non-standard feature, and as such disabled by default.
*
* @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER} instead
*/
@Deprecated
ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(false),
/**
* Feature that determines whether parser will allow
* JSON integral numbers to start with additional (ignorable)
* zeroes (like: 000001). If enabled, no exception is thrown, and extra
* nulls are silently ignored (and not included in textual representation
* exposed via {@link JsonParser#getText}).
*
* Since JSON specification does not allow leading zeroes,
* this is a non-standard feature, and as such disabled by default.
*
* @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_LEADING_ZEROS_FOR_NUMBERS} instead
*/
@Deprecated
ALLOW_NUMERIC_LEADING_ZEROS(false),
/**
* Feature that allows parser to recognize set of
* "Not-a-Number" (NaN) tokens as legal floating number
* values (similar to how many other data formats and
* programming language source code allows it).
* Specific subset contains values that
* XML Schema
* (see section 3.2.4.1, Lexical Representation)
* allows (tokens are quoted contents, not including quotes):
*
* Since JSON specification does not allow use of such values,
* this is a non-standard feature, and as such disabled by default.
*
* @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_NON_NUMERIC_NUMBERS} instead
*/
@Deprecated
ALLOW_NON_NUMERIC_NUMBERS(false),
/**
* Feature allows the support for "missing" values in a JSON array: missing
* value meaning sequence of two commas, without value in-between but only
* optional white space.
* Enabling this feature will expose "missing" values as {@link JsonToken#VALUE_NULL}
* tokens, which typically become Java nulls in arrays and {@link java.util.Collection}
* in data-binding.
*
* For example, enabling this feature will represent a JSON array
* Since the JSON specification does not allow missing values this is a non-compliant JSON
* feature and is disabled by default.
*
* @since 2.8
*
* @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_MISSING_VALUES} instead
*/
@Deprecated
ALLOW_MISSING_VALUES(false),
/**
* Feature that determines whether {@link JsonParser} will allow for a single trailing
* comma following the final value (in an Array) or member (in an Object). These commas
* will simply be ignored.
*
* For example, when this feature is enabled,
* When combined with
* Since the JSON specification does not permit trailing commas, this is a non-standard
* feature, and as such disabled by default.
*
* @since 2.9
*
* @deprecated Since 2.10 use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_TRAILING_COMMA} instead
*/
@Deprecated
ALLOW_TRAILING_COMMA(false),
// // // Validity checks
/**
* Feature that determines whether {@link JsonParser} will explicitly
* check that no duplicate JSON Object field names are encountered.
* If enabled, parser will check all names within context and report
* duplicates by throwing a {@link JsonParseException}; if disabled,
* parser will not do such checking. Assumption in latter case is
* that caller takes care of handling duplicates at a higher level:
* data-binding, for example, has features to specify detection to
* be done there.
*
* Note that enabling this feature will incur performance overhead
* due to having to store and check additional information: this typically
* adds 20-30% to execution time for basic parsing.
*
* @since 2.3
*/
STRICT_DUPLICATE_DETECTION(false),
/**
* Feature that determines what to do if the underlying data format requires knowledge
* of all properties to decode (usually via a Schema), and if no definition is
* found for a property that input content contains.
* Typically most textual data formats do NOT require schema information (although
* some do, such as CSV), whereas many binary data formats do require definitions
* (such as Avro, protobuf), although not all (Smile, CBOR, BSON and MessagePack do not).
* Further note that some formats that do require schema information will not be able
* to ignore undefined properties: for example, Avro is fully positional and there is
* no possibility of undefined data. This leaves formats like Protobuf that have identifiers
* that may or may not map; and as such Protobuf format does make use of this feature.
*
* Note that support for this feature is implemented by individual data format
* module, if (and only if) it makes sense for the format in question. For JSON,
* for example, this feature has no effect as properties need not be pre-defined.
*
* Feature is disabled by default, meaning that if the underlying data format
* requires knowledge of all properties to output, attempts to read an unknown
* property will result in a {@link JsonProcessingException}
*
* @since 2.6
*/
IGNORE_UNDEFINED(false),
// // // Other
/**
* Feature that determines whether {@link JsonLocation} instances should be constructed
* with reference to source or not. If source reference is included, its type and contents
* are included when `toString()` method is called (most notably when printing out parse
* exception with that location information). If feature is disabled, no source reference
* is passed and source is only indicated as "UNKNOWN".
*
* Most common reason for disabling this feature is to avoid leaking information about
* internal information; this may be done for security reasons.
* Note that even if source reference is included, only parts of contents are usually
* printed, and not the whole contents. Further, many source reference types can not
* necessarily access contents (like streams), so only type is indicated, not contents.
*
* Feature is enabled by default, meaning that "source reference" information is passed
* and some or all of the source content may be included in {@link JsonLocation} information
* constructed either when requested explicitly, or when needed for an exception.
*
* @since 2.9
*/
INCLUDE_SOURCE_IN_LOCATION(true),
;
/**
* Whether feature is enabled or disabled by default.
*/
private final boolean _defaultState;
private final int _mask;
/**
* Method that calculates bit set (flags) of all features that
* are enabled by default.
*/
public static int collectDefaults()
{
int flags = 0;
for (Feature f : values()) {
if (f.enabledByDefault()) {
flags |= f.getMask();
}
}
return flags;
}
private Feature(boolean defaultState) {
_mask = (1 << ordinal());
_defaultState = defaultState;
}
public boolean enabledByDefault() { return _defaultState; }
/**
* @since 2.3
*/
public boolean enabledIn(int flags) { return (flags & _mask) != 0; }
public int getMask() { return _mask; }
}
/*
/**********************************************************
/* Minimal configuration state
/**********************************************************
*/
/**
* Bit flag composed of bits that indicate which
* {@link com.fasterxml.jackson.core.JsonParser.Feature}s
* are enabled.
*/
protected int _features;
/**
* Optional container that holds the request payload which will be displayed on JSON parsing error.
*
* @since 2.8
*/
protected transient RequestPayload _requestPayload;
/*
/**********************************************************
/* Construction, configuration, initialization
/**********************************************************
*/
protected JsonParser() { }
protected JsonParser(int features) { _features = features; }
/**
* Accessor for {@link ObjectCodec} associated with this
* parser, if any. Codec is used by {@link #readValueAs(Class)}
* method (and its variants).
*/
public abstract ObjectCodec getCodec();
/**
* Setter that allows defining {@link ObjectCodec} associated with this
* parser, if any. Codec is used by {@link #readValueAs(Class)}
* method (and its variants).
*/
public abstract void setCodec(ObjectCodec c);
/**
* Method that can be used to get access to object that is used
* to access input being parsed; this is usually either
* {@link InputStream} or {@link Reader}, depending on what
* parser was constructed with.
* Note that returned value may be null in some cases; including
* case where parser implementation does not want to exposed raw
* source to caller.
* In cases where input has been decorated, object returned here
* is the decorated version; this allows some level of interaction
* between users of parser and decorator object.
*
* In general use of this accessor should be considered as
* "last effort", i.e. only used if no other mechanism is applicable.
*/
public Object getInputSource() { return null; }
/**
* Helper method, usually equivalent to:
*
* Note that "current value" is NOT populated (or used) by Streaming parser;
* it is only used by higher-level data-binding functionality.
* The reason it is included here is that it can be stored and accessed hierarchically,
* and gets passed through data-binding.
*
* @since 2.5
*/
public Object getCurrentValue() {
JsonStreamContext ctxt = getParsingContext();
return (ctxt == null) ? null : ctxt.getCurrentValue();
}
/**
* Helper method, usually equivalent to:
*
* If parser does not support specified schema, {@link UnsupportedOperationException}
* is thrown.
*
* @param schema Schema to use
*
* @throws UnsupportedOperationException if parser does not support schema
*/
public void setSchema(FormatSchema schema) {
throw new UnsupportedOperationException("Parser of type "+getClass().getName()+" does not support schema of type '"
+schema.getSchemaType()+"'");
}
/**
* Method for accessing Schema that this parser uses, if any.
* Default implementation returns null.
*
* @since 2.1
*/
public FormatSchema getSchema() { return null; }
/**
* Method that can be used to verify that given schema can be used with
* this parser (using {@link #setSchema}).
*
* @param schema Schema to check
*
* @return True if this parser can use given schema; false if not
*/
public boolean canUseSchema(FormatSchema schema) { return false; }
/*
/**********************************************************
/* Capability introspection
/**********************************************************
*/
/**
* Method that can be called to determine if a custom
* {@link ObjectCodec} is needed for binding data parsed
* using {@link JsonParser} constructed by this factory
* (which typically also implies the same for serialization
* with {@link JsonGenerator}).
*
* @return True if custom codec is needed with parsers and
* generators created by this factory; false if a general
* {@link ObjectCodec} is enough
*
* @since 2.1
*/
public boolean requiresCustomCodec() { return false;}
/**
* Method that can be called to determine if this parser instance
* uses non-blocking ("asynchronous") input access for decoding or not.
* Access mode is determined by earlier calls via {@link JsonFactory};
* it may not be changed after construction.
*
* If non-blocking decoding is u (@code true}, it is possible to call
* {@link #getNonBlockingInputFeeder()} to obtain object to use
* for feeding input; otherwise (
* Default implementation will simply throw an exception to indicate that
* the generator implementation does not support any {@link FormatFeature}s.
*
* @param values Bit mask of set/clear state for features to change
* @param mask Bit mask of features to change
*
* @since 2.6
*/
public JsonParser overrideFormatFeatures(int values, int mask) {
// 08-Oct-2018, tatu: For 2.10 we actually do get `JsonReadFeature`s, although they
// are (for 2.x only, not for 3.x) mapper to legacy settings. So do not freak out:
// throw new IllegalArgumentException("No FormatFeatures defined for parser of type "+getClass().getName());
return this;
}
/*
/**********************************************************
/* Public API, traversal
/**********************************************************
*/
/**
* Main iteration method, which will advance stream enough
* to determine type of the next token, if any. If none
* remaining (stream has no content other than possible
* white space before ending), null will be returned.
*
* @return Next token from the stream, if any found, or null
* to indicate end-of-input
*/
public abstract JsonToken nextToken() throws IOException;
/**
* Iteration method that will advance stream enough
* to determine type of the next token that is a value type
* (including JSON Array and Object start/end markers).
* Or put another way, nextToken() will be called once,
* and if {@link JsonToken#FIELD_NAME} is returned, another
* time to get the value for the field.
* Method is most useful for iterating over value entries
* of JSON objects; field name will still be available
* by calling {@link #getCurrentName} when parser points to
* the value.
*
* @return Next non-field-name token from the stream, if any found,
* or null to indicate end-of-input (or, for non-blocking
* parsers, {@link JsonToken#NOT_AVAILABLE} if no tokens were
* available yet)
*/
public abstract JsonToken nextValue() throws IOException;
/**
* Method that fetches next token (as if calling {@link #nextToken}) and
* verifies whether it is {@link JsonToken#FIELD_NAME} with specified name
* and returns result of that comparison.
* It is functionally equivalent to:
*
* Note that for many dataformat implementations this method
* will not do anything; this is the default implementation unless
* overridden by sub-classes.
*
* @since 2.8
*/
public void finishToken() throws IOException {
; // nothing
}
/*
/**********************************************************
/* Public API, simple token id/type access
/**********************************************************
*/
/**
* Accessor to find which token parser currently points to, if any;
* null will be returned if none.
* If return value is non-null, data associated with the token
* is available via other accessor methods.
*
* @return Type of the token this parser currently points to,
* if any: null before any tokens have been read, and
* after end-of-input has been encountered, as well as
* if the current token has been explicitly cleared.
*
* @since 2.8
*/
public JsonToken currentToken() {
return getCurrentToken();
}
/**
* Method similar to {@link #getCurrentToken()} but that returns an
*
* Use of int directly is typically more efficient on switch statements,
* so this method may be useful when building low-overhead codecs.
* Note, however, that effect may not be big enough to matter: make sure
* to profile performance before deciding to use this method.
*
* @since 2.8
*
* @return
* Note that no traversal or conversion is performed; so in some
* cases calling method like {@link #isExpectedStartArrayToken()}
* is necessary instead.
*
* @since 2.5
*/
public abstract boolean hasTokenId(int id);
/**
* Method that is functionally equivalent to:
*
* Note that no traversal or conversion is performed; so in some
* cases calling method like {@link #isExpectedStartArrayToken()}
* is necessary instead.
*
* @since 2.6
*/
public abstract boolean hasToken(JsonToken t);
/**
* Specialized accessor that can be used to verify that the current
* token indicates start array (usually meaning that current token
* is {@link JsonToken#START_ARRAY}) when start array is expected.
* For some specialized parsers this can return true for other cases
* as well; this is usually done to emulate arrays in cases underlying
* format is ambiguous (XML, for example, has no format-level difference
* between Objects and Arrays; it just has elements).
*
* Default implementation is equivalent to:
*
* Method was added to be used by the optional data binder, since
* it has to be able to consume last token used for binding (so that
* it will not be used again).
*/
public abstract void clearCurrentToken();
/**
* Method that can be called to get the last token that was
* cleared using {@link #clearCurrentToken}. This is not necessarily
* the latest token read.
* Will return null if no tokens have been cleared,
* or if parser has been closed.
*/
public abstract JsonToken getLastClearedToken();
/**
* Method that can be used to change what is considered to be
* the current (field) name.
* May be needed to support non-JSON data formats or unusual binding
* conventions; not needed for typical processing.
*
* Note that use of this method should only be done as sort of last
* resort, as it is a work-around for regular operation.
*
* @param name Name to use as the current name; may be null.
*/
public abstract void overrideCurrentName(String name);
/*
/**********************************************************
/* Public API, access to token information, text
/**********************************************************
*/
/**
* Method that can be called to get the name associated with
* the current token: for {@link JsonToken#FIELD_NAME}s it will
* be the same as what {@link #getText} returns;
* for field values it will be preceding field name;
* and for others (array values, root-level values) null.
*/
public abstract String getCurrentName() throws IOException;
// 15-Dec-2017, tatu: Forward-looking, added in 2.9.4 (and officially in 3.0)
// to smooth upgrading
public String currentName() throws IOException {
return getCurrentName();
}
/**
* Method for accessing textual representation of the current token;
* if no current token (before first call to {@link #nextToken}, or
* after encountering end-of-input), returns null.
* Method can be called for any token type.
*/
public abstract String getText() throws IOException;
/**
* Method to read the textual representation of the current token in chunks and
* pass it to the given Writer.
* Conceptually same as calling:
*
* Note that caller MUST NOT modify the returned
* character array in any way -- doing so may corrupt
* current parser state and render parser instance useless.
*
* The only reason to call this method (over {@link #getText})
* is to avoid construction of a String object (which
* will make a copy of contents).
*/
public abstract char[] getTextCharacters() throws IOException;
/**
* Accessor used with {@link #getTextCharacters}, to know length
* of String stored in returned buffer.
*
* @return Number of characters within buffer returned
* by {@link #getTextCharacters} that are part of
* textual content of the current token.
*/
public abstract int getTextLength() throws IOException;
/**
* Accessor used with {@link #getTextCharacters}, to know offset
* of the first text content character within buffer.
*
* @return Offset of the first character within buffer returned
* by {@link #getTextCharacters} that is part of
* textual content of the current token.
*/
public abstract int getTextOffset() throws IOException;
/**
* Method that can be used to determine whether calling of
* {@link #getTextCharacters} would be the most efficient
* way to access textual content for the event parser currently
* points to.
*
* Default implementation simply returns false since only actual
* implementation class has knowledge of its internal buffering
* state.
* Implementations are strongly encouraged to properly override
* this method, to allow efficient copying of content by other
* code.
*
* @return True if parser currently has character array that can
* be efficiently returned via {@link #getTextCharacters}; false
* means that it may or may not exist
*/
public abstract boolean hasTextCharacters();
/*
/**********************************************************
/* Public API, access to token information, numeric
/**********************************************************
*/
/**
* Generic number value accessor method that will work for
* all kinds of numeric values. It will return the optimal
* (simplest/smallest possible) wrapper object that can
* express the numeric value just parsed.
*/
public abstract Number getNumberValue() throws IOException;
/**
* If current token is of type
* {@link JsonToken#VALUE_NUMBER_INT} or
* {@link JsonToken#VALUE_NUMBER_FLOAT}, returns
* one of {@link NumberType} constants; otherwise returns null.
*/
public abstract NumberType getNumberType() throws IOException;
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_INT} and
* it can be expressed as a value of Java byte primitive type.
* It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT};
* if so, it is equivalent to calling {@link #getDoubleValue}
* and then casting; except for possible overflow/underflow
* exception.
*
* Note: if the resulting integer value falls outside range of
* Java byte, a {@link JsonParseException}
* will be thrown to indicate numeric overflow/underflow.
*/
public byte getByteValue() throws IOException {
int value = getIntValue();
// So far so good: but does it fit?
// [JACKSON-804]: Let's actually allow range of [-128, 255], as those are uniquely mapped
// (instead of just signed range of [-128, 127])
if (value < MIN_BYTE_I || value > MAX_BYTE_I) {
throw _constructError("Numeric value ("+getText()+") out of range of Java byte");
}
return (byte) value;
}
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_INT} and
* it can be expressed as a value of Java short primitive type.
* It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT};
* if so, it is equivalent to calling {@link #getDoubleValue}
* and then casting; except for possible overflow/underflow
* exception.
*
* Note: if the resulting integer value falls outside range of
* Java short, a {@link JsonParseException}
* will be thrown to indicate numeric overflow/underflow.
*/
public short getShortValue() throws IOException
{
int value = getIntValue();
if (value < MIN_SHORT_I || value > MAX_SHORT_I) {
throw _constructError("Numeric value ("+getText()+") out of range of Java short");
}
return (short) value;
}
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_INT} and
* it can be expressed as a value of Java int primitive type.
* It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT};
* if so, it is equivalent to calling {@link #getDoubleValue}
* and then casting; except for possible overflow/underflow
* exception.
*
* Note: if the resulting integer value falls outside range of
* Java int, a {@link JsonParseException}
* may be thrown to indicate numeric overflow/underflow.
*/
public abstract int getIntValue() throws IOException;
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_INT} and
* it can be expressed as a Java long primitive type.
* It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT};
* if so, it is equivalent to calling {@link #getDoubleValue}
* and then casting to int; except for possible overflow/underflow
* exception.
*
* Note: if the token is an integer, but its value falls
* outside of range of Java long, a {@link JsonParseException}
* may be thrown to indicate numeric overflow/underflow.
*/
public abstract long getLongValue() throws IOException;
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_INT} and
* it can not be used as a Java long primitive type due to its
* magnitude.
* It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT};
* if so, it is equivalent to calling {@link #getDecimalValue}
* and then constructing a {@link BigInteger} from that value.
*/
public abstract BigInteger getBigIntegerValue() throws IOException;
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_FLOAT} and
* it can be expressed as a Java float primitive type.
* It can also be called for {@link JsonToken#VALUE_NUMBER_INT};
* if so, it is equivalent to calling {@link #getLongValue}
* and then casting; except for possible overflow/underflow
* exception.
*
* Note: if the value falls
* outside of range of Java float, a {@link JsonParseException}
* will be thrown to indicate numeric overflow/underflow.
*/
public abstract float getFloatValue() throws IOException;
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_FLOAT} and
* it can be expressed as a Java double primitive type.
* It can also be called for {@link JsonToken#VALUE_NUMBER_INT};
* if so, it is equivalent to calling {@link #getLongValue}
* and then casting; except for possible overflow/underflow
* exception.
*
* Note: if the value falls
* outside of range of Java double, a {@link JsonParseException}
* will be thrown to indicate numeric overflow/underflow.
*/
public abstract double getDoubleValue() throws IOException;
/**
* Numeric accessor that can be called when the current
* token is of type {@link JsonToken#VALUE_NUMBER_FLOAT} or
* {@link JsonToken#VALUE_NUMBER_INT}. No under/overflow exceptions
* are ever thrown.
*/
public abstract BigDecimal getDecimalValue() throws IOException;
/*
/**********************************************************
/* Public API, access to token information, other
/**********************************************************
*/
/**
* Convenience accessor that can be called when the current
* token is {@link JsonToken#VALUE_TRUE} or
* {@link JsonToken#VALUE_FALSE}.
*
* Note: if the token is not of above-mentioned boolean types,
an integer, but its value falls
* outside of range of Java long, a {@link JsonParseException}
* may be thrown to indicate numeric overflow/underflow.
*/
public boolean getBooleanValue() throws IOException {
JsonToken t = currentToken();
if (t == JsonToken.VALUE_TRUE) return true;
if (t == JsonToken.VALUE_FALSE) return false;
throw new JsonParseException(this,
String.format("Current token (%s) not of boolean type", t))
.withRequestPayload(_requestPayload);
}
/**
* Accessor that can be called if (and only if) the current token
* is {@link JsonToken#VALUE_EMBEDDED_OBJECT}. For other token types,
* null is returned.
*
* Note: only some specialized parser implementations support
* embedding of objects (usually ones that are facades on top
* of non-streaming sources, such as object trees). One exception
* is access to binary content (whether via base64 encoding or not)
* which typically is accessible using this method, as well as
* {@link #getBinaryValue()}.
*/
public Object getEmbeddedObject() throws IOException { return null; }
/*
/**********************************************************
/* Public API, access to token information, binary
/**********************************************************
*/
/**
* Method that can be used to read (and consume -- results
* may not be accessible using other methods after the call)
* base64-encoded binary data
* included in the current textual JSON value.
* It works similar to getting String value via {@link #getText}
* and decoding result (except for decoding part),
* but should be significantly more performant.
*
* Note that non-decoded textual contents of the current token
* are not guaranteed to be accessible after this method
* is called. Current implementation, for example, clears up
* textual content during decoding.
* Decoded binary content, however, will be retained until
* parser is advanced to the next event.
*
* @param bv Expected variant of base64 encoded
* content (see {@link Base64Variants} for definitions
* of "standard" variants).
*
* @return Decoded binary data
*/
public abstract byte[] getBinaryValue(Base64Variant bv) throws IOException;
/**
* Convenience alternative to {@link #getBinaryValue(Base64Variant)}
* that defaults to using
* {@link Base64Variants#getDefaultVariant} as the default encoding.
*/
public byte[] getBinaryValue() throws IOException {
return getBinaryValue(Base64Variants.getDefaultVariant());
}
/**
* Method that can be used as an alternative to {@link #getBigIntegerValue()},
* especially when value can be large. The main difference (beyond method
* of returning content using {@link OutputStream} instead of as byte array)
* is that content will NOT remain accessible after method returns: any content
* processed will be consumed and is not buffered in any way. If caller needs
* buffering, it has to implement it.
*
* @param out Output stream to use for passing decoded binary data
*
* @return Number of bytes that were decoded and written via {@link OutputStream}
*
* @since 2.1
*/
public int readBinaryValue(OutputStream out) throws IOException {
return readBinaryValue(Base64Variants.getDefaultVariant(), out);
}
/**
* Similar to {@link #readBinaryValue(OutputStream)} but allows explicitly
* specifying base64 variant to use.
*
* @param bv base64 variant to use
* @param out Output stream to use for passing decoded binary data
*
* @return Number of bytes that were decoded and written via {@link OutputStream}
*
* @since 2.1
*/
public int readBinaryValue(Base64Variant bv, OutputStream out) throws IOException {
_reportUnsupportedOperation();
return 0; // never gets here
}
/*
/**********************************************************
/* Public API, access to token information, coercion/conversion
/**********************************************************
*/
/**
* Method that will try to convert value of current token to a
* int.
* Numbers are coerced using default Java rules; booleans convert to 0 (false)
* and 1 (true), and Strings are parsed using default Java language integer
* parsing rules.
*
* If representation can not be converted to an int (including structured type
* markers like start/end Object/Array)
* default value of 0 will be returned; no exceptions are thrown.
*/
public int getValueAsInt() throws IOException {
return getValueAsInt(0);
}
/**
* Method that will try to convert value of current token to a
* int.
* Numbers are coerced using default Java rules; booleans convert to 0 (false)
* and 1 (true), and Strings are parsed using default Java language integer
* parsing rules.
*
* If representation can not be converted to an int (including structured type
* markers like start/end Object/Array)
* specified def will be returned; no exceptions are thrown.
*/
public int getValueAsInt(int def) throws IOException { return def; }
/**
* Method that will try to convert value of current token to a
* long.
* Numbers are coerced using default Java rules; booleans convert to 0 (false)
* and 1 (true), and Strings are parsed using default Java language integer
* parsing rules.
*
* If representation can not be converted to a long (including structured type
* markers like start/end Object/Array)
* default value of 0L will be returned; no exceptions are thrown.
*/
public long getValueAsLong() throws IOException {
return getValueAsLong(0);
}
/**
* Method that will try to convert value of current token to a
* long.
* Numbers are coerced using default Java rules; booleans convert to 0 (false)
* and 1 (true), and Strings are parsed using default Java language integer
* parsing rules.
*
* If representation can not be converted to a long (including structured type
* markers like start/end Object/Array)
* specified def will be returned; no exceptions are thrown.
*/
public long getValueAsLong(long def) throws IOException {
return def;
}
/**
* Method that will try to convert value of current token to a Java
* double.
* Numbers are coerced using default Java rules; booleans convert to 0.0 (false)
* and 1.0 (true), and Strings are parsed using default Java language floating
* point parsing rules.
*
* If representation can not be converted to a double (including structured types
* like Objects and Arrays),
* default value of 0.0 will be returned; no exceptions are thrown.
*/
public double getValueAsDouble() throws IOException {
return getValueAsDouble(0.0);
}
/**
* Method that will try to convert value of current token to a
* Java double.
* Numbers are coerced using default Java rules; booleans convert to 0.0 (false)
* and 1.0 (true), and Strings are parsed using default Java language floating
* point parsing rules.
*
* If representation can not be converted to a double (including structured types
* like Objects and Arrays),
* specified def will be returned; no exceptions are thrown.
*/
public double getValueAsDouble(double def) throws IOException {
return def;
}
/**
* Method that will try to convert value of current token to a
* boolean.
* JSON booleans map naturally; integer numbers other than 0 map to true, and
* 0 maps to false
* and Strings 'true' and 'false' map to corresponding values.
*
* If representation can not be converted to a boolean value (including structured types
* like Objects and Arrays),
* default value of false will be returned; no exceptions are thrown.
*/
public boolean getValueAsBoolean() throws IOException {
return getValueAsBoolean(false);
}
/**
* Method that will try to convert value of current token to a
* boolean.
* JSON booleans map naturally; integer numbers other than 0 map to true, and
* 0 maps to false
* and Strings 'true' and 'false' map to corresponding values.
*
* If representation can not be converted to a boolean value (including structured types
* like Objects and Arrays),
* specified def will be returned; no exceptions are thrown.
*/
public boolean getValueAsBoolean(boolean def) throws IOException {
return def;
}
/**
* Method that will try to convert value of current token to a
* {@link java.lang.String}.
* JSON Strings map naturally; scalar values get converted to
* their textual representation.
* If representation can not be converted to a String value (including structured types
* like Objects and Arrays and null token), default value of
* null will be returned; no exceptions are thrown.
*
* @since 2.1
*/
public String getValueAsString() throws IOException {
return getValueAsString(null);
}
/**
* Method that will try to convert value of current token to a
* {@link java.lang.String}.
* JSON Strings map naturally; scalar values get converted to
* their textual representation.
* If representation can not be converted to a String value (including structured types
* like Objects and Arrays and null token), specified default value
* will be returned; no exceptions are thrown.
*
* @since 2.1
*/
public abstract String getValueAsString(String def) throws IOException;
/*
/**********************************************************
/* Public API, Native Ids (type, object)
/**********************************************************
*/
/**
* Introspection method that may be called to see if the underlying
* data format supports some kind of Object Ids natively (many do not;
* for example, JSON doesn't).
*
* Default implementation returns true; overridden by data formats
* that do support native Object Ids. Caller is expected to either
* use a non-native notation (explicit property or such), or fail,
* in case it can not use native object ids.
*
* @since 2.3
*/
public boolean canReadObjectId() { return false; }
/**
* Introspection method that may be called to see if the underlying
* data format supports some kind of Type Ids natively (many do not;
* for example, JSON doesn't).
*
* Default implementation returns true; overridden by data formats
* that do support native Type Ids. Caller is expected to either
* use a non-native notation (explicit property or such), or fail,
* in case it can not use native type ids.
*
* @since 2.3
*/
public boolean canReadTypeId() { return false; }
/**
* Method that can be called to check whether current token
* (one that was just read) has an associated Object id, and if
* so, return it.
* Note that while typically caller should check with {@link #canReadObjectId}
* first, it is not illegal to call this method even if that method returns
* true; but if so, it will return null. This may be used to simplify calling
* code.
*
* Default implementation will simply return null.
*
* @since 2.3
*/
public Object getObjectId() throws IOException { return null; }
/**
* Method that can be called to check whether current token
* (one that was just read) has an associated type id, and if
* so, return it.
* Note that while typically caller should check with {@link #canReadTypeId}
* first, it is not illegal to call this method even if that method returns
* true; but if so, it will return null. This may be used to simplify calling
* code.
*
* Default implementation will simply return null.
*
* @since 2.3
*/
public Object getTypeId() throws IOException { return null; }
/*
/**********************************************************
/* Public API, optional data binding functionality
/**********************************************************
*/
/**
* Method to deserialize JSON content into a non-container
* type (it can be an array type, however): typically a bean, array
* or a wrapper type (like {@link java.lang.Boolean}).
* Note: method can only be called if the parser has
* an object codec assigned; this is true for parsers constructed
* by
* 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
* 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
* Instances are fully immutable and can be cached, shared between threads.
*
* @author Tatu Saloranta
*
* @since 2.3
*/
public class JsonPointer
{
/**
* Character used to separate segments.
*
* @since 2.9
*/
public final static char SEPARATOR = '/';
/**
* Marker instance used to represent segment that matches current
* node or position (that is, returns true for
* {@link #matches()}).
*/
protected final static JsonPointer EMPTY = new JsonPointer();
/**
* Reference to rest of the pointer beyond currently matching
* segment (if any); null if this pointer refers to the matching
* segment.
*/
protected final JsonPointer _nextSegment;
/**
* Reference from currently matching segment (if any) to node
* before leaf.
* Lazily constructed if/as needed.
*
* NOTE: we'll use `volatile` here assuming that this is unlikely to
* become a performance bottleneck. If it becomes one we can probably
* just drop it and things still should work (despite warnings as per JMM
* regarding visibility (and lack thereof) of unguarded changes).
*
* @since 2.5
*/
protected volatile JsonPointer _head;
/**
* We will retain representation of the pointer, as a String,
* so that {@link #toString} should be as efficient as possible.
*/
protected final String _asString;
protected final String _matchingPropertyName;
protected final int _matchingElementIndex;
/*
/**********************************************************
/* Construction
/**********************************************************
*/
/**
* Constructor used for creating "empty" instance, used to represent
* state that matches current node.
*/
protected JsonPointer() {
_nextSegment = null;
_matchingPropertyName = "";
_matchingElementIndex = -1;
_asString = "";
}
/**
* Constructor used for creating non-empty Segments
*/
protected JsonPointer(String fullString, String segment, JsonPointer next) {
_asString = fullString;
_nextSegment = next;
// Ok; may always be a property
_matchingPropertyName = segment;
// but could be an index, if parsable
_matchingElementIndex = _parseIndex(segment);
}
/**
* @since 2.5
*/
protected JsonPointer(String fullString, String segment, int matchIndex, JsonPointer next) {
_asString = fullString;
_nextSegment = next;
_matchingPropertyName = segment;
_matchingElementIndex = matchIndex;
}
/*
/**********************************************************
/* Factory methods
/**********************************************************
*/
/**
* Factory method that parses given input and construct matching pointer
* instance, if it represents a valid JSON Pointer: if not, a
* {@link IllegalArgumentException} is thrown.
*
* @throws IllegalArgumentException Thrown if the input does not present a valid JSON Pointer
* expression: currently the only such expression is one that does NOT start with
* a slash ('/').
*/
public static JsonPointer compile(String input) throws IllegalArgumentException
{
// First quick checks for well-known 'empty' pointer
if ((input == null) || input.length() == 0) {
return EMPTY;
}
// And then quick validity check:
if (input.charAt(0) != '/') {
throw new IllegalArgumentException("Invalid input: JSON Pointer expression must start with '/': "+"\""+input+"\"");
}
return _parseTail(input);
}
/**
* Alias for {@link #compile}; added to make instances automatically
* deserializable by Jackson databind.
*/
public static JsonPointer valueOf(String input) { return compile(input); }
/**
* Accessor for an "empty" expression, that is, one you can get by
* calling {@link #compile} with "" (empty String).
*
* NOTE: this is different from expression for {@code "/"} which would
* instead match Object node property with empty String ("") as name.
*
* @since 2.10
*/
public static JsonPointer empty() { return EMPTY; }
/**
* Factory method that will construct a pointer instance that describes
* path to location given {@link JsonStreamContext} points to.
*
* @param context Context to build pointer expression fot
* @param includeRoot Whether to include number offset for virtual "root context"
* or not.
*
* @since 2.9
*/
public static JsonPointer forPath(JsonStreamContext context,
boolean includeRoot)
{
// First things first: last segment may be for START_ARRAY/START_OBJECT,
// in which case it does not yet point to anything, and should be skipped
if (context == null) {
return EMPTY;
}
if (!context.hasPathSegment()) {
// one special case; do not prune root if we need it
if (!(includeRoot && context.inRoot() && context.hasCurrentIndex())) {
context = context.getParent();
}
}
JsonPointer tail = null;
for (; context != null; context = context.getParent()) {
if (context.inObject()) {
String seg = context.getCurrentName();
if (seg == null) { // is this legal?
seg = "";
}
tail = new JsonPointer(_fullPath(tail, seg), seg, tail);
} else if (context.inArray() || includeRoot) {
int ix = context.getCurrentIndex();
String ixStr = String.valueOf(ix);
tail = new JsonPointer(_fullPath(tail, ixStr), ixStr, ix, tail);
}
// NOTE: this effectively drops ROOT node(s); should have 1 such node,
// as the last one, but we don't have to care (probably some paths have
// no root, for example)
}
if (tail == null) {
return EMPTY;
}
return tail;
}
private static String _fullPath(JsonPointer tail, String segment)
{
if (tail == null) {
StringBuilder sb = new StringBuilder(segment.length()+1);
sb.append('/');
_appendEscaped(sb, segment);
return sb.toString();
}
String tailDesc = tail._asString;
StringBuilder sb = new StringBuilder(segment.length() + 1 + tailDesc.length());
sb.append('/');
_appendEscaped(sb, segment);
sb.append(tailDesc);
return sb.toString();
}
private static void _appendEscaped(StringBuilder sb, String segment)
{
for (int i = 0, end = segment.length(); i < end; ++i) {
char c = segment.charAt(i);
if (c == '/') {
sb.append("~1");
continue;
}
if (c == '~') {
sb.append("~0");
continue;
}
sb.append(c);
}
}
/* Factory method that composes a pointer instance, given a set
* of 'raw' segments: raw meaning that no processing will be done,
* no escaping may is present.
*
* @param segments
*
* @return Constructed path instance
*/
/* TODO!
public static JsonPointer fromSegment(String... segments)
{
if (segments.length == 0) {
return EMPTY;
}
JsonPointer prev = null;
for (String segment : segments) {
JsonPointer next = new JsonPointer()
}
}
*/
/*
/**********************************************************
/* Public API
/**********************************************************
*/
public boolean matches() { return _nextSegment == null; }
public String getMatchingProperty() { return _matchingPropertyName; }
public int getMatchingIndex() { return _matchingElementIndex; }
/**
* @return True if the root selector matches property name (that is, could
* match field value of JSON Object node)
*/
public boolean mayMatchProperty() { return _matchingPropertyName != null; }
/**
* @return True if the root selector matches element index (that is, could
* match an element of JSON Array node)
*/
public boolean mayMatchElement() { return _matchingElementIndex >= 0; }
/**
* Returns the leaf of current JSON Pointer expression.
* Leaf is the last non-null segment of current JSON Pointer.
*
* @since 2.5
*/
public JsonPointer last() {
JsonPointer current = this;
if (current == EMPTY) {
return null;
}
JsonPointer next;
while ((next = current._nextSegment) != JsonPointer.EMPTY) {
current = next;
}
return current;
}
/**
* Mutant factory method that will return
*
* Method is mostly used to determine whether this context should be used for
* constructing {@link JsonPointer}
*
* @since 2.9
*/
public boolean hasPathSegment() {
if (_type == TYPE_OBJECT) {
return hasCurrentName();
} else if (_type == TYPE_ARRAY) {
return hasCurrentIndex();
}
return false;
}
/**
* Method for accessing name associated with the current location.
* Non-null for
* Note that "current value" is NOT populated (or used) by Streaming parser or generator;
* it is only used by higher-level data-binding functionality.
* The reason it is included here is that it can be stored and accessed hierarchically,
* and gets passed through data-binding.
*
* @return Currently active value, if one has been assigned.
*
* @since 2.5
*/
public Object getCurrentValue() {
return null;
}
/**
* Method to call to pass value to be returned via {@link #getCurrentValue}; typically
* called indirectly through {@link JsonParser#setCurrentValue}
* or {@link JsonGenerator#setCurrentValue}).
*
* @since 2.5
*/
public void setCurrentValue(Object v) { }
/**
* Factory method for constructing a {@link JsonPointer} that points to the current
* location within the stream that this context is for, excluding information about
* "root context" (only relevant for multi-root-value cases)
*
* @since 2.9
*/
public JsonPointer pathAsPointer() {
return JsonPointer.forPath(this, false);
}
/**
* Factory method for constructing a {@link JsonPointer} that points to the current
* location within the stream that this context is for, optionally including
* "root value index"
*
* @param includeRoot Whether root-value offset is included as the first segment or not;
*
* @since 2.9
*/
public JsonPointer pathAsPointer(boolean includeRoot) {
return JsonPointer.forPath(this, includeRoot);
}
/**
* Optional method that may be used to access starting location of this context:
* for example, in case of JSON `Object` context, offset at which `[` token was
* read or written. Often used for error reporting purposes.
* Implementations that do not keep track of such location are expected to return
* {@link JsonLocation#NA}; this is what the default implementation does.
*
* @return Location pointing to the point where the context
* start marker was found (or written); never `null`.
*
* NOTE: demoted from
* Note: this token is never returned by regular JSON readers, but
* only by readers that expose other kinds of source (like
*
* The standard implementation of this class is
*
* 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
* Note: since Jackson 2.1, stateful implementations MUST implement
* {@link com.fasterxml.jackson.core.util.Instantiatable} interface,
* to allow for constructing per-generation instances and avoid
* state corruption.
* Stateless implementations need not do this; but those are less common.
*/
public interface PrettyPrinter
{
/**
* @since 2.9
*/
public final static Separators DEFAULT_SEPARATORS = Separators.createDefaultInstance();
/**
* Default String used for separating root values is single space.
*
* @since 2.9
*/
public final static SerializedString DEFAULT_ROOT_VALUE_SEPARATOR = new SerializedString(" ");
/*
/**********************************************************
/* First methods that act both as events, and expect
/* output for correct functioning (i.e something gets
/* output even when not pretty-printing)
/**********************************************************
*/
// // // Root-level handling:
/**
* Method called after a root-level value has been completely
* output, and before another value is to be output.
*
* Default
* handling (without pretty-printing) will output a space, to
* allow values to be parsed correctly. Pretty-printer is
* to output some other suitable and nice-looking separator
* (tab(s), space(s), linefeed(s) or any combination thereof).
*/
void writeRootValueSeparator(JsonGenerator gen) throws IOException;
// // Object handling
/**
* Method called when an Object value is to be output, before
* any fields are output.
*
* Default handling (without pretty-printing) will output
* the opening curly bracket.
* Pretty-printer is
* to output a curly bracket as well, but can surround that
* with other (white-space) decoration.
*/
void writeStartObject(JsonGenerator gen) throws IOException;
/**
* Method called after an Object value has been completely output
* (minus closing curly bracket).
*
* Default handling (without pretty-printing) will output
* the closing curly bracket.
* Pretty-printer is
* to output a curly bracket as well, but can surround that
* with other (white-space) decoration.
*
* @param nrOfEntries Number of direct members of the array that
* have been output
*/
void writeEndObject(JsonGenerator gen, int nrOfEntries) throws IOException;
/**
* Method called after an object entry (field:value) has been completely
* output, and before another value is to be output.
*
* Default handling (without pretty-printing) will output a single
* comma to separate the two. Pretty-printer is
* to output a comma as well, but can surround that with other
* (white-space) decoration.
*/
void writeObjectEntrySeparator(JsonGenerator gen) throws IOException;
/**
* Method called after an object field has been output, but
* before the value is output.
*
* Default handling (without pretty-printing) will output a single
* colon to separate the two. Pretty-printer is
* to output a colon as well, but can surround that with other
* (white-space) decoration.
*/
void writeObjectFieldValueSeparator(JsonGenerator gen) throws IOException;
// // // Array handling
/**
* Method called when an Array value is to be output, before
* any member/child values are output.
*
* Default handling (without pretty-printing) will output
* the opening bracket.
* Pretty-printer is
* to output a bracket as well, but can surround that
* with other (white-space) decoration.
*/
void writeStartArray(JsonGenerator gen) throws IOException;
/**
* Method called after an Array value has been completely output
* (minus closing bracket).
*
* Default handling (without pretty-printing) will output
* the closing bracket.
* Pretty-printer is
* to output a bracket as well, but can surround that
* with other (white-space) decoration.
*
* @param nrOfValues Number of direct members of the array that
* have been output
*/
void writeEndArray(JsonGenerator gen, int nrOfValues) throws IOException;
/**
* Method called after an array value has been completely
* output, and before another value is to be output.
*
* Default handling (without pretty-printing) will output a single
* comma to separate the two. Pretty-printer is
* to output a comma as well, but can surround that with other
* (white-space) decoration.
*/
void writeArrayValueSeparator(JsonGenerator gen) throws IOException;
/*
/**********************************************************
/* Then events that by default do not produce any output
/* but that are often overridden to add white space
/* in pretty-printing mode
/**********************************************************
*/
/**
* Method called after array start marker has been output,
* and right before the first value is to be output.
* It is not called for arrays with no values.
*
* Default handling does not output anything, but pretty-printer
* is free to add any white space decoration.
*/
void beforeArrayValues(JsonGenerator gen) throws IOException;
/**
* Method called after object start marker has been output,
* and right before the field name of the first entry is
* to be output.
* It is not called for objects without entries.
*
* Default handling does not output anything, but pretty-printer
* is free to add any white space decoration.
*/
void beforeObjectEntries(JsonGenerator gen) throws IOException;
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/SerializableString.java 0000664 0000000 0000000 00000011111 13561642473 0032772 0 ustar 00root root 0000000 0000000 /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*/
package com.fasterxml.jackson.core;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* Interface that defines how Jackson package can interact with efficient
* pre-serialized or lazily-serialized and reused String representations.
* Typically implementations store possible serialized version(s) so that
* serialization of String can be done more efficiently, especially when
* used multiple times.
*
* Note that "quoted" in methods means quoting of 'special' characters using
* JSON backlash notation (and not use of actual double quotes).
*
* @see com.fasterxml.jackson.core.io.SerializedString
*/
public interface SerializableString
{
/**
* Returns unquoted String that this object represents (and offers
* serialized forms for)
*/
String getValue();
/**
* Returns length of the (unquoted) String as characters.
* Functionally equivalent to:
*
* Feature is enabled by default.
*/
AUTO_CLOSE_SOURCE(JsonParser.Feature.AUTO_CLOSE_SOURCE),
// // // Validity checks
/**
* Feature that determines whether {@link JsonParser} will explicitly
* check that no duplicate JSON Object field names are encountered.
* If enabled, parser will check all names within context and report
* duplicates by throwing a {@link JsonParseException}; if disabled,
* parser will not do such checking. Assumption in latter case is
* that caller takes care of handling duplicates at a higher level:
* data-binding, for example, has features to specify detection to
* be done there.
*
* Note that enabling this feature will incur performance overhead
* due to having to store and check additional information: this typically
* adds 20-30% to execution time for basic parsing.
*/
STRICT_DUPLICATE_DETECTION(JsonParser.Feature.STRICT_DUPLICATE_DETECTION),
/**
* Feature that determines what to do if the underlying data format requires knowledge
* of all properties to decode (usually via a Schema), and if no definition is
* found for a property that input content contains.
* Typically most textual data formats do NOT require schema information (although
* some do, such as CSV), whereas many binary data formats do require definitions
* (such as Avro, protobuf), although not all (Smile, CBOR, BSON and MessagePack do not).
* Further note that some formats that do require schema information will not be able
* to ignore undefined properties: for example, Avro is fully positional and there is
* no possibility of undefined data. This leaves formats like Protobuf that have identifiers
* that may or may not map; and as such Protobuf format does make use of this feature.
*
* Note that support for this feature is implemented by individual data format
* module, if (and only if) it makes sense for the format in question. For JSON,
* for example, this feature has no effect as properties need not be pre-defined.
*
* Feature is disabled by default, meaning that if the underlying data format
* requires knowledge of all properties to output, attempts to read an unknown
* property will result in a {@link JsonProcessingException}
*/
IGNORE_UNDEFINED(JsonParser.Feature.IGNORE_UNDEFINED),
// // // Other
/**
* Feature that determines whether {@link JsonLocation} instances should be constructed
* with reference to source or not. If source reference is included, its type and contents
* are included when `toString()` method is called (most notably when printing out parse
* exception with that location information). If feature is disabled, no source reference
* is passed and source is only indicated as "UNKNOWN".
*
* Most common reason for disabling this feature is to avoid leaking information about
* internal information; this may be done for security reasons.
* Note that even if source reference is included, only parts of contents are usually
* printed, and not the whole contents. Further, many source reference types can not
* necessarily access contents (like streams), so only type is indicated, not contents.
*
* Feature is enabled by default, meaning that "source reference" information is passed
* and some or all of the source content may be included in {@link JsonLocation} information
* constructed either when requested explicitly, or when needed for an exception.
*/
INCLUDE_SOURCE_IN_LOCATION(JsonParser.Feature.INCLUDE_SOURCE_IN_LOCATION),
;
/**
* Whether feature is enabled or disabled by default.
*/
private final boolean _defaultState;
private final int _mask;
/**
* For backwards compatibility we may need to map to one of existing {@link JsonParser.Feature}s;
* if so, this is the feature to enable/disable.
*/
final private JsonParser.Feature _mappedFeature;
private StreamReadFeature(JsonParser.Feature mapTo) {
// only for 2.x, let's map everything to legacy feature:
_mappedFeature = mapTo;
_mask = mapTo.getMask();
_defaultState = mapTo.enabledByDefault();
}
/**
* Method that calculates bit set (flags) of all features that
* are enabled by default.
*/
public static int collectDefaults()
{
int flags = 0;
for (StreamReadFeature f : values()) {
if (f.enabledByDefault()) {
flags |= f.getMask();
}
}
return flags;
}
public boolean enabledByDefault() { return _defaultState; }
public boolean enabledIn(int flags) { return (flags & _mask) != 0; }
public int getMask() { return _mask; }
public JsonParser.Feature mappedFeature() { return _mappedFeature; }
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/StreamWriteFeature.java 0000664 0000000 0000000 00000014033 13561642473 0032765 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core;
import java.io.OutputStream;
import java.io.Writer;
import java.math.BigDecimal;
/**
* Token writer (generator) features not-specific to any particular format backend.
* Eventual replacement for non-JSON-specific {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s.
*
* @since 2.10
*/
public enum StreamWriteFeature
{
// // Low-level I/O / content features
/**
* Feature that determines whether generator will automatically
* close underlying output target that is NOT owned by the
* generator.
* If disabled, calling application has to separately
* close the underlying {@link OutputStream} and {@link Writer}
* instances used to create the generator. If enabled, generator
* will handle closing, as long as generator itself gets closed:
* this happens when end-of-input is encountered, or generator
* is closed by a call to {@link JsonGenerator#close}.
*
* Feature is enabled by default.
*/
AUTO_CLOSE_TARGET(JsonGenerator.Feature.AUTO_CLOSE_TARGET),
/**
* Feature that determines what happens when the generator is
* closed while there are still unmatched
* {@link JsonToken#START_ARRAY} or {@link JsonToken#START_OBJECT}
* entries in output content. If enabled, such Array(s) and/or
* Object(s) are automatically closed (that is, matching END_ token write
* call is made for all open scopes); if disabled, no additional
* write calls are made.
*
* Feature is enabled by default.
*/
AUTO_CLOSE_CONTENT(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT),
/**
* Feature that specifies that calls to {@link JsonGenerator#flush} will cause
* matching
* Feature is enabled by default.
*/
FLUSH_PASSED_TO_STREAM(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM),
// // Datatype coercion features
/**
* Feature that determines whether {@link java.math.BigDecimal} entries are
* serialized using {@link java.math.BigDecimal#toPlainString()} to prevent
* values to be written using scientific notation.
*
* NOTE: only affects generators that serialize {@link java.math.BigDecimal}s
* using textual representation (textual formats but potentially some binary
* formats).
*
* Feature is disabled by default, so default output mode is used; this generally
* depends on how {@link BigDecimal} has been created.
*/
WRITE_BIGDECIMAL_AS_PLAIN(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN),
// // Schema/Validity support features
/**
* Feature that determines whether {@link JsonGenerator} will explicitly
* check that no duplicate JSON Object field names are written.
* If enabled, generator will check all names within context and report
* duplicates by throwing a {@link JsonGenerationException}; if disabled,
* no such checking will be done. Assumption in latter case is
* that caller takes care of not trying to write duplicate names.
*
* Note that enabling this feature will incur performance overhead
* due to having to store and check additional information.
*
* Feature is disabled by default.
*/
STRICT_DUPLICATE_DETECTION(JsonGenerator.Feature.STRICT_DUPLICATE_DETECTION),
/**
* Feature that determines what to do if the underlying data format requires knowledge
* of all properties to output, and if no definition is found for a property that
* caller tries to write. If enabled, such properties will be quietly ignored;
* if disabled, a {@link JsonProcessingException} will be thrown to indicate the
* problem.
* Typically most textual data formats do NOT require schema information (although
* some do, such as CSV), whereas many binary data formats do require definitions
* (such as Avro, protobuf), although not all (Smile, CBOR, BSON and MessagePack do not).
*
* Note that support for this feature is implemented by individual data format
* module, if (and only if) it makes sense for the format in question. For JSON,
* for example, this feature has no effect as properties need not be pre-defined.
*
* Feature is disabled by default, meaning that if the underlying data format
* requires knowledge of all properties to output, attempts to write an unknown
* property will result in a {@link JsonProcessingException}
*/
IGNORE_UNKNOWN(JsonGenerator.Feature.IGNORE_UNKNOWN),
;
/**
* Whether feature is enabled or disabled by default.
*/
private final boolean _defaultState;
private final int _mask;
/**
* For backwards compatibility we may need to map to one of existing {@link JsonParser.Feature}s;
* if so, this is the feature to enable/disable.
*/
final private JsonGenerator.Feature _mappedFeature;
private StreamWriteFeature(JsonGenerator.Feature mappedTo) {
// only for 2.x, let's map everything to legacy feature:
_mappedFeature = mappedTo;
_mask = mappedTo.getMask();
_defaultState = mappedTo.enabledByDefault();
}
/**
* Method that calculates bit set (flags) of all features that
* are enabled by default.
*/
public static int collectDefaults()
{
int flags = 0;
for (StreamWriteFeature f : values()) {
if (f.enabledByDefault()) {
flags |= f.getMask();
}
}
return flags;
}
public boolean enabledByDefault() { return _defaultState; }
public boolean enabledIn(int flags) { return (flags & _mask) != 0; }
public int getMask() { return _mask; }
public JsonGenerator.Feature mappedFeature() { return _mappedFeature; }
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java 0000664 0000000 0000000 00000021336 13561642473 0031152 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core;
import com.fasterxml.jackson.core.io.InputDecorator;
import com.fasterxml.jackson.core.io.OutputDecorator;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.core.json.JsonWriteFeature;
/**
* Since 2.10, Builder class is offered for creating token stream factories
* with difference configurations: with 3.x they will be fully immutable.
*
* @since 2.10
*/
public abstract class TSFBuilder
* Default implementation returns
* Default implementation returns
* Note that in Jackson 1.x
* NOTE: starting with Jackson 2.2, there is more functionality
* available via this class, and the intent is that this should
* form actual base for multiple alternative tree representations;
* for example, immutable trees could use different implementation
* than mutable trees. It should also be possible to move actual
* Tree Model implementation out of databind package eventually
* (Jackson 3?).
*
* @since 2.2
*/
public interface TreeNode
{
/*
/**********************************************************
/* Minimal introspection methods
/**********************************************************
*/
/**
* Method that can be used for efficient type detection
* when using stream abstraction for traversing nodes.
* Will return the first {@link JsonToken} that equivalent
* stream event would produce (for most nodes there is just
* one token but for structured/container types multiple)
*/
JsonToken asToken();
/**
* If this node is a numeric type (as per {@link JsonToken#isNumeric}),
* returns native type that node uses to store the numeric value;
* otherwise returns null.
*
* @return Type of number contained, if any; or null if node does not
* contain numeric value.
*/
JsonParser.NumberType numberType();
/**
* Method that returns number of child nodes this node contains:
* for Array nodes, number of child elements, for Object nodes,
* number of fields, and for all other nodes 0.
*
* @return For non-container nodes returns 0; for arrays number of
* contained elements, and for objects number of fields.
*
* @since 2.2
*/
int size();
/**
* Method that returns true for all value nodes: ones that
* are not containers, and that do not represent "missing" nodes
* in the path. Such value nodes represent String, Number, Boolean
* and null values from JSON.
*
* Note: one and only one of methods {@link #isValueNode},
* {@link #isContainerNode} and {@link #isMissingNode} ever
* returns true for any given node.
*
* @since 2.2
*/
boolean isValueNode();
/**
* Method that returns true for container nodes: Arrays and Objects.
*
* Note: one and only one of methods {@link #isValueNode},
* {@link #isContainerNode} and {@link #isMissingNode} ever
* returns true for any given node.
*
* @since 2.2
*/
boolean isContainerNode();
/**
* Method that returns true for "virtual" nodes which represent
* missing entries constructed by path accessor methods when
* there is no actual node matching given criteria.
*
* Note: one and only one of methods {@link #isValueNode},
* {@link #isContainerNode} and {@link #isMissingNode} ever
* returns true for any given node.
*
* @since 2.2
*/
boolean isMissingNode();
/**
* Method that returns true if this node is an Array node, false
* otherwise.
* Note that if true is returned, {@link #isContainerNode}
* must also return true.
*
* @since 2.2
*/
boolean isArray();
/**
* Method that returns true if this node is an Object node, false
* otherwise.
* Note that if true is returned, {@link #isContainerNode}
* must also return true.
*
* @since 2.2
*/
boolean isObject();
/*
/**********************************************************
/* Basic traversal through structured entries (Arrays, Objects)
/**********************************************************
*/
/**
* Method for accessing value of the specified field of
* an object node. If this node is not an object (or it
* does not have a value for specified field name), or
* if there is no field with such name, null is returned.
*
* NOTE: handling of explicit null values may vary between
* implementations; some trees may retain explicit nulls, others
* not.
*
* @return Node that represent value of the specified field,
* if this node is an object and has value for the specified
* field. Null otherwise.
*
* @since 2.2
*/
TreeNode get(String fieldName);
/**
* Method for accessing value of the specified element of
* an array node. For other nodes, null is returned.
*
* For array nodes, index specifies
* exact location within array and allows for efficient iteration
* over child elements (underlying storage is guaranteed to
* be efficiently indexable, i.e. has random-access to elements).
* If index is less than 0, or equal-or-greater than
*
* 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
*
* Note that if the same expression is used often, it is preferable to construct
* {@link JsonPointer} instance once and reuse it: this method will not perform
* any caching of compiled expressions.
*
* @param jsonPointerExpression Expression to compile as a {@link JsonPointer}
* instance
*
* @return Node that matches given JSON Pointer: if no match exists,
* will return a node for which {@link TreeNode#isMissingNode()} returns true.
*
* @since 2.3
*/
TreeNode at(String jsonPointerExpression) throws IllegalArgumentException;
/*
/**********************************************************
/* Converting to/from Streaming API
/**********************************************************
*/
/**
* Method for constructing a {@link JsonParser} instance for
* iterating over contents of the tree that this node is root of.
* Functionally equivalent to first serializing tree using
* {@link ObjectCodec} and then re-parsing but
* more efficient.
*
* NOTE: constructed parser instance will NOT initially point to a token,
* so before passing it to deserializers, it is typically necessary to
* advance it to the first available token by calling {@link JsonParser#nextToken()}.
*
* Also note that calling this method will NOT pass {@link ObjectCodec}
* reference, so data-binding callback methods like {@link JsonParser#readValueAs(Class)}
* will not work with calling {@link JsonParser#setCodec}).
* It is often better to call {@link #traverse(ObjectCodec)} to pass the codec explicitly.
*/
JsonParser traverse();
/**
* Same as {@link #traverse()}, but additionally passes {@link com.fasterxml.jackson.core.ObjectCodec}
* to use if {@link JsonParser#readValueAs(Class)} is used (otherwise caller must call
* {@link JsonParser#setCodec} on response explicitly).
*
* NOTE: constructed parser instance will NOT initially point to a token,
* so before passing it to deserializers, it is typically necessary to
* advance it to the first available token by calling {@link JsonParser#nextToken()}.
*
* @since 2.1
*/
JsonParser traverse(ObjectCodec codec);
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/Version.java 0000664 0000000 0000000 00000010541 13561642473 0030630 0 ustar 00root root 0000000 0000000 /* 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
* Note that 'minimal' here mostly refers to minimal number of fields
* (size) and functionality that is specific to certain types
* of parser implementations; but not necessarily to number of methods.
*/
public abstract class ParserMinimalBase extends JsonParser
{
// Control chars:
protected final static int INT_TAB = '\t';
protected final static int INT_LF = '\n';
protected final static int INT_CR = '\r';
protected final static int INT_SPACE = 0x0020;
// Markup
protected final static int INT_LBRACKET = '[';
protected final static int INT_RBRACKET = ']';
protected final static int INT_LCURLY = '{';
protected final static int INT_RCURLY = '}';
protected final static int INT_QUOTE = '"';
protected final static int INT_APOS = '\'';
protected final static int INT_BACKSLASH = '\\';
protected final static int INT_SLASH = '/';
protected final static int INT_ASTERISK = '*';
protected final static int INT_COLON = ':';
protected final static int INT_COMMA = ',';
protected final static int INT_HASH = '#';
// Number chars
protected final static int INT_0 = '0';
protected final static int INT_9 = '9';
protected final static int INT_MINUS = '-';
protected final static int INT_PLUS = '+';
protected final static int INT_PERIOD = '.';
protected final static int INT_e = 'e';
protected final static int INT_E = 'E';
protected final static char CHAR_NULL = '\0';
/**
* @since 2.9
*/
protected final static byte[] NO_BYTES = new byte[0];
/**
* @since 2.9
*/
protected final static int[] NO_INTS = new int[0];
/*
/**********************************************************
/* Constants and fields of former 'JsonNumericParserBase'
/**********************************************************
*/
protected final static int NR_UNKNOWN = 0;
// First, integer types
protected final static int NR_INT = 0x0001;
protected final static int NR_LONG = 0x0002;
protected final static int NR_BIGINT = 0x0004;
// And then floating point types
protected final static int NR_DOUBLE = 0x008;
protected final static int NR_BIGDECIMAL = 0x0010;
/**
* NOTE! Not used by JSON implementation but used by many of binary codecs
*
* @since 2.9
*/
protected final static int NR_FLOAT = 0x020;
// Also, we need some numeric constants
protected final static BigInteger BI_MIN_INT = BigInteger.valueOf(Integer.MIN_VALUE);
protected final static BigInteger BI_MAX_INT = BigInteger.valueOf(Integer.MAX_VALUE);
protected final static BigInteger BI_MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE);
protected final static BigInteger BI_MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
protected final static BigDecimal BD_MIN_LONG = new BigDecimal(BI_MIN_LONG);
protected final static BigDecimal BD_MAX_LONG = new BigDecimal(BI_MAX_LONG);
protected final static BigDecimal BD_MIN_INT = new BigDecimal(BI_MIN_INT);
protected final static BigDecimal BD_MAX_INT = new BigDecimal(BI_MAX_INT);
protected final static long MIN_INT_L = (long) Integer.MIN_VALUE;
protected final static long MAX_INT_L = (long) Integer.MAX_VALUE;
// These are not very accurate, but have to do... (for bounds checks)
protected final static double MIN_LONG_D = (double) Long.MIN_VALUE;
protected final static double MAX_LONG_D = (double) Long.MAX_VALUE;
protected final static double MIN_INT_D = (double) Integer.MIN_VALUE;
protected final static double MAX_INT_D = (double) Integer.MAX_VALUE;
/*
/**********************************************************
/* Misc other constants
/**********************************************************
*/
/**
* Maximum number of characters to include in token reported
* as part of error messages.
*
* @since 2.9
*/
protected final static int MAX_ERROR_TOKEN_LENGTH = 256;
/*
/**********************************************************
/* Minimal generally useful state
/**********************************************************
*/
/**
* Last token retrieved via {@link #nextToken}, if any.
* Null before the first call to
* NOTE: `this` instance is modified and no new instance is constructed.
*/
@Override
public InputCoercionException withParser(JsonParser p) {
_processor = p;
return this;
}
@Override
public InputCoercionException withRequestPayload(RequestPayload p) {
_requestPayload = p;
return this;
}
/**
* Accessor for getting information about input type (in form of token, giving "shape"
* of input) for which coercion failed.
*/
public JsonToken getInputType() {
return _inputType;
}
/**
* Accessor for getting information about target type (in form of Java {@link java.lang.Class})
* for which coercion failed.
*/
public Class> getTargetType() {
return _targetType;
}
}
StreamReadException.java 0000664 0000000 0000000 00000006167 13561642473 0033622 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/exc package com.fasterxml.jackson.core.exc;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.util.RequestPayload;
/**
* Intermediate base class for all read-side streaming processing problems, including
* parsing and input value coercion problems.
*
* @since 2.10
*/
public abstract class StreamReadException
extends JsonProcessingException
{
final static long serialVersionUID = 1L;
protected transient JsonParser _processor;
/**
* Optional payload that can be assigned to pass along for error reporting
* or handling purposes. Core streaming parser implementations DO NOT
* initialize this; it is up to using applications and frameworks to
* populate it.
*/
protected RequestPayload _requestPayload;
public StreamReadException(JsonParser p, String msg) {
super(msg, (p == null) ? null : p.getCurrentLocation());
_processor = p;
}
public StreamReadException(JsonParser p, String msg, Throwable root) {
super(msg, (p == null) ? null : p.getCurrentLocation(), root);
_processor = p;
}
public StreamReadException(JsonParser p, String msg, JsonLocation loc) {
super(msg, loc, null);
_processor = p;
}
protected StreamReadException(String msg, JsonLocation loc, Throwable rootCause) {
super(msg);
if (rootCause != null) {
initCause(rootCause);
}
_location = loc;
}
/**
* Fluent method that may be used to assign originating {@link JsonParser},
* to be accessed using {@link #getProcessor()}.
*
* NOTE: `this` instance is modified and no new instance is constructed.
*/
public abstract StreamReadException withParser(JsonParser p);
/**
* Fluent method that may be used to assign payload to this exception,
* to let recipient access it for diagnostics purposes.
*
* NOTE: `this` instance is modified and no new instance is constructed.
*/
public abstract StreamReadException withRequestPayload(RequestPayload p);
@Override
public JsonParser getProcessor() {
return _processor;
}
/**
* Method that may be called to find payload that was being parsed, if
* one was specified for parser that threw this Exception.
*
* @return request body, if payload was specified; `null` otherwise
*/
public RequestPayload getRequestPayload() {
return _requestPayload;
}
/**
* The method returns the String representation of the request payload if
* one was specified for parser that threw this Exception.
*
* @return request body as String, if payload was specified; `null` otherwise
*/
public String getRequestPayloadAsString() {
return (_requestPayload != null) ? _requestPayload.toString() : null;
}
/**
* Overriding the getMessage() to include the request body
*/
@Override
public String getMessage() {
String msg = super.getMessage();
if (_requestPayload != null) {
msg += "\nRequest payload : " + _requestPayload.toString();
}
return msg;
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/exc/package-info.java 0000664 0000000 0000000 00000000252 13561642473 0032304 0 ustar 00root root 0000000 0000000 /**
* Package for some of {@link com.fasterxml.jackson.core.JsonProcessingException}
* subtypes contained by streaming API.
*/
package com.fasterxml.jackson.core.exc;
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/filter/ 0000775 0000000 0000000 00000000000 13561642473 0027624 5 ustar 00root root 0000000 0000000 FilteringGeneratorDelegate.java 0000664 0000000 0000000 00000065150 13561642473 0035644 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/filter package com.fasterxml.jackson.core.filter;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.util.JsonGeneratorDelegate;
/**
* Specialized {@link JsonGeneratorDelegate} that allows use of
* {@link TokenFilter} for outputting a subset of content that
* caller tries to generate.
*
* @since 2.6
*/
public class FilteringGeneratorDelegate extends JsonGeneratorDelegate
{
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Object consulted to determine whether to write parts of content generator
* is asked to write or not.
*/
protected TokenFilter rootFilter;
/**
* Flag that determines whether filtering will continue after the first
* match is indicated or not: if `false`, output is based on just the first
* full match (returning {@link TokenFilter#INCLUDE_ALL}) and no more
* checks are made; if `true` then filtering will be applied as necessary
* until end of content.
*/
protected boolean _allowMultipleMatches;
/**
* Flag that determines whether path leading up to included content should
* also be automatically included or not. If `false`, no path inclusion is
* done and only explicitly included entries are output; if `true` then
* path from main level down to match is also included as necessary.
*/
protected boolean _includePath;
/* NOTE: this feature is included in the first version (2.6), but
* there is no public API to enable it, yet, since there isn't an
* actual use case. But it seemed possible need could arise, which
* is feature has not yet been removed. If no use is found within
* first version or two, just remove.
*
* Marked as deprecated since its status is uncertain.
*/
@Deprecated
protected boolean _includeImmediateParent;
/*
/**********************************************************
/* Additional state
/**********************************************************
*/
/**
* Although delegate has its own output context it is not sufficient since we actually
* have to keep track of excluded (filtered out) structures as well as ones delegate
* actually outputs.
*/
protected TokenFilterContext _filterContext;
/**
* State that applies to the item within container, used where applicable.
* Specifically used to pass inclusion state between property name and
* property, and also used for array elements.
*/
protected TokenFilter _itemFilter;
/**
* Number of tokens for which {@link TokenFilter#INCLUDE_ALL}
* has been returned
*/
protected int _matchCount;
/*
/**********************************************************
/* Construction, initialization
/**********************************************************
*/
public FilteringGeneratorDelegate(JsonGenerator d, TokenFilter f,
boolean includePath, boolean allowMultipleMatches)
{
// By default, do NOT delegate copy methods
super(d, false);
rootFilter = f;
// and this is the currently active filter for root values
_itemFilter = f;
_filterContext = TokenFilterContext.createRootContext(f);
_includePath = includePath;
_allowMultipleMatches = allowMultipleMatches;
}
/*
/**********************************************************
/* Extended API
/**********************************************************
*/
public TokenFilter getFilter() { return rootFilter; }
public JsonStreamContext getFilterContext() {
return _filterContext;
}
/**
* Accessor for finding number of matches, where specific token and sub-tree
* starting (if structured type) are passed.
*/
public int getMatchCount() {
return _matchCount;
}
/*
/**********************************************************
/* Public API, accessors
/**********************************************************
*/
@Override
public JsonStreamContext getOutputContext() {
/* 11-Apr-2015, tatu: Choice is between pre- and post-filter context;
* let's expose post-filter context that correlates with the view
* of caller.
*/
return _filterContext;
}
/*
/**********************************************************
/* Public API, write methods, structural
/**********************************************************
*/
@Override
public void writeStartArray() throws IOException
{
// First things first: whole-sale skipping easy
if (_itemFilter == null) {
_filterContext = _filterContext.createChildArrayContext(null, false);
return;
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) { // include the whole sub-tree?
_filterContext = _filterContext.createChildArrayContext(_itemFilter, true);
delegate.writeStartArray();
return;
}
// Ok; regular checking state then
_itemFilter = _filterContext.checkValue(_itemFilter);
if (_itemFilter == null) {
_filterContext = _filterContext.createChildArrayContext(null, false);
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
_itemFilter = _itemFilter.filterStartArray();
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
_checkParentPath();
_filterContext = _filterContext.createChildArrayContext(_itemFilter, true);
delegate.writeStartArray();
} else {
_filterContext = _filterContext.createChildArrayContext(_itemFilter, false);
}
}
@Override
public void writeStartArray(int size) throws IOException
{
if (_itemFilter == null) {
_filterContext = _filterContext.createChildArrayContext(null, false);
return;
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
_filterContext = _filterContext.createChildArrayContext(_itemFilter, true);
delegate.writeStartArray(size);
return;
}
_itemFilter = _filterContext.checkValue(_itemFilter);
if (_itemFilter == null) {
_filterContext = _filterContext.createChildArrayContext(null, false);
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
_itemFilter = _itemFilter.filterStartArray();
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
_checkParentPath();
_filterContext = _filterContext.createChildArrayContext(_itemFilter, true);
delegate.writeStartArray(size);
} else {
_filterContext = _filterContext.createChildArrayContext(_itemFilter, false);
}
}
@Override
public void writeEndArray() throws IOException
{
_filterContext = _filterContext.closeArray(delegate);
if (_filterContext != null) {
_itemFilter = _filterContext.getFilter();
}
}
@Override
public void writeStartObject() throws IOException
{
if (_itemFilter == null) {
_filterContext = _filterContext.createChildObjectContext(_itemFilter, false);
return;
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
_filterContext = _filterContext.createChildObjectContext(_itemFilter, true);
delegate.writeStartObject();
return;
}
TokenFilter f = _filterContext.checkValue(_itemFilter);
if (f == null) {
return;
}
if (f != TokenFilter.INCLUDE_ALL) {
f = f.filterStartObject();
}
if (f == TokenFilter.INCLUDE_ALL) {
_checkParentPath();
_filterContext = _filterContext.createChildObjectContext(f, true);
delegate.writeStartObject();
} else { // filter out
_filterContext = _filterContext.createChildObjectContext(f, false);
}
}
@Override
public void writeStartObject(Object forValue) throws IOException
{
if (_itemFilter == null) {
_filterContext = _filterContext.createChildObjectContext(_itemFilter, false);
return;
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
_filterContext = _filterContext.createChildObjectContext(_itemFilter, true);
delegate.writeStartObject(forValue);
return;
}
TokenFilter f = _filterContext.checkValue(_itemFilter);
if (f == null) {
return;
}
if (f != TokenFilter.INCLUDE_ALL) {
f = f.filterStartObject();
}
if (f == TokenFilter.INCLUDE_ALL) {
_checkParentPath();
_filterContext = _filterContext.createChildObjectContext(f, true);
delegate.writeStartObject(forValue);
} else { // filter out
_filterContext = _filterContext.createChildObjectContext(f, false);
}
}
@Override
public void writeEndObject() throws IOException
{
_filterContext = _filterContext.closeObject(delegate);
if (_filterContext != null) {
_itemFilter = _filterContext.getFilter();
}
}
@Override
public void writeFieldName(String name) throws IOException
{
TokenFilter state = _filterContext.setFieldName(name);
if (state == null) {
_itemFilter = null;
return;
}
if (state == TokenFilter.INCLUDE_ALL) {
_itemFilter = state;
delegate.writeFieldName(name);
return;
}
state = state.includeProperty(name);
_itemFilter = state;
if (state == TokenFilter.INCLUDE_ALL) {
_checkPropertyParentPath();
}
}
@Override
public void writeFieldName(SerializableString name) throws IOException
{
TokenFilter state = _filterContext.setFieldName(name.getValue());
if (state == null) {
_itemFilter = null;
return;
}
if (state == TokenFilter.INCLUDE_ALL) {
_itemFilter = state;
delegate.writeFieldName(name);
return;
}
state = state.includeProperty(name.getValue());
_itemFilter = state;
if (state == TokenFilter.INCLUDE_ALL) {
_checkPropertyParentPath();
}
}
/*
/**********************************************************
/* Public API, write methods, text/String values
/**********************************************************
*/
@Override
public void writeString(String value) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeString(value)) {
return;
}
}
_checkParentPath();
}
delegate.writeString(value);
}
@Override
public void writeString(char[] text, int offset, int len) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
String value = new String(text, offset, len);
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeString(value)) {
return;
}
}
_checkParentPath();
}
delegate.writeString(text, offset, len);
}
@Override
public void writeString(SerializableString value) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeString(value.getValue())) {
return;
}
}
_checkParentPath();
}
delegate.writeString(value);
}
@Override
public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRawUTF8String(text, offset, length);
}
}
@Override
public void writeUTF8String(byte[] text, int offset, int length) throws IOException
{
// not exact match, but best we can do
if (_checkRawValueWrite()) {
delegate.writeUTF8String(text, offset, length);
}
}
/*
/**********************************************************
/* Public API, write methods, binary/raw content
/**********************************************************
*/
@Override
public void writeRaw(String text) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRaw(text);
}
}
@Override
public void writeRaw(String text, int offset, int len) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRaw(text);
}
}
@Override
public void writeRaw(SerializableString text) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRaw(text);
}
}
@Override
public void writeRaw(char[] text, int offset, int len) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRaw(text, offset, len);
}
}
@Override
public void writeRaw(char c) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRaw(c);
}
}
@Override
public void writeRawValue(String text) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRaw(text);
}
}
@Override
public void writeRawValue(String text, int offset, int len) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRaw(text, offset, len);
}
}
@Override
public void writeRawValue(char[] text, int offset, int len) throws IOException
{
if (_checkRawValueWrite()) {
delegate.writeRaw(text, offset, len);
}
}
@Override
public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException
{
if (_checkBinaryWrite()) {
delegate.writeBinary(b64variant, data, offset, len);
}
}
@Override
public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) throws IOException
{
if (_checkBinaryWrite()) {
return delegate.writeBinary(b64variant, data, dataLength);
}
return -1;
}
/*
/**********************************************************
/* Public API, write methods, other value types
/**********************************************************
*/
@Override
public void writeNumber(short v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNumber(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeNumber(v);
}
@Override
public void writeNumber(int v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNumber(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeNumber(v);
}
@Override
public void writeNumber(long v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNumber(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeNumber(v);
}
@Override
public void writeNumber(BigInteger v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNumber(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeNumber(v);
}
@Override
public void writeNumber(double v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNumber(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeNumber(v);
}
@Override
public void writeNumber(float v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNumber(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeNumber(v);
}
@Override
public void writeNumber(BigDecimal v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNumber(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeNumber(v);
}
@Override
public void writeNumber(String encodedValue) throws IOException, UnsupportedOperationException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeRawValue()) { // close enough?
return;
}
}
_checkParentPath();
}
delegate.writeNumber(encodedValue);
}
@Override
public void writeBoolean(boolean v) throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeBoolean(v)) {
return;
}
}
_checkParentPath();
}
delegate.writeBoolean(v);
}
@Override
public void writeNull() throws IOException
{
if (_itemFilter == null) {
return;
}
if (_itemFilter != TokenFilter.INCLUDE_ALL) {
TokenFilter state = _filterContext.checkValue(_itemFilter);
if (state == null) {
return;
}
if (state != TokenFilter.INCLUDE_ALL) {
if (!state.includeNull()) {
return;
}
}
_checkParentPath();
}
delegate.writeNull();
}
/*
/**********************************************************
/* Overridden field methods
/**********************************************************
*/
@Override
public void writeOmittedField(String fieldName) throws IOException {
// Hmmh. Not sure how this would work but...
if (_itemFilter != null) {
delegate.writeOmittedField(fieldName);
}
}
/*
/**********************************************************
/* Public API, write methods, Native Ids
/**********************************************************
*/
// 25-Mar-2015, tatu: These are tricky as they sort of predate actual filtering calls.
// Let's try to use current state as a clue at least...
@Override
public void writeObjectId(Object id) throws IOException {
if (_itemFilter != null) {
delegate.writeObjectId(id);
}
}
@Override
public void writeObjectRef(Object id) throws IOException {
if (_itemFilter != null) {
delegate.writeObjectRef(id);
}
}
@Override
public void writeTypeId(Object id) throws IOException {
if (_itemFilter != null) {
delegate.writeTypeId(id);
}
}
/*
/**********************************************************
/* Public API, write methods, serializing Java objects
/**********************************************************
*/
// Base class definitions for these seems correct to me, iff not directly delegating:
/*
@Override
public void writeObject(Object pojo) throws IOException,JsonProcessingException {
if (delegateCopyMethods) {
delegate.writeObject(pojo);
return;
}
// NOTE: copied from
if (pojo == null) {
writeNull();
} else {
if (getCodec() != null) {
getCodec().writeValue(this, pojo);
return;
}
_writeSimpleObject(pojo);
}
}
@Override
public void writeTree(TreeNode rootNode) throws IOException {
if (delegateCopyMethods) {
delegate.writeTree(rootNode);
return;
}
// As with 'writeObject()', we are not check if write would work
if (rootNode == null) {
writeNull();
} else {
if (getCodec() == null) {
throw new IllegalStateException("No ObjectCodec defined");
}
getCodec().writeValue(this, rootNode);
}
}
*/
/*
/**********************************************************
/* Public API, copy-through methods
/**********************************************************
*/
// Base class definitions for these seems correct to me, iff not directly delegating:
/*
@Override
public void copyCurrentEvent(JsonParser jp) throws IOException {
if (delegateCopyMethods) delegate.copyCurrentEvent(jp);
else super.copyCurrentEvent(jp);
}
@Override
public void copyCurrentStructure(JsonParser jp) throws IOException {
if (delegateCopyMethods) delegate.copyCurrentStructure(jp);
else super.copyCurrentStructure(jp);
}
*/
/*
/**********************************************************
/* Helper methods
/**********************************************************
*/
protected void _checkParentPath() throws IOException
{
++_matchCount;
// only need to construct path if parent wasn't written
if (_includePath) {
_filterContext.writePath(delegate);
}
// also: if no multiple matches desired, short-cut checks
if (!_allowMultipleMatches) {
// Mark parents as "skip" so that further check calls are not made
_filterContext.skipParentChecks();
}
}
/**
* Specialized variant of {@link #_checkParentPath} used when checking
* parent for a property name to be included with value: rules are slightly
* different.
*/
protected void _checkPropertyParentPath() throws IOException
{
++_matchCount;
if (_includePath) {
_filterContext.writePath(delegate);
} else if (_includeImmediateParent) {
// 21-Apr-2015, tatu: Note that there is no API to enable this currently...
// retained for speculative future use
_filterContext.writeImmediatePath(delegate);
}
// also: if no multiple matches desired, short-cut checks
if (!_allowMultipleMatches) {
// Mark parents as "skip" so that further check calls are not made
_filterContext.skipParentChecks();
}
}
protected boolean _checkBinaryWrite() throws IOException
{
if (_itemFilter == null) {
return false;
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
return true;
}
if (_itemFilter.includeBinary()) { // close enough?
_checkParentPath();
return true;
}
return false;
}
protected boolean _checkRawValueWrite() throws IOException
{
if (_itemFilter == null) {
return false;
}
if (_itemFilter == TokenFilter.INCLUDE_ALL) {
return true;
}
if (_itemFilter.includeRawValue()) { // close enough?
_checkParentPath();
return true;
}
return false;
}
}
FilteringParserDelegate.java 0000664 0000000 0000000 00000104676 13561642473 0035161 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/filter package com.fasterxml.jackson.core.filter;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.util.JsonParserDelegate;
import static com.fasterxml.jackson.core.JsonTokenId.*;
/**
* Specialized {@link JsonParserDelegate} that allows use of
* {@link TokenFilter} for outputting a subset of content that
* is visible to caller
*
* @since 2.6
*/
public class FilteringParserDelegate extends JsonParserDelegate
{
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Object consulted to determine whether to write parts of content generator
* is asked to write or not.
*/
protected TokenFilter rootFilter;
/**
* Flag that determines whether filtering will continue after the first
* match is indicated or not: if `false`, output is based on just the first
* full match (returning {@link TokenFilter#INCLUDE_ALL}) and no more
* checks are made; if `true` then filtering will be applied as necessary
* until end of content.
*/
protected boolean _allowMultipleMatches;
/**
* Flag that determines whether path leading up to included content should
* also be automatically included or not. If `false`, no path inclusion is
* done and only explicitly included entries are output; if `true` then
* path from main level down to match is also included as necessary.
*/
protected boolean _includePath;
/* NOTE: this feature is included in the first version (2.6), but
* there is no public API to enable it, yet, since there isn't an
* actual use case. But it seemed possible need could arise, which
* is feature has not yet been removed. If no use is found within
* first version or two, just remove.
*
* Marked as deprecated since its status is uncertain.
*/
@Deprecated
protected boolean _includeImmediateParent;
/*
/**********************************************************
/* State
/**********************************************************
*/
/**
* Last token retrieved via {@link #nextToken}, if any.
* Null before the first call to
* Default implementation returns
* Default implementation returns
* The default implementation simply returns
* The default implementation simply returns
* The default implementation simply returns
* Default action is to call
* NOTE: no binary payload passed; assumption is this won't be of much use.
*/
public boolean includeBinary() {
return _includeScalar();
}
/**
* Call made to verify whether leaf-level
* raw (pre-encoded, not quoted by generator) value
* should be included in output or not.
*
* NOTE: value itself not passed since it may come on multiple forms
* and is unlikely to be of much use in determining inclusion
* criteria.
*/
public boolean includeRawValue() {
return _includeScalar();
}
/**
* Call made to verify whether leaf-level
* embedded (Opaque) value
* should be included in output or not.
*/
public boolean includeEmbeddedValue(Object ob) {
return _includeScalar();
}
/*
/**********************************************************
/* Overrides
/**********************************************************
*/
@Override
public String toString() {
if (this == INCLUDE_ALL) {
return "TokenFilter.INCLUDE_ALL";
}
return super.toString();
}
/*
/**********************************************************
/* Other methods
/**********************************************************
*/
/**
* Overridable default implementation delegated to all scalar value
* inclusion check methods.
* The default implementation simply includes all leaf values.
*/
protected boolean _includeScalar() {
return true;
}
}
TokenFilterContext.java 0000664 0000000 0000000 00000025430 13561642473 0034207 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/filter package com.fasterxml.jackson.core.filter;
import java.io.IOException;
import com.fasterxml.jackson.core.*;
/**
* Alternative variant of {@link JsonStreamContext}, used when filtering
* content being read or written (based on {@link TokenFilter}).
*
* @since 2.6
*/
public class TokenFilterContext extends JsonStreamContext
{
/**
* Parent context for this context; null for root context.
*/
protected final TokenFilterContext _parent;
/*
/**********************************************************
/* Simple instance reuse slots; speed up things
/* a bit (10-15%) for docs with lots of small
/* arrays/objects
/**********************************************************
*/
protected TokenFilterContext _child;
/*
/**********************************************************
/* Location/state information
/**********************************************************
*/
/**
* Name of the field of which value is to be parsed; only
* used for OBJECT contexts
*/
protected String _currentName;
/**
* Filter to use for items in this state (for properties of Objects,
* elements of Arrays, and root-level values of root context)
*/
protected TokenFilter _filter;
/**
* Flag that indicates that start token has been read/written,
* so that matching close token needs to be read/written as well
* when context is getting closed.
*/
protected boolean _startHandled;
/**
* Flag that indicates that the current name of this context
* still needs to be read/written, if path from root down to
* included leaf is to be exposed.
*/
protected boolean _needToHandleName;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
protected TokenFilterContext(int type, TokenFilterContext parent,
TokenFilter filter, boolean startHandled)
{
super();
_type = type;
_parent = parent;
_filter = filter;
_index = -1;
_startHandled = startHandled;
_needToHandleName = false;
}
protected TokenFilterContext reset(int type,
TokenFilter filter, boolean startWritten)
{
_type = type;
_filter = filter;
_index = -1;
_currentName = null;
_startHandled = startWritten;
_needToHandleName = false;
return this;
}
/*
/**********************************************************
/* Factory methods
/**********************************************************
*/
public static TokenFilterContext createRootContext(TokenFilter filter) {
// true -> since we have no start/end marker, consider start handled
return new TokenFilterContext(TYPE_ROOT, null, filter, true);
}
public TokenFilterContext createChildArrayContext(TokenFilter filter, boolean writeStart) {
TokenFilterContext ctxt = _child;
if (ctxt == null) {
_child = ctxt = new TokenFilterContext(TYPE_ARRAY, this, filter, writeStart);
return ctxt;
}
return ctxt.reset(TYPE_ARRAY, filter, writeStart);
}
public TokenFilterContext createChildObjectContext(TokenFilter filter, boolean writeStart) {
TokenFilterContext ctxt = _child;
if (ctxt == null) {
_child = ctxt = new TokenFilterContext(TYPE_OBJECT, this, filter, writeStart);
return ctxt;
}
return ctxt.reset(TYPE_OBJECT, filter, writeStart);
}
/*
/**********************************************************
/* State changes
/**********************************************************
*/
public TokenFilter setFieldName(String name) throws JsonProcessingException {
_currentName = name;
_needToHandleName = true;
return _filter;
}
/**
* Method called to check whether value is to be included at current output
* position, either as Object property, Array element, or root value.
*/
public TokenFilter checkValue(TokenFilter filter) {
// First, checks for Object properties have been made earlier:
if (_type == TYPE_OBJECT) {
return filter;
}
// We increase it first because at the beginning of array, value is -1
int ix = ++_index;
if (_type == TYPE_ARRAY) {
return filter.includeElement(ix);
}
return filter.includeRootValue(ix);
}
/**
* Method called to ensure that parent path from root is written up to
* and including this node.
*/
public void writePath(JsonGenerator gen) throws IOException
{
if ((_filter == null) || (_filter == TokenFilter.INCLUDE_ALL)) {
return;
}
if (_parent != null) {
_parent._writePath(gen);
}
if (_startHandled) {
// even if Object started, need to start leaf-level name
if (_needToHandleName) {
gen.writeFieldName(_currentName);
}
} else {
_startHandled = true;
if (_type == TYPE_OBJECT) {
gen.writeStartObject();
gen.writeFieldName(_currentName); // we know name must be written
} else if (_type == TYPE_ARRAY) {
gen.writeStartArray();
}
}
}
/**
* Variant of {@link #writePath(JsonGenerator)} called when all we
* need is immediately surrounding Object. Method typically called
* when including a single property but not including full path
* to root.
*/
public void writeImmediatePath(JsonGenerator gen) throws IOException
{
if ((_filter == null) || (_filter == TokenFilter.INCLUDE_ALL)) {
return;
}
if (_startHandled) {
// even if Object started, need to start leaf-level name
if (_needToHandleName) {
gen.writeFieldName(_currentName);
}
} else {
_startHandled = true;
if (_type == TYPE_OBJECT) {
gen.writeStartObject();
if (_needToHandleName) {
gen.writeFieldName(_currentName);
}
} else if (_type == TYPE_ARRAY) {
gen.writeStartArray();
}
}
}
private void _writePath(JsonGenerator gen) throws IOException
{
if ((_filter == null) || (_filter == TokenFilter.INCLUDE_ALL)) {
return;
}
if (_parent != null) {
_parent._writePath(gen);
}
if (_startHandled) {
// even if Object started, need to start leaf-level name
if (_needToHandleName) {
_needToHandleName = false; // at parent must explicitly clear
gen.writeFieldName(_currentName);
}
} else {
_startHandled = true;
if (_type == TYPE_OBJECT) {
gen.writeStartObject();
if (_needToHandleName) {
_needToHandleName = false; // at parent must explicitly clear
gen.writeFieldName(_currentName);
}
} else if (_type == TYPE_ARRAY) {
gen.writeStartArray();
}
}
}
public TokenFilterContext closeArray(JsonGenerator gen) throws IOException
{
if (_startHandled) {
gen.writeEndArray();
}
if ((_filter != null) && (_filter != TokenFilter.INCLUDE_ALL)) {
_filter.filterFinishArray();
}
return _parent;
}
public TokenFilterContext closeObject(JsonGenerator gen) throws IOException
{
if (_startHandled) {
gen.writeEndObject();
}
if ((_filter != null) && (_filter != TokenFilter.INCLUDE_ALL)) {
_filter.filterFinishObject();
}
return _parent;
}
public void skipParentChecks() {
_filter = null;
for (TokenFilterContext ctxt = _parent; ctxt != null; ctxt = ctxt._parent) {
_parent._filter = null;
}
}
/*
/**********************************************************
/* Accessors, mutators
/**********************************************************
*/
@Override
public Object getCurrentValue() { return null; }
@Override
public void setCurrentValue(Object v) { }
@Override public final TokenFilterContext getParent() { return _parent; }
@Override public final String getCurrentName() { return _currentName; }
// @since 2.9
@Override public boolean hasCurrentName() { return _currentName != null; }
public TokenFilter getFilter() { return _filter; }
public boolean isStartHandled() { return _startHandled; }
public JsonToken nextTokenToRead() {
if (!_startHandled) {
_startHandled = true;
if (_type == TYPE_OBJECT) {
return JsonToken.START_OBJECT;
}
// Note: root should never be unhandled
return JsonToken.START_ARRAY;
}
// But otherwise at most might have FIELD_NAME
if (_needToHandleName && (_type == TYPE_OBJECT)) {
_needToHandleName = false;
return JsonToken.FIELD_NAME;
}
return null;
}
public TokenFilterContext findChildOf(TokenFilterContext parent) {
if (_parent == parent) {
return this;
}
TokenFilterContext curr = _parent;
while (curr != null) {
TokenFilterContext p = curr._parent;
if (p == parent) {
return curr;
}
curr = p;
}
// should never occur but...
return null;
}
// // // Internally used abstract methods
protected void appendDesc(StringBuilder sb) {
if (_parent != null) {
_parent.appendDesc(sb);
}
if (_type == TYPE_OBJECT) {
sb.append('{');
if (_currentName != null) {
sb.append('"');
// !!! TODO: Name chars should be escaped?
sb.append(_currentName);
sb.append('"');
} else {
sb.append('?');
}
sb.append('}');
} else if (_type == TYPE_ARRAY) {
sb.append('[');
sb.append(getCurrentIndex());
sb.append(']');
} else {
// nah, ROOT:
sb.append("/");
}
}
// // // Overridden standard methods
/**
* Overridden to provide developer writeable "JsonPath" representation
* of the context.
*/
@Override public String toString() {
StringBuilder sb = new StringBuilder(64);
appendDesc(sb);
return sb.toString();
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/format/ 0000775 0000000 0000000 00000000000 13561642473 0027627 5 ustar 00root root 0000000 0000000 DataFormatDetector.java 0000664 0000000 0000000 00000016125 13561642473 0034134 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/format package 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
* For example, when testing for XML data format,
* seeing a less-than character ("<") alone (with possible leading spaces)
* would be a strong indication that data could
* be in xml format (but see below for {@link #FULL_MATCH} description for more)
*/
SOLID_MATCH,
/**
* Value that indicates that given data contains a signature that is deemed
* specific enough to uniquely indicate data format used.
*
* For example, when testing for XML data format,
* seing "<xml" as the first data bytes ("XML declaration", as per XML specification)
* could give full confidence that data is indeed in XML format.
* Not all data formats have unique leading identifiers to allow full matches; for example,
* JSON only has heuristic matches and can have at most {@link #SOLID_MATCH}) match.
*/
FULL_MATCH
;
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/format/package-info.java 0000664 0000000 0000000 00000000337 13561642473 0033021 0 ustar 00root root 0000000 0000000 /**
* Package that contains interfaces needed for dynamic, pluggable
* format (auto)detection; as well as basic utility classes for
* simple format detection functionality.
*/
package com.fasterxml.jackson.core.format;
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/ 0000775 0000000 0000000 00000000000 13561642473 0026746 5 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/CharTypes.java 0000664 0000000 0000000 00000023737 13561642473 0031527 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.io;
import java.util.Arrays;
public final class CharTypes
{
private final static char[] HC = "0123456789ABCDEF".toCharArray();
private final static byte[] HB;
static {
int len = HC.length;
HB = new byte[len];
for (int i = 0; i < len; ++i) {
HB[i] = (byte) HC[i];
}
}
/**
* Lookup table used for determining which input characters
* need special handling when contained in text segment.
*/
private final static int[] sInputCodes;
static {
/* 96 would do for most cases (backslash is ASCII 94)
* but if we want to do lookups by raw bytes it's better
* to have full table
*/
final int[] table = new int[256];
// Control chars and non-space white space are not allowed unquoted
for (int i = 0; i < 32; ++i) {
table[i] = -1;
}
// And then string end and quote markers are special too
table['"'] = 1;
table['\\'] = 1;
sInputCodes = table;
}
/**
* Additionally we can combine UTF-8 decoding info into similar
* data table.
*/
private final static int[] sInputCodesUTF8;
static {
final int[] table = new int[sInputCodes.length];
System.arraycopy(sInputCodes, 0, table, 0, table.length);
for (int c = 128; c < 256; ++c) {
int code;
// We'll add number of bytes needed for decoding
if ((c & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
code = 2;
} else if ((c & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
code = 3;
} else if ((c & 0xF8) == 0xF0) {
// 4 bytes; double-char with surrogates and all...
code = 4;
} else {
// And -1 seems like a good "universal" error marker...
code = -1;
}
table[c] = code;
}
sInputCodesUTF8 = table;
}
/**
* To support non-default (and -standard) unquoted field names mode,
* need to have alternate checking.
* Basically this is list of 8-bit ASCII characters that are legal
* as part of Javascript identifier
*/
private final static int[] sInputCodesJsNames;
static {
final int[] table = new int[256];
// Default is "not a name char", mark ones that are
Arrays.fill(table, -1);
// Assume rules with JS same as Java (change if/as needed)
for (int i = 33; i < 256; ++i) {
if (Character.isJavaIdentifierPart((char) i)) {
table[i] = 0;
}
}
/* As per [JACKSON-267], '@', '#' and '*' are also to be accepted as well.
* And '-' (for hyphenated names); and '+' for sake of symmetricity...
*/
table['@'] = 0;
table['#'] = 0;
table['*'] = 0;
table['-'] = 0;
table['+'] = 0;
sInputCodesJsNames = table;
}
/**
* This table is similar to Latin-1, except that it marks all "high-bit"
* code as ok. They will be validated at a later point, when decoding
* name
*/
private final static int[] sInputCodesUtf8JsNames;
static {
final int[] table = new int[256];
// start with 8-bit JS names
System.arraycopy(sInputCodesJsNames, 0, table, 0, table.length);
Arrays.fill(table, 128, 128, 0);
sInputCodesUtf8JsNames = table;
}
/**
* Decoding table used to quickly determine characters that are
* relevant within comment content.
*/
private final static int[] sInputCodesComment;
static {
final int[] buf = new int[256];
// but first: let's start with UTF-8 multi-byte markers:
System.arraycopy(sInputCodesUTF8, 128, buf, 128, 128);
// default (0) means "ok" (skip); -1 invalid, others marked by char itself
Arrays.fill(buf, 0, 32, -1); // invalid white space
buf['\t'] = 0; // tab is still fine
buf['\n'] = '\n'; // lf/cr need to be observed, ends cpp comment
buf['\r'] = '\r';
buf['*'] = '*'; // end marker for c-style comments
sInputCodesComment = buf;
}
/**
* Decoding table used for skipping white space and comments.
*
* @since 2.3
*/
private final static int[] sInputCodesWS;
static {
// but first: let's start with UTF-8 multi-byte markers:
final int[] buf = new int[256];
System.arraycopy(sInputCodesUTF8, 128, buf, 128, 128);
// default (0) means "not whitespace" (end); 1 "whitespace", -1 invalid,
// 2-4 UTF-8 multi-bytes, others marked by char itself
//
Arrays.fill(buf, 0, 32, -1); // invalid white space
buf[' '] = 1;
buf['\t'] = 1;
buf['\n'] = '\n'; // lf/cr need to be observed, ends cpp comment
buf['\r'] = '\r';
buf['/'] = '/'; // start marker for c/cpp comments
buf['#'] = '#'; // start marker for YAML comments
sInputCodesWS = buf;
}
/**
* Lookup table used for determining which output characters in
* 7-bit ASCII range need to be quoted.
*/
private final static int[] sOutputEscapes128;
static {
int[] table = new int[128];
// Control chars need generic escape sequence
for (int i = 0; i < 32; ++i) {
// 04-Mar-2011, tatu: Used to use "-(i + 1)", replaced with constant
table[i] = CharacterEscapes.ESCAPE_STANDARD;
}
// Others (and some within that range too) have explicit shorter sequences
table['"'] = '"';
table['\\'] = '\\';
// Escaping of slash is optional, so let's not add it
table[0x08] = 'b';
table[0x09] = 't';
table[0x0C] = 'f';
table[0x0A] = 'n';
table[0x0D] = 'r';
sOutputEscapes128 = table;
}
/**
* Lookup table for the first 256 Unicode characters (ASCII / UTF-8)
* range. For actual hex digits, contains corresponding value;
* for others -1.
*
* NOTE: before 2.10.1, was of size 128, extended for simpler handling
*/
private final static int[] sHexValues = new int[256];
static {
Arrays.fill(sHexValues, -1);
for (int i = 0; i < 10; ++i) {
sHexValues['0' + i] = i;
}
for (int i = 0; i < 6; ++i) {
sHexValues['a' + i] = 10 + i;
sHexValues['A' + i] = 10 + i;
}
}
public static int[] getInputCodeLatin1() { return sInputCodes; }
public static int[] getInputCodeUtf8() { return sInputCodesUTF8; }
public static int[] getInputCodeLatin1JsNames() { return sInputCodesJsNames; }
public static int[] getInputCodeUtf8JsNames() { return sInputCodesUtf8JsNames; }
public static int[] getInputCodeComment() { return sInputCodesComment; }
public static int[] getInputCodeWS() { return sInputCodesWS; }
/**
* Accessor for getting a read-only encoding table for first 128 Unicode
* code points (single-byte UTF-8 characters).
* Value of 0 means "no escaping"; other positive values that value is character
* to use after backslash; and negative values that generic (backslash - u)
* escaping is to be used.
*/
public static int[] get7BitOutputEscapes() { return sOutputEscapes128; }
/**
* Alternative to {@link #get7BitOutputEscapes()} when a non-standard quote character
* is used.
*
* @since 2.10
*/
public static int[] get7BitOutputEscapes(int quoteChar) {
if (quoteChar == '"') {
return sOutputEscapes128;
}
return AltEscapes.instance.escapesFor(quoteChar);
}
public static int charToHex(int ch)
{
// 08-Nov-2019, tatu: As per [core#540] and [core#578], changed to
// force masking here so caller need not do that.
return sHexValues[ch & 0xFF];
}
public static void appendQuoted(StringBuilder sb, String content)
{
final int[] escCodes = sOutputEscapes128;
int escLen = escCodes.length;
for (int i = 0, len = content.length(); i < len; ++i) {
char c = content.charAt(i);
if (c >= escLen || escCodes[c] == 0) {
sb.append(c);
continue;
}
sb.append('\\');
int escCode = escCodes[c];
if (escCode < 0) { // generic quoting (hex value)
// The only negative value sOutputEscapes128 returns
// is CharacterEscapes.ESCAPE_STANDARD, which mean
// appendQuotes should encode using the Unicode encoding;
// not sure if this is the right way to encode for
// CharacterEscapes.ESCAPE_CUSTOM or other (future)
// CharacterEscapes.ESCAPE_XXX values.
// We know that it has to fit in just 2 hex chars
sb.append('u');
sb.append('0');
sb.append('0');
int value = c; // widening
sb.append(HC[value >> 4]);
sb.append(HC[value & 0xF]);
} else { // "named", i.e. prepend with slash
sb.append((char) escCode);
}
}
}
public static char[] copyHexChars() {
return (char[]) HC.clone();
}
public static byte[] copyHexBytes() {
return (byte[]) HB.clone();
}
// @since 2.10
private static class AltEscapes {
public final static AltEscapes instance = new AltEscapes();
private int[][] _altEscapes = new int[128][];
public int[] escapesFor(int quoteChar) {
int[] esc = _altEscapes[quoteChar];
if (esc == null) {
esc = Arrays.copyOf(sOutputEscapes128, 128);
// Only add escape setting if character does not already have it
if (esc[quoteChar] == 0) {
esc[quoteChar] = CharacterEscapes.ESCAPE_STANDARD;
}
_altEscapes[quoteChar] = esc;
}
return esc;
}
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/CharacterEscapes.java 0000664 0000000 0000000 00000005346 13561642473 0033021 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.io;
import java.util.Arrays;
import com.fasterxml.jackson.core.SerializableString;
/**
* Abstract base class that defines interface for customizing character
* escaping aspects for String values, for formats that use escaping.
* For JSON this applies to both property names and String values.
*/
@SuppressWarnings("serial")
public abstract class CharacterEscapes
implements java.io.Serializable // since 2.1
{
/**
* Value used for lookup tables to indicate that matching characters
* do not need to be escaped.
*/
public final static int ESCAPE_NONE = 0;
/**
* Value used for lookup tables to indicate that matching characters
* are to be escaped using standard escaping; for JSON this means
* (for example) using "backslash - u" escape method.
*/
public final static int ESCAPE_STANDARD = -1;
/**
* Value used for lookup tables to indicate that matching characters
* will need custom escapes; and that another call
* to {@link #getEscapeSequence} is needed to figure out exact escape
* sequence to output.
*/
public final static int ESCAPE_CUSTOM = -2;
/**
* Method generators can call to get lookup table for determining
* escape handling for first 128 characters of Unicode (ASCII
* characters. Caller is not to modify contents of this array, since
* this is expected to be a shared copy.
*
* @return Array with size of at least 128, where first 128 entries
* have either one of
* NOTE: non-final since 2.4, to allow sub-classing.
*/
public class IOContext
{
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Reference to the source object, which can be used for displaying
* location information
*/
protected final Object _sourceRef;
/**
* Encoding used by the underlying stream, if known.
*/
protected JsonEncoding _encoding;
/**
* Flag that indicates whether underlying input/output source/target
* object is fully managed by the owner of this context (parser or
* generator). If true, it is, and is to be closed by parser/generator;
* if false, calling application has to do closing (unless auto-closing
* feature is enabled for the parser/generator in question; in which
* case it acts like the owner).
*/
protected final boolean _managedResource;
/*
/**********************************************************
/* Buffer handling, recycling
/**********************************************************
*/
/**
* Recycler used for actual allocation/deallocation/reuse
*/
protected final BufferRecycler _bufferRecycler;
/**
* Reference to the allocated I/O buffer for low-level input reading,
* if any allocated.
*/
protected byte[] _readIOBuffer;
/**
* Reference to the allocated I/O buffer used for low-level
* encoding-related buffering.
*/
protected byte[] _writeEncodingBuffer;
/**
* Reference to the buffer allocated for temporary use with
* base64 encoding or decoding.
*/
protected byte[] _base64Buffer;
/**
* Reference to the buffer allocated for tokenization purposes,
* in which character input is read, and from which it can be
* further returned.
*/
protected char[] _tokenCBuffer;
/**
* Reference to the buffer allocated for buffering it for
* output, before being encoded: generally this means concatenating
* output, then encoding when buffer fills up.
*/
protected char[] _concatCBuffer;
/**
* Reference temporary buffer Parser instances need if calling
* app decides it wants to access name via 'getTextCharacters' method.
* Regular text buffer can not be used as it may contain textual
* representation of the value token.
*/
protected char[] _nameCopyBuffer;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public IOContext(BufferRecycler br, Object sourceRef, boolean managedResource)
{
_bufferRecycler = br;
_sourceRef = sourceRef;
_managedResource = managedResource;
}
public void setEncoding(JsonEncoding enc) {
_encoding = enc;
}
/**
* @since 1.6
*/
public IOContext withEncoding(JsonEncoding enc) {
_encoding = enc;
return this;
}
/*
/**********************************************************
/* Public API, accessors
/**********************************************************
*/
public Object getSourceReference() { return _sourceRef; }
public JsonEncoding getEncoding() { return _encoding; }
public boolean isResourceManaged() { return _managedResource; }
/*
/**********************************************************
/* Public API, buffer management
/**********************************************************
*/
public TextBuffer constructTextBuffer() {
return new TextBuffer(_bufferRecycler);
}
/**
*
* Note: the method can only be called once during its life cycle.
* This is to protect against accidental sharing.
*/
public byte[] allocReadIOBuffer() {
_verifyAlloc(_readIOBuffer);
return (_readIOBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.BYTE_READ_IO_BUFFER));
}
/**
* @since 2.4
*/
public byte[] allocReadIOBuffer(int minSize) {
_verifyAlloc(_readIOBuffer);
return (_readIOBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.BYTE_READ_IO_BUFFER, minSize));
}
public byte[] allocWriteEncodingBuffer() {
_verifyAlloc(_writeEncodingBuffer);
return (_writeEncodingBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.BYTE_WRITE_ENCODING_BUFFER));
}
/**
* @since 2.4
*/
public byte[] allocWriteEncodingBuffer(int minSize) {
_verifyAlloc(_writeEncodingBuffer);
return (_writeEncodingBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.BYTE_WRITE_ENCODING_BUFFER, minSize));
}
/**
* @since 2.1
*/
public byte[] allocBase64Buffer() {
_verifyAlloc(_base64Buffer);
return (_base64Buffer = _bufferRecycler.allocByteBuffer(BufferRecycler.BYTE_BASE64_CODEC_BUFFER));
}
/**
* @since 2.9
*/
public byte[] allocBase64Buffer(int minSize) {
_verifyAlloc(_base64Buffer);
return (_base64Buffer = _bufferRecycler.allocByteBuffer(BufferRecycler.BYTE_BASE64_CODEC_BUFFER, minSize));
}
public char[] allocTokenBuffer() {
_verifyAlloc(_tokenCBuffer);
return (_tokenCBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CHAR_TOKEN_BUFFER));
}
/**
* @since 2.4
*/
public char[] allocTokenBuffer(int minSize) {
_verifyAlloc(_tokenCBuffer);
return (_tokenCBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CHAR_TOKEN_BUFFER, minSize));
}
public char[] allocConcatBuffer() {
_verifyAlloc(_concatCBuffer);
return (_concatCBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CHAR_CONCAT_BUFFER));
}
public char[] allocNameCopyBuffer(int minSize) {
_verifyAlloc(_nameCopyBuffer);
return (_nameCopyBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CHAR_NAME_COPY_BUFFER, minSize));
}
/**
* Method to call when all the processing buffers can be safely
* recycled.
*/
public void releaseReadIOBuffer(byte[] buf) {
if (buf != null) {
/* Let's do sanity checks to ensure once-and-only-once release,
* as well as avoiding trying to release buffers not owned
*/
_verifyRelease(buf, _readIOBuffer);
_readIOBuffer = null;
_bufferRecycler.releaseByteBuffer(BufferRecycler.BYTE_READ_IO_BUFFER, buf);
}
}
public void releaseWriteEncodingBuffer(byte[] buf) {
if (buf != null) {
/* Let's do sanity checks to ensure once-and-only-once release,
* as well as avoiding trying to release buffers not owned
*/
_verifyRelease(buf, _writeEncodingBuffer);
_writeEncodingBuffer = null;
_bufferRecycler.releaseByteBuffer(BufferRecycler.BYTE_WRITE_ENCODING_BUFFER, buf);
}
}
public void releaseBase64Buffer(byte[] buf) {
if (buf != null) { // sanity checks, release once-and-only-once, must be one owned
_verifyRelease(buf, _base64Buffer);
_base64Buffer = null;
_bufferRecycler.releaseByteBuffer(BufferRecycler.BYTE_BASE64_CODEC_BUFFER, buf);
}
}
public void releaseTokenBuffer(char[] buf) {
if (buf != null) {
_verifyRelease(buf, _tokenCBuffer);
_tokenCBuffer = null;
_bufferRecycler.releaseCharBuffer(BufferRecycler.CHAR_TOKEN_BUFFER, buf);
}
}
public void releaseConcatBuffer(char[] buf) {
if (buf != null) {
// 14-Jan-2014, tatu: Let's actually allow upgrade of the original buffer.
_verifyRelease(buf, _concatCBuffer);
_concatCBuffer = null;
_bufferRecycler.releaseCharBuffer(BufferRecycler.CHAR_CONCAT_BUFFER, buf);
}
}
public void releaseNameCopyBuffer(char[] buf) {
if (buf != null) {
// 14-Jan-2014, tatu: Let's actually allow upgrade of the original buffer.
_verifyRelease(buf, _nameCopyBuffer);
_nameCopyBuffer = null;
_bufferRecycler.releaseCharBuffer(BufferRecycler.CHAR_NAME_COPY_BUFFER, buf);
}
}
/*
/**********************************************************
/* Internal helpers
/**********************************************************
*/
protected final void _verifyAlloc(Object buffer) {
if (buffer != null) { throw new IllegalStateException("Trying to call same allocXxx() method second time"); }
}
protected final void _verifyRelease(byte[] toRelease, byte[] src) {
// 07-Mar-2016, tatu: As per [core#255], only prevent shrinking of buffer
if ((toRelease != src) && (toRelease.length < src.length)) { throw wrongBuf(); }
}
protected final void _verifyRelease(char[] toRelease, char[] src) {
// 07-Mar-2016, tatu: As per [core#255], only prevent shrinking of buffer
if ((toRelease != src) && (toRelease.length < src.length)) { throw wrongBuf(); }
}
private IllegalArgumentException wrongBuf() {
// sanity check failed; trying to return different, smaller buffer.
return new IllegalArgumentException("Trying to release buffer smaller than original");
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/InputDecorator.java 0000664 0000000 0000000 00000007631 13561642473 0032562 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.io;
import java.io.*;
/**
* Handler class that can be used to decorate input sources.
* Typical use is to use a filter abstraction (filtered stream,
* reader) around original input source, and apply additional
* processing during read operations.
*/
public abstract class InputDecorator
implements java.io.Serializable // since 2.1
{
private static final long serialVersionUID = 1L;
/**
* Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when
* creating parser given an {@link InputStream}, when this decorator
* has been registered.
*
* @param ctxt IO context in use (provides access to declared encoding).
* NOTE: at this point context may not have all information initialized;
* specifically auto-detected encoding is only available once parsing starts,
* which may occur only after this method is called.
* @param in Original input source
*
* @return InputStream to use; either 'in' as is, or decorator
* version that typically delogates to 'in'
*/
public abstract InputStream decorate(IOContext ctxt, InputStream in)
throws IOException;
/**
* Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when
* creating parser on given "raw" byte source.
* Method can either construct a {@link InputStream} for reading; or return
* null to indicate that no wrapping should occur.
*
* @param ctxt IO context in use (provides access to declared encoding)
* NOTE: at this point context may not have all information initialized;
* specifically auto-detected encoding is only available once parsing starts,
* which may occur only after this method is called.
* @param src Input buffer that contains contents to parse
* @param offset Offset of the first available byte in the input buffer
* @param length Number of bytes available in the input buffer
*
* @return Either {@link InputStream} to use as input source; or null to indicate
* that contents are to be processed as-is by caller
*/
public abstract InputStream decorate(IOContext ctxt, byte[] src, int offset, int length)
throws IOException;
/**
* Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when
* creating parser given an {@link DataInput}, when this decorator
* has been registered.
*
* Default implementation simply throws {@link UnsupportedOperationException}
*
* @param ctxt IO context in use (provides access to declared encoding).
* NOTE: at this point context may not have all information initialized;
* specifically auto-detected encoding is only available once parsing starts,
* which may occur only after this method is called.
* @param input Original input source
*
* @return InputStream to use; either 'input' as is, or decorator
* version that typically delogates to 'input'
*
* @since 2.8
*/
public DataInput decorate(IOContext ctxt, DataInput input)
throws IOException {
throw new UnsupportedOperationException();
}
/**
* Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when
* creating parser given an {@link Reader}, when this decorator
* has been registered.
*
* @param ctxt IO context in use (provides access to declared encoding)
* NOTE: at this point context may not have all information initialized;
* specifically auto-detected encoding is only available once parsing starts,
* which may occur only after this method is called.
* @param r Original reader
*
* @return Reader to use; either passed in argument, or something that
* calls it (for example, a {@link FilterReader})
*/
public abstract Reader decorate(IOContext ctxt, Reader r) throws IOException;
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/JsonEOFException.java 0000664 0000000 0000000 00000002212 13561642473 0032730 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.io;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
/**
* Specialized {@link JsonParseException} that is thrown when end-of-input
* is reached unexpectedly, either within token being decoded, or during
* skipping of intervening white-space that is not between root-level
* tokens (that is, is within JSON Object or JSON Array construct).
*
* @since 2.8
*/
public class JsonEOFException extends JsonParseException
{
private static final long serialVersionUID = 1L;
/**
* Type of token that was being decoded, if parser had enough information
* to recognize type (such as starting double-quote for Strings)
*/
protected final JsonToken _token;
public JsonEOFException(JsonParser p, JsonToken token, String msg) {
super(p, msg);
_token = token;
}
/**
* Accessor for possibly available information about token that was being
* decoded while encountering end of input.
*/
public JsonToken getTokenBeingDecoded() {
return _token;
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/JsonStringEncoder.java 0000664 0000000 0000000 00000044715 13561642473 0033224 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.io;
import java.util.Arrays;
import com.fasterxml.jackson.core.util.ByteArrayBuilder;
import com.fasterxml.jackson.core.util.TextBuffer;
/**
* Helper class used for efficient encoding of JSON String values (including
* JSON field names) into Strings or UTF-8 byte arrays.
*
* Note that methods in here are somewhat optimized, but not ridiculously so.
* Reason is that conversion method results are expected to be cached so that
* these methods will not be hot spots during normal operation.
*/
public final class JsonStringEncoder
{
/*
/**********************************************************************
/* Constants
/**********************************************************************
*/
private final static char[] HC = CharTypes.copyHexChars();
private final static byte[] HB = CharTypes.copyHexBytes();
private final static int SURR1_FIRST = 0xD800;
private final static int SURR1_LAST = 0xDBFF;
private final static int SURR2_FIRST = 0xDC00;
private final static int SURR2_LAST = 0xDFFF;
private final static int INITIAL_CHAR_BUFFER_SIZE = 120;
private final static int INITIAL_BYTE_BUFFER_SIZE = 200;
/*
/**********************************************************************
/* Construction, instance access
/**********************************************************************
*/
// Since 2.10 we have stateless singleton and NO fancy ThreadLocal/SofRef caching!!!
private final static JsonStringEncoder instance = new JsonStringEncoder();
public JsonStringEncoder() { }
/**
* Factory method for getting an instance; this is either recycled per-thread instance,
* or a newly constructed one.
*/
public static JsonStringEncoder getInstance() {
return instance;
}
/*
/**********************************************************************
/* Public API
/**********************************************************************
*/
/**
* Method that will quote text contents using JSON standard quoting,
* and return results as a character array
*/
public char[] quoteAsString(String input)
{
char[] outputBuffer = new char[INITIAL_CHAR_BUFFER_SIZE];
final int[] escCodes = CharTypes.get7BitOutputEscapes();
final int escCodeCount = escCodes.length;
int inPtr = 0;
final int inputLen = input.length();
TextBuffer textBuffer = null;
int outPtr = 0;
char[] qbuf = null;
outer:
while (inPtr < inputLen) {
tight_loop:
while (true) {
char c = input.charAt(inPtr);
if (c < escCodeCount && escCodes[c] != 0) {
break tight_loop;
}
if (outPtr >= outputBuffer.length) {
if (textBuffer == null) {
textBuffer = TextBuffer.fromInitial(outputBuffer);
}
outputBuffer = textBuffer.finishCurrentSegment();
outPtr = 0;
}
outputBuffer[outPtr++] = c;
if (++inPtr >= inputLen) {
break outer;
}
}
// something to escape; 2 or 6-char variant?
if (qbuf == null) {
qbuf = _qbuf();
}
char d = input.charAt(inPtr++);
int escCode = escCodes[d];
int length = (escCode < 0)
? _appendNumeric(d, qbuf)
: _appendNamed(escCode, qbuf);
;
if ((outPtr + length) > outputBuffer.length) {
int first = outputBuffer.length - outPtr;
if (first > 0) {
System.arraycopy(qbuf, 0, outputBuffer, outPtr, first);
}
if (textBuffer == null) {
textBuffer = TextBuffer.fromInitial(outputBuffer);
}
outputBuffer = textBuffer.finishCurrentSegment();
int second = length - first;
System.arraycopy(qbuf, first, outputBuffer, 0, second);
outPtr = second;
} else {
System.arraycopy(qbuf, 0, outputBuffer, outPtr, length);
outPtr += length;
}
}
if (textBuffer == null) {
return Arrays.copyOfRange(outputBuffer, 0, outPtr);
}
textBuffer.setCurrentLength(outPtr);
return textBuffer.contentsAsArray();
}
/**
* Overloaded variant of {@link #quoteAsString(String)}.
*
* @since 2.10
*/
public char[] quoteAsString(CharSequence input)
{
// 15-Aug-2019, tatu: Optimize common case as JIT can't get rid of overhead otherwise
if (input instanceof String) {
return quoteAsString((String) input);
}
TextBuffer textBuffer = null;
char[] outputBuffer = new char[INITIAL_CHAR_BUFFER_SIZE];
final int[] escCodes = CharTypes.get7BitOutputEscapes();
final int escCodeCount = escCodes.length;
int inPtr = 0;
final int inputLen = input.length();
int outPtr = 0;
char[] qbuf = null;
outer:
while (inPtr < inputLen) {
tight_loop:
while (true) {
char c = input.charAt(inPtr);
if (c < escCodeCount && escCodes[c] != 0) {
break tight_loop;
}
if (outPtr >= outputBuffer.length) {
if (textBuffer == null) {
textBuffer = TextBuffer.fromInitial(outputBuffer);
}
outputBuffer = textBuffer.finishCurrentSegment();
outPtr = 0;
}
outputBuffer[outPtr++] = c;
if (++inPtr >= inputLen) {
break outer;
}
}
// something to escape; 2 or 6-char variant?
if (qbuf == null) {
qbuf = _qbuf();
}
char d = input.charAt(inPtr++);
int escCode = escCodes[d];
int length = (escCode < 0)
? _appendNumeric(d, qbuf)
: _appendNamed(escCode, qbuf);
;
if ((outPtr + length) > outputBuffer.length) {
int first = outputBuffer.length - outPtr;
if (first > 0) {
System.arraycopy(qbuf, 0, outputBuffer, outPtr, first);
}
if (textBuffer == null) {
textBuffer = TextBuffer.fromInitial(outputBuffer);
}
outputBuffer = textBuffer.finishCurrentSegment();
int second = length - first;
System.arraycopy(qbuf, first, outputBuffer, 0, second);
outPtr = second;
} else {
System.arraycopy(qbuf, 0, outputBuffer, outPtr, length);
outPtr += length;
}
}
if (textBuffer == null) {
return Arrays.copyOfRange(outputBuffer, 0, outPtr);
}
textBuffer.setCurrentLength(outPtr);
return textBuffer.contentsAsArray();
}
/**
* Method that will quote text contents using JSON standard quoting,
* and append results to a supplied {@link StringBuilder}.
* Use this variant if you have e.g. a {@link StringBuilder} and want to avoid superfluous copying of it.
*
* @since 2.8
*/
public void quoteAsString(CharSequence input, StringBuilder output)
{
final int[] escCodes = CharTypes.get7BitOutputEscapes();
final int escCodeCount = escCodes.length;
int inPtr = 0;
final int inputLen = input.length();
char[] qbuf = null;
outer:
while (inPtr < inputLen) {
tight_loop:
while (true) {
char c = input.charAt(inPtr);
if (c < escCodeCount && escCodes[c] != 0) {
break tight_loop;
}
output.append(c);
if (++inPtr >= inputLen) {
break outer;
}
}
// something to escape; 2 or 6-char variant?
if (qbuf == null) {
qbuf = _qbuf();
}
char d = input.charAt(inPtr++);
int escCode = escCodes[d];
int length = (escCode < 0)
? _appendNumeric(d, qbuf)
: _appendNamed(escCode, qbuf);
output.append(qbuf, 0, length);
}
}
/**
* Will quote given JSON String value using standard quoting, encode
* results as UTF-8, and return result as a byte array.
*/
@SuppressWarnings("resource")
public byte[] quoteAsUTF8(String text)
{
int inputPtr = 0;
int inputEnd = text.length();
int outputPtr = 0;
byte[] outputBuffer = new byte[INITIAL_BYTE_BUFFER_SIZE];
ByteArrayBuilder bb = null;
main:
while (inputPtr < inputEnd) {
final int[] escCodes = CharTypes.get7BitOutputEscapes();
inner_loop: // ASCII and escapes
while (true) {
int ch = text.charAt(inputPtr);
if (ch > 0x7F || escCodes[ch] != 0) {
break inner_loop;
}
if (outputPtr >= outputBuffer.length) {
if (bb == null) {
bb = ByteArrayBuilder.fromInitial(outputBuffer, outputPtr);
}
outputBuffer = bb.finishCurrentSegment();
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) ch;
if (++inputPtr >= inputEnd) {
break main;
}
}
if (bb == null) {
bb = ByteArrayBuilder.fromInitial(outputBuffer, outputPtr);
}
if (outputPtr >= outputBuffer.length) {
outputBuffer = bb.finishCurrentSegment();
outputPtr = 0;
}
// Ok, so what did we hit?
int ch = (int) text.charAt(inputPtr++);
if (ch <= 0x7F) { // needs quoting
int escape = escCodes[ch];
// ctrl-char, 6-byte escape...
outputPtr = _appendByte(ch, escape, bb, outputPtr);
outputBuffer = bb.getCurrentSegment();
continue main;
}
if (ch <= 0x7FF) { // fine, just needs 2 byte output
outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
ch = (0x80 | (ch & 0x3f));
} else { // 3 or 4 bytes
// Surrogates?
if (ch < SURR1_FIRST || ch > SURR2_LAST) { // nope
outputBuffer[outputPtr++] = (byte) (0xe0 | (ch >> 12));
if (outputPtr >= outputBuffer.length) {
outputBuffer = bb.finishCurrentSegment();
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
ch = (0x80 | (ch & 0x3f));
} else { // yes, surrogate pair
if (ch > SURR1_LAST) { // must be from first range
_illegal(ch);
}
// and if so, followed by another from next range
if (inputPtr >= inputEnd) {
_illegal(ch);
}
ch = _convert(ch, text.charAt(inputPtr++));
if (ch > 0x10FFFF) { // illegal, as per RFC 4627
_illegal(ch);
}
outputBuffer[outputPtr++] = (byte) (0xf0 | (ch >> 18));
if (outputPtr >= outputBuffer.length) {
outputBuffer = bb.finishCurrentSegment();
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 12) & 0x3f));
if (outputPtr >= outputBuffer.length) {
outputBuffer = bb.finishCurrentSegment();
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
ch = (0x80 | (ch & 0x3f));
}
}
if (outputPtr >= outputBuffer.length) {
outputBuffer = bb.finishCurrentSegment();
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) ch;
}
if (bb == null) {
return Arrays.copyOfRange(outputBuffer, 0, outputPtr);
}
return bb.completeAndCoalesce(outputPtr);
}
/**
* Will encode given String as UTF-8 (without any quoting), return
* resulting byte array.
*/
@SuppressWarnings("resource")
public byte[] encodeAsUTF8(String text)
{
int inputPtr = 0;
int inputEnd = text.length();
int outputPtr = 0;
byte[] outputBuffer = new byte[INITIAL_BYTE_BUFFER_SIZE];
int outputEnd = outputBuffer.length;
ByteArrayBuilder bb = null;
main_loop:
while (inputPtr < inputEnd) {
int c = text.charAt(inputPtr++);
// first tight loop for ascii
while (c <= 0x7F) {
if (outputPtr >= outputEnd) {
if (bb == null) {
bb = ByteArrayBuilder.fromInitial(outputBuffer, outputPtr);
}
outputBuffer = bb.finishCurrentSegment();
outputEnd = outputBuffer.length;
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) c;
if (inputPtr >= inputEnd) {
break main_loop;
}
c = text.charAt(inputPtr++);
}
// then multi-byte...
if (bb == null) {
bb = ByteArrayBuilder.fromInitial(outputBuffer, outputPtr);
}
if (outputPtr >= outputEnd) {
outputBuffer = bb.finishCurrentSegment();
outputEnd = outputBuffer.length;
outputPtr = 0;
}
if (c < 0x800) { // 2-byte
outputBuffer[outputPtr++] = (byte) (0xc0 | (c >> 6));
} else { // 3 or 4 bytes
// Surrogates?
if (c < SURR1_FIRST || c > SURR2_LAST) { // nope
outputBuffer[outputPtr++] = (byte) (0xe0 | (c >> 12));
if (outputPtr >= outputEnd) {
outputBuffer = bb.finishCurrentSegment();
outputEnd = outputBuffer.length;
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
} else { // yes, surrogate pair
if (c > SURR1_LAST) { // must be from first range
_illegal(c);
}
// and if so, followed by another from next range
if (inputPtr >= inputEnd) {
_illegal(c);
}
c = _convert(c, text.charAt(inputPtr++));
if (c > 0x10FFFF) { // illegal, as per RFC 4627
_illegal(c);
}
outputBuffer[outputPtr++] = (byte) (0xf0 | (c >> 18));
if (outputPtr >= outputEnd) {
outputBuffer = bb.finishCurrentSegment();
outputEnd = outputBuffer.length;
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
if (outputPtr >= outputEnd) {
outputBuffer = bb.finishCurrentSegment();
outputEnd = outputBuffer.length;
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
}
}
if (outputPtr >= outputEnd) {
outputBuffer = bb.finishCurrentSegment();
outputEnd = outputBuffer.length;
outputPtr = 0;
}
outputBuffer[outputPtr++] = (byte) (0x80 | (c & 0x3f));
}
if (bb == null) {
return Arrays.copyOfRange(outputBuffer, 0, outputPtr);
}
return bb.completeAndCoalesce(outputPtr);
}
/*
/**********************************************************************
/* Internal methods
/**********************************************************************
*/
private char[] _qbuf() {
char[] qbuf = new char[6];
qbuf[0] = '\\';
qbuf[2] = '0';
qbuf[3] = '0';
return qbuf;
}
private int _appendNumeric(int value, char[] qbuf) {
qbuf[1] = 'u';
// We know it's a control char, so only the last 2 chars are non-0
qbuf[4] = HC[value >> 4];
qbuf[5] = HC[value & 0xF];
return 6;
}
private int _appendNamed(int esc, char[] qbuf) {
qbuf[1] = (char) esc;
return 2;
}
private int _appendByte(int ch, int esc, ByteArrayBuilder bb, int ptr)
{
bb.setCurrentSegmentLength(ptr);
bb.append('\\');
if (esc < 0) { // standard escape
bb.append('u');
if (ch > 0xFF) {
int hi = (ch >> 8);
bb.append(HB[hi >> 4]);
bb.append(HB[hi & 0xF]);
ch &= 0xFF;
} else {
bb.append('0');
bb.append('0');
}
bb.append(HB[ch >> 4]);
bb.append(HB[ch & 0xF]);
} else { // 2-char simple escape
bb.append((byte) esc);
}
return bb.getCurrentSegmentLength();
}
private static int _convert(int p1, int p2) {
// Ok, then, is the second part valid?
if (p2 < SURR2_FIRST || p2 > SURR2_LAST) {
throw new IllegalArgumentException("Broken surrogate pair: first char 0x"+Integer.toHexString(p1)+", second 0x"+Integer.toHexString(p2)+"; illegal combination");
}
return 0x10000 + ((p1 - SURR1_FIRST) << 10) + (p2 - SURR2_FIRST);
}
private static void _illegal(int c) {
throw new IllegalArgumentException(UTF8Writer.illegalSurrogateDesc(c));
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/MergedStream.java 0000664 0000000 0000000 00000005707 13561642473 0032201 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.io;
import java.io.*;
/**
* Simple {@link InputStream} implementation that is used to "unwind" some
* data previously read from an input stream; so that as long as some of
* that data remains, it's returned; but as long as it's read, we'll
* just use data from the underlying original stream.
* This is similar to {@link java.io.PushbackInputStream}, but here there's
* only one implicit pushback, when instance is constructed.
*/
public final class MergedStream extends InputStream
{
final private IOContext _ctxt;
final private InputStream _in;
private byte[] _b;
private int _ptr;
final private int _end;
public MergedStream(IOContext ctxt, InputStream in, byte[] buf, int start, int end) {
_ctxt = ctxt;
_in = in;
_b = buf;
_ptr = start;
_end = end;
}
@Override
public int available() throws IOException {
if (_b != null) {
return _end - _ptr;
}
return _in.available();
}
@Override public void close() throws IOException {
_free();
_in.close();
}
@Override public void mark(int readlimit) {
if (_b == null) { _in.mark(readlimit); }
}
@Override public boolean markSupported() {
// Only supports marks past the initial rewindable section...
return (_b == null) && _in.markSupported();
}
@Override public int read() throws IOException {
if (_b != null) {
int c = _b[_ptr++] & 0xFF;
if (_ptr >= _end) {
_free();
}
return c;
}
return _in.read();
}
@Override public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (_b != null) {
int avail = _end - _ptr;
if (len > avail) {
len = avail;
}
System.arraycopy(_b, _ptr, b, off, len);
_ptr += len;
if (_ptr >= _end) {
_free();
}
return len;
}
return _in.read(b, off, len);
}
@Override
public void reset() throws IOException {
if (_b == null) { _in.reset(); }
}
@Override
public long skip(long n) throws IOException {
long count = 0L;
if (_b != null) {
int amount = _end - _ptr;
if (amount > n) { // all in pushed back segment?
_ptr += (int) n;
return n;
}
_free();
count += amount;
n -= amount;
}
if (n > 0) { count += _in.skip(n); }
return count;
}
private void _free() {
byte[] buf = _b;
if (buf != null) {
_b = null;
if (_ctxt != null) {
_ctxt.releaseReadIOBuffer(buf);
}
}
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java 0000664 0000000 0000000 00000022755 13561642473 0032074 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.io;
import java.math.BigDecimal;
public final class NumberInput
{
/**
* Textual representation of a double constant that can cause nasty problems
* with JDK (see http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308).
*/
public final static String NASTY_SMALL_DOUBLE = "2.2250738585072012e-308";
/**
* Constants needed for parsing longs from basic int parsing methods
*/
final static long L_BILLION = 1000000000;
final static String MIN_LONG_STR_NO_SIGN = String.valueOf(Long.MIN_VALUE).substring(1);
final static String MAX_LONG_STR = String.valueOf(Long.MAX_VALUE);
/**
* Fast method for parsing integers that are known to fit into
* regular 32-bit signed int type. This means that length is
* between 1 and 9 digits (inclusive)
*
* Note: public to let unit tests call it
*/
public static int parseInt(char[] ch, int off, int len)
{
int num = ch[off + len - 1] - '0';
switch(len) {
case 9:
num += (ch[off++] - '0') * 100000000;
case 8:
num += (ch[off++] - '0') * 10000000;
case 7:
num += (ch[off++] - '0') * 1000000;
case 6:
num += (ch[off++] - '0') * 100000;
case 5:
num += (ch[off++] - '0') * 10000;
case 4:
num += (ch[off++] - '0') * 1000;
case 3:
num += (ch[off++] - '0') * 100;
case 2:
num += (ch[off] - '0') * 10;
}
return num;
}
/**
* Helper method to (more) efficiently parse integer numbers from
* String values.
*/
public static int parseInt(String s)
{
/* Ok: let's keep strategy simple: ignoring optional minus sign,
* we'll accept 1 - 9 digits and parse things efficiently;
* otherwise just defer to JDK parse functionality.
*/
char c = s.charAt(0);
int len = s.length();
boolean neg = (c == '-');
int offset = 1;
// must have 1 - 9 digits after optional sign:
// negative?
if (neg) {
if (len == 1 || len > 10) {
return Integer.parseInt(s);
}
c = s.charAt(offset++);
} else {
if (len > 9) {
return Integer.parseInt(s);
}
}
if (c > '9' || c < '0') {
return Integer.parseInt(s);
}
int num = c - '0';
if (offset < len) {
c = s.charAt(offset++);
if (c > '9' || c < '0') {
return Integer.parseInt(s);
}
num = (num * 10) + (c - '0');
if (offset < len) {
c = s.charAt(offset++);
if (c > '9' || c < '0') {
return Integer.parseInt(s);
}
num = (num * 10) + (c - '0');
// Let's just loop if we have more than 3 digits:
if (offset < len) {
do {
c = s.charAt(offset++);
if (c > '9' || c < '0') {
return Integer.parseInt(s);
}
num = (num * 10) + (c - '0');
} while (offset < len);
}
}
}
return neg ? -num : num;
}
public static long parseLong(char[] ch, int off, int len)
{
// Note: caller must ensure length is [10, 18]
int len1 = len-9;
long val = parseInt(ch, off, len1) * L_BILLION;
return val + (long) parseInt(ch, off+len1, 9);
}
public static long parseLong(String s)
{
/* Ok, now; as the very first thing, let's just optimize case of "fake longs";
* that is, if we know they must be ints, call int parsing
*/
int length = s.length();
if (length <= 9) {
return (long) parseInt(s);
}
// !!! TODO: implement efficient 2-int parsing...
return Long.parseLong(s);
}
/**
* Helper method for determining if given String representation of
* an integral number would fit in 64-bit Java long or not.
* Note that input String must NOT contain leading minus sign (even
* if 'negative' is set to true).
*
* @param negative Whether original number had a minus sign (which is
* NOT passed to this method) or not
*/
public static boolean inLongRange(char[] ch, int off, int len,
boolean negative)
{
String cmpStr = negative ? MIN_LONG_STR_NO_SIGN : MAX_LONG_STR;
int cmpLen = cmpStr.length();
if (len < cmpLen) return true;
if (len > cmpLen) return false;
for (int i = 0; i < cmpLen; ++i) {
int diff = ch[off+i] - cmpStr.charAt(i);
if (diff != 0) {
return (diff < 0);
}
}
return true;
}
/**
* Similar to {@link #inLongRange(char[],int,int,boolean)}, but
* with String argument
*
* @param negative Whether original number had a minus sign (which is
* NOT passed to this method) or not
*/
public static boolean inLongRange(String s, boolean negative)
{
String cmp = negative ? MIN_LONG_STR_NO_SIGN : MAX_LONG_STR;
int cmpLen = cmp.length();
int alen = s.length();
if (alen < cmpLen) return true;
if (alen > cmpLen) return false;
// could perhaps just use String.compareTo()?
for (int i = 0; i < cmpLen; ++i) {
int diff = s.charAt(i) - cmp.charAt(i);
if (diff != 0) {
return (diff < 0);
}
}
return true;
}
public static int parseAsInt(String s, int def)
{
if (s == null) {
return def;
}
s = s.trim();
int len = s.length();
if (len == 0) {
return def;
}
// One more thing: use integer parsing for 'simple'
int i = 0;
if (i < len) { // skip leading sign:
char c = s.charAt(0);
if (c == '+') { // for plus, actually physically remove
s = s.substring(1);
len = s.length();
} else if (c == '-') { // minus, just skip for checks, must retain
++i;
}
}
for (; i < len; ++i) {
char c = s.charAt(i);
// if other symbols, parse as Double, coerce
if (c > '9' || c < '0') {
try {
return (int) parseDouble(s);
} catch (NumberFormatException e) {
return def;
}
}
}
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) { }
return def;
}
public static long parseAsLong(String s, long def)
{
if (s == null) {
return def;
}
s = s.trim();
int len = s.length();
if (len == 0) {
return def;
}
// One more thing: use long parsing for 'simple'
int i = 0;
if (i < len) { // skip leading sign:
char c = s.charAt(0);
if (c == '+') { // for plus, actually physically remove
s = s.substring(1);
len = s.length();
} else if (c == '-') { // minus, just skip for checks, must retain
++i;
}
}
for (; i < len; ++i) {
char c = s.charAt(i);
// if other symbols, parse as Double, coerce
if (c > '9' || c < '0') {
try {
return (long) parseDouble(s);
} catch (NumberFormatException e) {
return def;
}
}
}
try {
return Long.parseLong(s);
} catch (NumberFormatException e) { }
return def;
}
public static double parseAsDouble(String s, double def)
{
if (s == null) { return def; }
s = s.trim();
int len = s.length();
if (len == 0) {
return def;
}
try {
return parseDouble(s);
} catch (NumberFormatException e) { }
return def;
}
public static double parseDouble(String s) throws NumberFormatException {
// [JACKSON-486]: avoid some nasty float representations... but should it be MIN_NORMAL or MIN_VALUE?
/* as per [JACKSON-827], let's use MIN_VALUE as it is available on all JDKs; normalized
* only in JDK 1.6. In practice, should not really matter.
*/
if (NASTY_SMALL_DOUBLE.equals(s)) {
return Double.MIN_VALUE;
}
return Double.parseDouble(s);
}
public static BigDecimal parseBigDecimal(String s) throws NumberFormatException {
try { return new BigDecimal(s); } catch (NumberFormatException e) {
throw _badBD(s);
}
}
public static BigDecimal parseBigDecimal(char[] b) throws NumberFormatException {
return parseBigDecimal(b, 0, b.length);
}
public static BigDecimal parseBigDecimal(char[] b, int off, int len) throws NumberFormatException {
try { return new BigDecimal(b, off, len); } catch (NumberFormatException e) {
throw _badBD(new String(b, off, len));
}
}
private static NumberFormatException _badBD(String s) {
return new NumberFormatException("Value \""+s+"\" can not be represented as BigDecimal");
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/NumberOutput.java 0000664 0000000 0000000 00000037324 13561642473 0032273 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.io;
public final class NumberOutput
{
private static int MILLION = 1000000;
private static int BILLION = 1000000000;
private static long BILLION_L = 1000000000L;
private static long MIN_INT_AS_LONG = (long) Integer.MIN_VALUE;
private static long MAX_INT_AS_LONG = (long) Integer.MAX_VALUE;
final static String SMALLEST_INT = String.valueOf(Integer.MIN_VALUE);
final static String SMALLEST_LONG = String.valueOf(Long.MIN_VALUE);
/**
* Encoded representations of 3-decimal-digit indexed values, where
* 3 LSB are ascii characters
*
* @since 2.8.2
*/
private final static int[] TRIPLET_TO_CHARS = new int[1000];
static {
/* Let's fill it with NULLs for ignorable leading digits,
* and digit chars for others
*/
int fullIx = 0;
for (int i1 = 0; i1 < 10; ++i1) {
for (int i2 = 0; i2 < 10; ++i2) {
for (int i3 = 0; i3 < 10; ++i3) {
int enc = ((i1 + '0') << 16)
| ((i2 + '0') << 8)
| (i3 + '0');
TRIPLET_TO_CHARS[fullIx++] = enc;
}
}
}
}
private final static String[] sSmallIntStrs = new String[] {
"0","1","2","3","4","5","6","7","8","9","10"
};
private final static String[] sSmallIntStrs2 = new String[] {
"-1","-2","-3","-4","-5","-6","-7","-8","-9","-10"
};
/*
/**********************************************************
/* Efficient serialization methods using raw buffers
/**********************************************************
*/
/**
* @return Offset within buffer after outputting int
*/
public static int outputInt(int v, char[] b, int off)
{
if (v < 0) {
if (v == Integer.MIN_VALUE) {
// Special case: no matching positive value within range;
// let's then "upgrade" to long and output as such.
return _outputSmallestI(b, off);
}
b[off++] = '-';
v = -v;
}
if (v < MILLION) { // at most 2 triplets...
if (v < 1000) {
if (v < 10) {
b[off] = (char) ('0' + v);
return off+1;
}
return _leading3(v, b, off);
}
int thousands = v / 1000;
v -= (thousands * 1000); // == value % 1000
off = _leading3(thousands, b, off);
off = _full3(v, b, off);
return off;
}
// ok, all 3 triplets included
/* Let's first hand possible billions separately before
* handling 3 triplets. This is possible since we know we
* can have at most '2' as billion count.
*/
if (v >= BILLION) {
v -= BILLION;
if (v >= BILLION) {
v -= BILLION;
b[off++] = '2';
} else {
b[off++] = '1';
}
return _outputFullBillion(v, b, off);
}
int newValue = v / 1000;
int ones = (v - (newValue * 1000)); // == value % 1000
v = newValue;
newValue /= 1000;
int thousands = (v - (newValue * 1000));
off = _leading3(newValue, b, off);
off = _full3(thousands, b, off);
return _full3(ones, b, off);
}
public static int outputInt(int v, byte[] b, int off)
{
if (v < 0) {
if (v == Integer.MIN_VALUE) {
return _outputSmallestI(b, off);
}
b[off++] = '-';
v = -v;
}
if (v < MILLION) { // at most 2 triplets...
if (v < 1000) {
if (v < 10) {
b[off++] = (byte) ('0' + v);
} else {
off = _leading3(v, b, off);
}
} else {
int thousands = v / 1000;
v -= (thousands * 1000); // == value % 1000
off = _leading3(thousands, b, off);
off = _full3(v, b, off);
}
return off;
}
if (v >= BILLION) {
v -= BILLION;
if (v >= BILLION) {
v -= BILLION;
b[off++] = '2';
} else {
b[off++] = '1';
}
return _outputFullBillion(v, b, off);
}
int newValue = v / 1000;
int ones = (v - (newValue * 1000)); // == value % 1000
v = newValue;
newValue /= 1000;
int thousands = (v - (newValue * 1000));
off = _leading3(newValue, b, off);
off = _full3(thousands, b, off);
return _full3(ones, b, off);
}
/**
* @return Offset within buffer after outputting int
*/
public static int outputLong(long v, char[] b, int off)
{
// First: does it actually fit in an int?
if (v < 0L) {
if (v > MIN_INT_AS_LONG) {
return outputInt((int) v, b, off);
}
if (v == Long.MIN_VALUE) {
return _outputSmallestL(b, off);
}
b[off++] = '-';
v = -v;
} else {
if (v <= MAX_INT_AS_LONG) {
return outputInt((int) v, b, off);
}
}
// Ok, let's separate last 9 digits (3 x full sets of 3)
long upper = v / BILLION_L;
v -= (upper * BILLION_L);
// two integers?
if (upper < BILLION_L) {
off = _outputUptoBillion((int) upper, b, off);
} else {
// no, two ints and bits; hi may be about 16 or so
long hi = upper / BILLION_L;
upper -= (hi * BILLION_L);
off = _leading3((int) hi, b, off);
off = _outputFullBillion((int) upper, b, off);
}
return _outputFullBillion((int) v, b, off);
}
public static int outputLong(long v, byte[] b, int off)
{
if (v < 0L) {
if (v > MIN_INT_AS_LONG) {
return outputInt((int) v, b, off);
}
if (v == Long.MIN_VALUE) {
return _outputSmallestL(b, off);
}
b[off++] = '-';
v = -v;
} else {
if (v <= MAX_INT_AS_LONG) {
return outputInt((int) v, b, off);
}
}
// Ok, let's separate last 9 digits (3 x full sets of 3)
long upper = v / BILLION_L;
v -= (upper * BILLION_L);
// two integers?
if (upper < BILLION_L) {
off = _outputUptoBillion((int) upper, b, off);
} else {
// no, two ints and bits; hi may be about 16 or so
long hi = upper / BILLION_L;
upper -= (hi * BILLION_L);
off = _leading3((int) hi, b, off);
off = _outputFullBillion((int) upper, b, off);
}
return _outputFullBillion((int) v, b, off);
}
/*
/**********************************************************
/* Convenience serialization methods
/**********************************************************
*/
/* !!! 05-Aug-2008, tatus: Any ways to further optimize
* these? (or need: only called by diagnostics methods?)
*/
public static String toString(int v)
{
// Lookup table for small values
if (v < sSmallIntStrs.length) {
if (v >= 0) {
return sSmallIntStrs[v];
}
int v2 = -v - 1;
if (v2 < sSmallIntStrs2.length) {
return sSmallIntStrs2[v2];
}
}
return Integer.toString(v);
}
public static String toString(long v) {
if (v <= Integer.MAX_VALUE && v >= Integer.MIN_VALUE) {
return toString((int) v);
}
return Long.toString(v);
}
public static String toString(double v) {
return Double.toString(v);
}
/**
* @since 2.6.0
*/
public static String toString(float v) {
return Float.toString(v);
}
/*
/**********************************************************
/* Other convenience methods
/**********************************************************
*/
/**
* Helper method to verify whether given {@code double} value is finite
* (regular rational number} or not (NaN or Infinity).
*
* @return True if number is NOT finite (is Infinity or NaN); false otherwise
*
* Since 2.10
*/
public static boolean notFinite(double value) {
// before Java 8 need separate checks
return Double.isNaN(value) || Double.isInfinite(value);
}
/**
* Helper method to verify whether given {@code float} value is finite
* (regular rational number} or not (NaN or Infinity).
*
* @return True if number is NOT finite (is Infinity or NaN); false otherwise
*
* Since 2.10
*/
public static boolean notFinite(float value) {
// before Java 8 need separate checks
return Float.isNaN(value) || Float.isInfinite(value);
}
/*
/**********************************************************
/* Internal helper methods
/**********************************************************
*/
private static int _outputUptoBillion(int v, char[] b, int off)
{
if (v < MILLION) { // at most 2 triplets...
if (v < 1000) {
return _leading3(v, b, off);
}
int thousands = v / 1000;
int ones = v - (thousands * 1000); // == value % 1000
return _outputUptoMillion(b, off, thousands, ones);
}
int thousands = v / 1000;
int ones = (v - (thousands * 1000)); // == value % 1000
int millions = thousands / 1000;
thousands -= (millions * 1000);
off = _leading3(millions, b, off);
int enc = TRIPLET_TO_CHARS[thousands];
b[off++] = (char) (enc >> 16);
b[off++] = (char) ((enc >> 8) & 0x7F);
b[off++] = (char) (enc & 0x7F);
enc = TRIPLET_TO_CHARS[ones];
b[off++] = (char) (enc >> 16);
b[off++] = (char) ((enc >> 8) & 0x7F);
b[off++] = (char) (enc & 0x7F);
return off;
}
private static int _outputFullBillion(int v, char[] b, int off)
{
int thousands = v / 1000;
int ones = (v - (thousands * 1000)); // == value % 1000
int millions = thousands / 1000;
int enc = TRIPLET_TO_CHARS[millions];
b[off++] = (char) (enc >> 16);
b[off++] = (char) ((enc >> 8) & 0x7F);
b[off++] = (char) (enc & 0x7F);
thousands -= (millions * 1000);
enc = TRIPLET_TO_CHARS[thousands];
b[off++] = (char) (enc >> 16);
b[off++] = (char) ((enc >> 8) & 0x7F);
b[off++] = (char) (enc & 0x7F);
enc = TRIPLET_TO_CHARS[ones];
b[off++] = (char) (enc >> 16);
b[off++] = (char) ((enc >> 8) & 0x7F);
b[off++] = (char) (enc & 0x7F);
return off;
}
private static int _outputUptoBillion(int v, byte[] b, int off)
{
if (v < MILLION) { // at most 2 triplets...
if (v < 1000) {
return _leading3(v, b, off);
}
int thousands = v / 1000;
int ones = v - (thousands * 1000); // == value % 1000
return _outputUptoMillion(b, off, thousands, ones);
}
int thousands = v / 1000;
int ones = (v - (thousands * 1000)); // == value % 1000
int millions = thousands / 1000;
thousands -= (millions * 1000);
off = _leading3(millions, b, off);
int enc = TRIPLET_TO_CHARS[thousands];
b[off++] = (byte) (enc >> 16);
b[off++] = (byte) (enc >> 8);
b[off++] = (byte) enc;
enc = TRIPLET_TO_CHARS[ones];
b[off++] = (byte) (enc >> 16);
b[off++] = (byte) (enc >> 8);
b[off++] = (byte) enc;
return off;
}
private static int _outputFullBillion(int v, byte[] b, int off)
{
int thousands = v / 1000;
int ones = (v - (thousands * 1000)); // == value % 1000
int millions = thousands / 1000;
thousands -= (millions * 1000);
int enc = TRIPLET_TO_CHARS[millions];
b[off++] = (byte) (enc >> 16);
b[off++] = (byte) (enc >> 8);
b[off++] = (byte) enc;
enc = TRIPLET_TO_CHARS[thousands];
b[off++] = (byte) (enc >> 16);
b[off++] = (byte) (enc >> 8);
b[off++] = (byte) enc;
enc = TRIPLET_TO_CHARS[ones];
b[off++] = (byte) (enc >> 16);
b[off++] = (byte) (enc >> 8);
b[off++] = (byte) enc;
return off;
}
private static int _outputUptoMillion(char[] b, int off, int thousands, int ones)
{
int enc = TRIPLET_TO_CHARS[thousands];
if (thousands > 9) {
if (thousands > 99) {
b[off++] = (char) (enc >> 16);
}
b[off++] = (char) ((enc >> 8) & 0x7F);
}
b[off++] = (char) (enc & 0x7F);
// and then full
enc = TRIPLET_TO_CHARS[ones];
b[off++] = (char) (enc >> 16);
b[off++] = (char) ((enc >> 8) & 0x7F);
b[off++] = (char) (enc & 0x7F);
return off;
}
private static int _outputUptoMillion(byte[] b, int off, int thousands, int ones)
{
int enc = TRIPLET_TO_CHARS[thousands];
if (thousands > 9) {
if (thousands > 99) {
b[off++] = (byte) (enc >> 16);
}
b[off++] = (byte) (enc >> 8);
}
b[off++] = (byte) enc;
// and then full
enc = TRIPLET_TO_CHARS[ones];
b[off++] = (byte) (enc >> 16);
b[off++] = (byte) (enc >> 8);
b[off++] = (byte) enc;
return off;
}
private static int _leading3(int t, char[] b, int off)
{
int enc = TRIPLET_TO_CHARS[t];
if (t > 9) {
if (t > 99) {
b[off++] = (char) (enc >> 16);
}
b[off++] = (char) ((enc >> 8) & 0x7F);
}
b[off++] = (char) (enc & 0x7F);
return off;
}
private static int _leading3(int t, byte[] b, int off)
{
int enc = TRIPLET_TO_CHARS[t];
if (t > 9) {
if (t > 99) {
b[off++] = (byte) (enc >> 16);
}
b[off++] = (byte) (enc >> 8);
}
b[off++] = (byte) enc;
return off;
}
private static int _full3(int t, char[] b, int off)
{
int enc = TRIPLET_TO_CHARS[t];
b[off++] = (char) (enc >> 16);
b[off++] = (char) ((enc >> 8) & 0x7F);
b[off++] = (char) (enc & 0x7F);
return off;
}
private static int _full3(int t, byte[] b, int off)
{
int enc = TRIPLET_TO_CHARS[t];
b[off++] = (byte) (enc >> 16);
b[off++] = (byte) (enc >> 8);
b[off++] = (byte) enc;
return off;
}
// // // Special cases for where we can not flip the sign bit
private static int _outputSmallestL(char[] b, int off)
{
int len = SMALLEST_LONG.length();
SMALLEST_LONG.getChars(0, len, b, off);
return (off + len);
}
private static int _outputSmallestL(byte[] b, int off)
{
int len = SMALLEST_LONG.length();
for (int i = 0; i < len; ++i) {
b[off++] = (byte) SMALLEST_LONG.charAt(i);
}
return off;
}
private static int _outputSmallestI(char[] b, int off)
{
int len = SMALLEST_INT.length();
SMALLEST_INT.getChars(0, len, b, off);
return (off + len);
}
private static int _outputSmallestI(byte[] b, int off)
{
int len = SMALLEST_INT.length();
for (int i = 0; i < len; ++i) {
b[off++] = (byte) SMALLEST_INT.charAt(i);
}
return off;
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/OutputDecorator.java 0000664 0000000 0000000 00000002672 13561642473 0032763 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.io;
import java.io.*;
/**
* Handler class that can be used to decorate output destinations.
* Typical use is to use a filter abstraction (filtered output stream,
* writer) around original output destination, and apply additional
* processing during write operations.
*/
@SuppressWarnings("serial")
public abstract class OutputDecorator implements java.io.Serializable // since 2.1
{
/**
* Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when
* creating generator for given {@link OutputStream}, when this decorator
* has been registered.
*
* @param ctxt IO context in use (provides access to declared encoding)
* @param out Original output destination
*
* @return OutputStream to use; either passed in argument, or something that
* calls it
*/
public abstract OutputStream decorate(IOContext ctxt, OutputStream out) throws IOException;
/**
* Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when
* creating generator for given {@link Writer}, when this decorator
* has been registered.
*
* @param ctxt IO context in use (provides access to declared encoding)
* @param w Original output writer
*
* @return Writer to use; either passed in argument, or something that calls it
*/
public abstract Writer decorate(IOContext ctxt, Writer w) throws IOException;
}
SegmentedStringWriter.java 0000664 0000000 0000000 00000004732 13561642473 0034037 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io package 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
*
* Class is final for performance reasons and since this is not designed to
* be extensible or customizable (customizations would occur in calling code)
*/
public class SerializedString
implements SerializableString, java.io.Serializable
{
private static final long serialVersionUID = 1L;
private static final JsonStringEncoder JSON_ENCODER = JsonStringEncoder.getInstance();
protected final String _value;
/* 13-Dec-2010, tatu: Whether use volatile or not is actually an important
* decision for multi-core use cases. Cost of volatility can be non-trivial
* for heavy use cases, and serialized-string instances are accessed often.
* Given that all code paths with common Jackson usage patterns go through
* a few memory barriers (mostly with cache/reuse pool access) it seems safe
* enough to omit volatiles here, given how simple lazy initialization is.
* This can be compared to how {@link String#hashCode} works; lazily and
* without synchronization or use of volatile keyword.
*
* Change to remove volatile was a request by implementors of a high-throughput
* search framework; and they believed this is an important optimization for
* heaviest, multi-core deployed use cases.
*/
/*
* 22-Sep-2013, tatu: FWIW, there have been no reports of problems in this
* area, or anything pointing to it. So I think we are safe up to JDK7
* and hopefully beyond.
*/
protected /*volatile*/ byte[] _quotedUTF8Ref;
protected /*volatile*/ byte[] _unquotedUTF8Ref;
protected /*volatile*/ char[] _quotedChars;
public SerializedString(String v) {
if (v == null) {
throw new IllegalStateException("Null String illegal for SerializedString");
}
_value = v;
}
/*
/**********************************************************
/* Serializable overrides
/**********************************************************
*/
/**
* Ugly hack, to work through the requirement that _value is indeed final,
* and that JDK serialization won't call ctor(s).
*
* @since 2.1
*/
protected transient String _jdkSerializeValue;
private void readObject(ObjectInputStream in) throws IOException {
_jdkSerializeValue = in.readUTF();
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeUTF(_value);
}
protected Object readResolve() {
return new SerializedString(_jdkSerializeValue);
}
/*
/**********************************************************
/* API
/**********************************************************
*/
@Override
public final String getValue() { return _value; }
/**
* Returns length of the String as characters
*/
@Override
public final int charLength() { return _value.length(); }
/**
* Accessor for accessing value that has been quoted (escaped) using JSON
* quoting rules (using backslash-prefixed codes) into a char array.
*/
@Override
public final char[] asQuotedChars() {
char[] result = _quotedChars;
if (result == null) {
_quotedChars = result = JSON_ENCODER.quoteAsString(_value);
}
return result;
}
/**
* Accessor for accessing value that has been quoted (escaped) using JSON
* quoting rules (using backslash-prefixed codes), and encoded using
* UTF-8 encoding into a byte array.
*/
@Override
public final byte[] asQuotedUTF8() {
byte[] result = _quotedUTF8Ref;
if (result == null) {
_quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value);
}
return result;
}
/**
* Accessor for accessing value as is (without JSON quoting (ecaping))
* encoded as UTF-8 byte array.
*/
@Override
public final byte[] asUnquotedUTF8() {
byte[] result = _unquotedUTF8Ref;
if (result == null) {
_unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value);
}
return result;
}
/*
/**********************************************************
/* Additional 2.0 methods for appending/writing contents
/**********************************************************
*/
@Override
public int appendQuoted(char[] buffer, int offset) {
char[] result = _quotedChars;
if (result == null) {
_quotedChars = result = JSON_ENCODER.quoteAsString(_value);
}
final int length = result.length;
if ((offset + length) > buffer.length) {
return -1;
}
System.arraycopy(result, 0, buffer, offset, length);
return length;
}
@Override
public int appendQuotedUTF8(byte[] buffer, int offset) {
byte[] result = _quotedUTF8Ref;
if (result == null) {
_quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value);
}
final int length = result.length;
if ((offset + length) > buffer.length) {
return -1;
}
System.arraycopy(result, 0, buffer, offset, length);
return length;
}
@Override
public int appendUnquoted(char[] buffer, int offset) {
String str = _value;
final int length = str.length();
if ((offset + length) > buffer.length) {
return -1;
}
str.getChars(0, length, buffer, offset);
return length;
}
@Override
public int appendUnquotedUTF8(byte[] buffer, int offset) {
byte[] result = _unquotedUTF8Ref;
if (result == null) {
_unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value);
}
final int length = result.length;
if ((offset + length) > buffer.length) {
return -1;
}
System.arraycopy(result, 0, buffer, offset, length);
return length;
}
@Override
public int writeQuotedUTF8(OutputStream out) throws IOException {
byte[] result = _quotedUTF8Ref;
if (result == null) {
_quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value);
}
final int length = result.length;
out.write(result, 0, length);
return length;
}
@Override
public int writeUnquotedUTF8(OutputStream out) throws IOException {
byte[] result = _unquotedUTF8Ref;
if (result == null) {
_unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value);
}
final int length = result.length;
out.write(result, 0, length);
return length;
}
@Override
public int putQuotedUTF8(ByteBuffer buffer) {
byte[] result = _quotedUTF8Ref;
if (result == null) {
_quotedUTF8Ref = result = JSON_ENCODER.quoteAsUTF8(_value);
}
final int length = result.length;
if (length > buffer.remaining()) {
return -1;
}
buffer.put(result, 0, length);
return length;
}
@Override
public int putUnquotedUTF8(ByteBuffer buffer) {
byte[] result = _unquotedUTF8Ref;
if (result == null) {
_unquotedUTF8Ref = result = JSON_ENCODER.encodeAsUTF8(_value);
}
final int length = result.length;
if (length > buffer.remaining()) {
return -1;
}
buffer.put(result, 0, length);
return length;
}
/*
/**********************************************************
/* Standard method overrides
/**********************************************************
*/
@Override
public final String toString() { return _value; }
@Override
public final int hashCode() { return _value.hashCode(); }
@Override
public final boolean equals(Object o) {
if (o == this) return true;
if (o == null || o.getClass() != getClass()) return false;
SerializedString other = (SerializedString) o;
return _value.equals(other._value);
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/UTF32Reader.java 0000664 0000000 0000000 00000021441 13561642473 0031541 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.io;
import java.io.*;
/**
* Since JDK does not come with UTF-32/UCS-4, let's implement a simple
* decoder to use.
*/
public class UTF32Reader extends Reader
{
/**
* JSON actually limits available Unicode range in the high end
* to the same as xml (to basically limit UTF-8 max byte sequence
* length to 4)
*/
final protected static int LAST_VALID_UNICODE_CHAR = 0x10FFFF;
final protected static char NC = (char) 0;
final protected IOContext _context;
protected InputStream _in;
protected byte[] _buffer;
protected int _ptr;
protected int _length;
protected final boolean _bigEndian;
/**
* Although input is fine with full Unicode set, Java still uses
* 16-bit chars, so we may have to split high-order chars into
* surrogate pairs.
*/
protected char _surrogate = NC;
/**
* Total read character count; used for error reporting purposes
*/
protected int _charCount;
/**
* Total read byte count; used for error reporting purposes
*/
protected int _byteCount;
protected final boolean _managedBuffers;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public UTF32Reader(IOContext ctxt, InputStream in, byte[] buf, int ptr, int len, boolean isBigEndian) {
_context = ctxt;
_in = in;
_buffer = buf;
_ptr = ptr;
_length = len;
_bigEndian = isBigEndian;
_managedBuffers = (in != null);
}
/*
/**********************************************************
/* Public API
/**********************************************************
*/
@Override
public void close() throws IOException {
InputStream in = _in;
if (in != null) {
_in = null;
freeBuffers();
in.close();
}
}
protected char[] _tmpBuf;
/**
* Although this method is implemented by the base class, AND it should
* never be called by main code, let's still implement it bit more
* efficiently just in case
*/
@Override
public int read() throws IOException {
if (_tmpBuf == null) {
_tmpBuf = new char[1];
}
if (read(_tmpBuf, 0, 1) < 1) {
return -1;
}
return _tmpBuf[0];
}
@Override
public int read(char[] cbuf, int start, int len) throws IOException {
// Already EOF?
if (_buffer == null) { return -1; }
if (len < 1) { return len; }
// Let's then ensure there's enough room...
if (start < 0 || (start+len) > cbuf.length) {
reportBounds(cbuf, start, len);
}
int outPtr = start;
final int outEnd = len+start;
// Ok, first; do we have a surrogate from last round?
if (_surrogate != NC) {
cbuf[outPtr++] = _surrogate;
_surrogate = NC;
// No need to load more, already got one char
} else {
// Note: we'll try to avoid blocking as much as possible. As a
// result, we only need to get 4 bytes for a full char.
int left = (_length - _ptr);
if (left < 4) {
if (!loadMore(left)) { // (legal) EOF?
// Ok if (but only if!) was at boundary
if (left == 0) {
return -1;
}
reportUnexpectedEOF(_length - _ptr, 4);
}
}
}
// 02-Jun-2017, tatu: Must ensure we don't try to read past buffer end:
final int lastValidInputStart = (_length - 4);
main_loop:
while (outPtr < outEnd) {
int ptr = _ptr;
int hi, lo;
if (_bigEndian) {
hi = (_buffer[ptr] << 8) | (_buffer[ptr+1] & 0xFF);
lo = ((_buffer[ptr+2] & 0xFF) << 8) | (_buffer[ptr+3] & 0xFF);
} else {
lo = (_buffer[ptr] & 0xFF) | ((_buffer[ptr+1] & 0xFF) << 8);
hi = (_buffer[ptr+2] & 0xFF)| (_buffer[ptr+3] << 8);
}
_ptr += 4;
// Does it need to be split to surrogates?
// (also, we can and need to verify illegal chars)
if (hi != 0) { // need to split into surrogates?
hi &= 0xFFFF; // since it may be sign extended
int ch = ((hi - 1) << 16) | lo; // ch -= 0x10000; to normalize starting with 0x0
if (hi > 0x10) { // last valid is 0x10FFFF
reportInvalid(ch, outPtr-start,
String.format(" (above 0x%08x)", LAST_VALID_UNICODE_CHAR));
}
cbuf[outPtr++] = (char) (0xD800 + (ch >> 10));
// hmmh. can this ever be 0? (not legal, at least?)
lo = (0xDC00 | (ch & 0x03FF));
// Room for second part?
if (outPtr >= outEnd) { // nope
_surrogate = (char) ch;
break main_loop;
}
}
cbuf[outPtr++] = (char) lo;
if (_ptr > lastValidInputStart) {
break main_loop;
}
}
int actualLen = (outPtr - start);
_charCount += actualLen;
return actualLen;
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
private void reportUnexpectedEOF(int gotBytes, int needed) throws IOException {
int bytePos = _byteCount + gotBytes, charPos = _charCount;
throw new CharConversionException("Unexpected EOF in the middle of a 4-byte UTF-32 char: got "+gotBytes+", needed "+needed+", at char #"+charPos+", byte #"+bytePos+")");
}
private void reportInvalid(int value, int offset, String msg) throws IOException {
int bytePos = _byteCount + _ptr - 1, charPos = _charCount + offset;
throw new CharConversionException("Invalid UTF-32 character 0x"+Integer.toHexString(value)+msg+" at char #"+charPos+", byte #"+bytePos+")");
}
/**
* @param available Number of "unused" bytes in the input buffer
*
* @return True, if enough bytes were read to allow decoding of at least
* one full character; false if EOF was encountered instead.
*/
private boolean loadMore(int available) throws IOException {
_byteCount += (_length - available);
// Bytes that need to be moved to the beginning of buffer?
if (available > 0) {
if (_ptr > 0) {
System.arraycopy(_buffer, _ptr, _buffer, 0, available);
_ptr = 0;
}
_length = available;
} else {
/* Ok; here we can actually reasonably expect an EOF,
* so let's do a separate read right away:
*/
_ptr = 0;
int count = (_in == null) ? -1 : _in.read(_buffer);
if (count < 1) {
_length = 0;
if (count < 0) { // -1
if (_managedBuffers) {
freeBuffers(); // to help GC?
}
return false;
}
// 0 count is no good; let's err out
reportStrangeStream();
}
_length = count;
}
/* Need at least 4 bytes; if we don't get that many, it's an
* error.
*/
while (_length < 4) {
int count = (_in == null) ? -1 : _in.read(_buffer, _length, _buffer.length - _length);
if (count < 1) {
if (count < 0) { // -1, EOF... no good!
if (_managedBuffers) {
freeBuffers(); // to help GC?
}
reportUnexpectedEOF(_length, 4);
}
// 0 count is no good; let's err out
reportStrangeStream();
}
_length += count;
}
return true;
}
/**
* This method should be called along with (or instead of) normal
* close. After calling this method, no further reads should be tried.
* Method will try to recycle read buffers (if any).
*/
private void freeBuffers() {
byte[] buf = _buffer;
if (buf != null) {
_buffer = null;
_context.releaseReadIOBuffer(buf);
}
}
private void reportBounds(char[] cbuf, int start, int len) throws IOException {
throw new ArrayIndexOutOfBoundsException("read(buf,"+start+","+len+"), cbuf["+cbuf.length+"]");
}
private void reportStrangeStream() throws IOException {
throw new IOException("Strange I/O stream, returned 0 bytes on read");
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/UTF8Writer.java 0000664 0000000 0000000 00000031352 13561642473 0031540 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.io;
import java.io.*;
public final class UTF8Writer extends Writer
{
final static int SURR1_FIRST = 0xD800;
final static int SURR1_LAST = 0xDBFF;
final static int SURR2_FIRST = 0xDC00;
final static int SURR2_LAST = 0xDFFF;
final private IOContext _context;
private OutputStream _out;
private byte[] _outBuffer;
final private int _outBufferEnd;
private int _outPtr;
/**
* When outputting chars from BMP, surrogate pairs need to be coalesced.
* To do this, both pairs must be known first; and since it is possible
* pairs may be split, we need temporary storage for the first half
*/
private int _surrogate;
public UTF8Writer(IOContext ctxt, OutputStream out)
{
_context = ctxt;
_out = out;
_outBuffer = ctxt.allocWriteEncodingBuffer();
/* Max. expansion for a single char (in unmodified UTF-8) is
* 4 bytes (or 3 depending on how you view it -- 4 when recombining
* surrogate pairs)
*/
_outBufferEnd = _outBuffer.length - 4;
_outPtr = 0;
}
@Override
public Writer append(char c)
throws IOException
{
write(c);
return this;
}
@Override
public void close()
throws IOException
{
if (_out != null) {
if (_outPtr > 0) {
_out.write(_outBuffer, 0, _outPtr);
_outPtr = 0;
}
OutputStream out = _out;
_out = null;
byte[] buf = _outBuffer;
if (buf != null) {
_outBuffer = null;
_context.releaseWriteEncodingBuffer(buf);
}
out.close();
/* Let's 'flush' orphan surrogate, no matter what; but only
* after cleanly closing everything else.
*/
int code = _surrogate;
_surrogate = 0;
if (code > 0) {
illegalSurrogate(code);
}
}
}
@Override
public void flush()
throws IOException
{
if (_out != null) {
if (_outPtr > 0) {
_out.write(_outBuffer, 0, _outPtr);
_outPtr = 0;
}
_out.flush();
}
}
@Override
public void write(char[] cbuf)
throws IOException
{
write(cbuf, 0, cbuf.length);
}
@Override
public void write(char[] cbuf, int off, int len)
throws IOException
{
if (len < 2) {
if (len == 1) {
write(cbuf[off]);
}
return;
}
// First: do we have a leftover surrogate to deal with?
if (_surrogate > 0) {
char second = cbuf[off++];
--len;
write(convertSurrogate(second));
// will have at least one more char
}
int outPtr = _outPtr;
byte[] outBuf = _outBuffer;
int outBufLast = _outBufferEnd; // has 4 'spare' bytes
// All right; can just loop it nice and easy now:
len += off; // len will now be the end of input buffer
output_loop:
for (; off < len; ) {
/* First, let's ensure we can output at least 4 bytes
* (longest UTF-8 encoded codepoint):
*/
if (outPtr >= outBufLast) {
_out.write(outBuf, 0, outPtr);
outPtr = 0;
}
int c = cbuf[off++];
// And then see if we have an Ascii char:
if (c < 0x80) { // If so, can do a tight inner loop:
outBuf[outPtr++] = (byte)c;
// Let's calc how many ascii chars we can copy at most:
int maxInCount = (len - off);
int maxOutCount = (outBufLast - outPtr);
if (maxInCount > maxOutCount) {
maxInCount = maxOutCount;
}
maxInCount += off;
ascii_loop:
while (true) {
if (off >= maxInCount) { // done with max. ascii seq
continue output_loop;
}
c = cbuf[off++];
if (c >= 0x80) {
break ascii_loop;
}
outBuf[outPtr++] = (byte) c;
}
}
// Nope, multi-byte:
if (c < 0x800) { // 2-byte
outBuf[outPtr++] = (byte) (0xc0 | (c >> 6));
outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
} else { // 3 or 4 bytes
// Surrogates?
if (c < SURR1_FIRST || c > SURR2_LAST) {
outBuf[outPtr++] = (byte) (0xe0 | (c >> 12));
outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
continue;
}
// Yup, a surrogate:
if (c > SURR1_LAST) { // must be from first range
_outPtr = outPtr;
illegalSurrogate(c);
}
_surrogate = c;
// and if so, followed by another from next range
if (off >= len) { // unless we hit the end?
break;
}
c = convertSurrogate(cbuf[off++]);
if (c > 0x10FFFF) { // illegal in JSON as well as in XML
_outPtr = outPtr;
illegalSurrogate(c);
}
outBuf[outPtr++] = (byte) (0xf0 | (c >> 18));
outBuf[outPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
}
}
_outPtr = outPtr;
}
@Override
public void write(int c) throws IOException
{
// First; do we have a left over surrogate?
if (_surrogate > 0) {
c = convertSurrogate(c);
// If not, do we start with a surrogate?
} else if (c >= SURR1_FIRST && c <= SURR2_LAST) {
// Illegal to get second part without first:
if (c > SURR1_LAST) {
illegalSurrogate(c);
}
// First part just needs to be held for now
_surrogate = c;
return;
}
if (_outPtr >= _outBufferEnd) { // let's require enough room, first
_out.write(_outBuffer, 0, _outPtr);
_outPtr = 0;
}
if (c < 0x80) { // ascii
_outBuffer[_outPtr++] = (byte) c;
} else {
int ptr = _outPtr;
if (c < 0x800) { // 2-byte
_outBuffer[ptr++] = (byte) (0xc0 | (c >> 6));
_outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f));
} else if (c <= 0xFFFF) { // 3 bytes
_outBuffer[ptr++] = (byte) (0xe0 | (c >> 12));
_outBuffer[ptr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
_outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f));
} else { // 4 bytes
if (c > 0x10FFFF) { // illegal
illegalSurrogate(c);
}
_outBuffer[ptr++] = (byte) (0xf0 | (c >> 18));
_outBuffer[ptr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
_outBuffer[ptr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
_outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f));
}
_outPtr = ptr;
}
}
@Override
public void write(String str) throws IOException
{
write(str, 0, str.length());
}
@Override
public void write(String str, int off, int len) throws IOException
{
if (len < 2) {
if (len == 1) {
write(str.charAt(off));
}
return;
}
// First: do we have a leftover surrogate to deal with?
if (_surrogate > 0) {
char second = str.charAt(off++);
--len;
write(convertSurrogate(second));
// will have at least one more char (case of 1 char was checked earlier on)
}
int outPtr = _outPtr;
byte[] outBuf = _outBuffer;
int outBufLast = _outBufferEnd; // has 4 'spare' bytes
// All right; can just loop it nice and easy now:
len += off; // len will now be the end of input buffer
output_loop:
for (; off < len; ) {
/* First, let's ensure we can output at least 4 bytes
* (longest UTF-8 encoded codepoint):
*/
if (outPtr >= outBufLast) {
_out.write(outBuf, 0, outPtr);
outPtr = 0;
}
int c = str.charAt(off++);
// And then see if we have an Ascii char:
if (c < 0x80) { // If so, can do a tight inner loop:
outBuf[outPtr++] = (byte)c;
// Let's calc how many ascii chars we can copy at most:
int maxInCount = (len - off);
int maxOutCount = (outBufLast - outPtr);
if (maxInCount > maxOutCount) {
maxInCount = maxOutCount;
}
maxInCount += off;
ascii_loop:
while (true) {
if (off >= maxInCount) { // done with max. ascii seq
continue output_loop;
}
c = str.charAt(off++);
if (c >= 0x80) {
break ascii_loop;
}
outBuf[outPtr++] = (byte) c;
}
}
// Nope, multi-byte:
if (c < 0x800) { // 2-byte
outBuf[outPtr++] = (byte) (0xc0 | (c >> 6));
outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
} else { // 3 or 4 bytes
// Surrogates?
if (c < SURR1_FIRST || c > SURR2_LAST) {
outBuf[outPtr++] = (byte) (0xe0 | (c >> 12));
outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
continue;
}
// Yup, a surrogate:
if (c > SURR1_LAST) { // must be from first range
_outPtr = outPtr;
illegalSurrogate(c);
}
_surrogate = c;
// and if so, followed by another from next range
if (off >= len) { // unless we hit the end?
break;
}
c = convertSurrogate(str.charAt(off++));
if (c > 0x10FFFF) { // illegal, as per RFC 4627
_outPtr = outPtr;
illegalSurrogate(c);
}
outBuf[outPtr++] = (byte) (0xf0 | (c >> 18));
outBuf[outPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
}
}
_outPtr = outPtr;
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
/**
* Method called to calculate UTF codepoint, from a surrogate pair.
*/
protected int convertSurrogate(int secondPart)
throws IOException
{
int firstPart = _surrogate;
_surrogate = 0;
// Ok, then, is the second part valid?
if (secondPart < SURR2_FIRST || secondPart > SURR2_LAST) {
throw new IOException("Broken surrogate pair: first char 0x"+Integer.toHexString(firstPart)+", second 0x"+Integer.toHexString(secondPart)+"; illegal combination");
}
return 0x10000 + ((firstPart - SURR1_FIRST) << 10) + (secondPart - SURR2_FIRST);
}
protected static void illegalSurrogate(int code) throws IOException {
throw new IOException(illegalSurrogateDesc(code));
}
protected static String illegalSurrogateDesc(int code)
{
if (code > 0x10FFFF) { // over max?
return "Illegal character point (0x"+Integer.toHexString(code)+") to output; max is 0x10FFFF as per RFC 4627";
}
if (code >= SURR1_FIRST) {
if (code <= SURR1_LAST) { // Unmatched first part (closing without second part?)
return "Unmatched first part of surrogate pair (0x"+Integer.toHexString(code)+")";
}
return "Unmatched second part of surrogate pair (0x"+Integer.toHexString(code)+")";
}
// should we ever get this?
return "Illegal character point (0x"+Integer.toHexString(code)+") to output";
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/ 0000775 0000000 0000000 00000000000 13561642473 0027310 5 ustar 00root root 0000000 0000000 ByteSourceJsonBootstrapper.java 0000664 0000000 0000000 00000043730 13561642473 0035426 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json package com.fasterxml.jackson.core.json;
import java.io.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.format.InputAccessor;
import com.fasterxml.jackson.core.format.MatchStrength;
import com.fasterxml.jackson.core.io.*;
import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
import com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer;
/**
* This class is used to determine the encoding of byte stream
* that is to contain JSON content. Rules are fairly simple, and
* defined in JSON specification (RFC-4627 or newer), except
* for BOM handling, which is a property of underlying
* streams.
*/
public final class ByteSourceJsonBootstrapper
{
public final static byte UTF8_BOM_1 = (byte) 0xEF;
public final static byte UTF8_BOM_2 = (byte) 0xBB;
public final static byte UTF8_BOM_3 = (byte) 0xBF;
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
private final IOContext _context;
private final InputStream _in;
/*
/**********************************************************
/* Input buffering
/**********************************************************
*/
private final byte[] _inputBuffer;
private int _inputPtr;
private int _inputEnd;
/**
* Flag that indicates whether buffer above is to be recycled
* after being used or not.
*/
private final boolean _bufferRecyclable;
/*
/**********************************************************
/* Input location
/**********************************************************
*/
/**
* Current number of input units (bytes or chars) that were processed in
* previous blocks,
* before contents of current input buffer.
*
* Note: includes possible BOMs, if those were part of the input.
*/
// private int _inputProcessed;
/*
/**********************************************************
/* Data gathered
/**********************************************************
*/
/**
* Whether input has been detected to be in Big-Endian encoding or not.
*/
private boolean _bigEndian = true;
private int _bytesPerChar; // 0 means "dunno yet"
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public ByteSourceJsonBootstrapper(IOContext ctxt, InputStream in) {
_context = ctxt;
_in = in;
_inputBuffer = ctxt.allocReadIOBuffer();
_inputEnd = _inputPtr = 0;
// _inputProcessed = 0;
_bufferRecyclable = true;
}
public ByteSourceJsonBootstrapper(IOContext ctxt, byte[] inputBuffer, int inputStart, int inputLen) {
_context = ctxt;
_in = null;
_inputBuffer = inputBuffer;
_inputPtr = inputStart;
_inputEnd = (inputStart + inputLen);
// Need to offset this for correct location info
// _inputProcessed = -inputStart;
_bufferRecyclable = false;
}
/*
/**********************************************************
/* Encoding detection during bootstrapping
/**********************************************************
*/
/**
* Method that should be called after constructing an instace.
* It will figure out encoding that content uses, to allow
* for instantiating a proper scanner object.
*/
public JsonEncoding detectEncoding() throws IOException
{
boolean foundEncoding = false;
// First things first: BOM handling
/* Note: we can require 4 bytes to be read, since no
* combination of BOM + valid JSON content can have
* shorter length (shortest valid JSON content is single
* digit char, but BOMs are chosen such that combination
* is always at least 4 chars long)
*/
if (ensureLoaded(4)) {
int quad = (_inputBuffer[_inputPtr] << 24)
| ((_inputBuffer[_inputPtr+1] & 0xFF) << 16)
| ((_inputBuffer[_inputPtr+2] & 0xFF) << 8)
| (_inputBuffer[_inputPtr+3] & 0xFF);
if (handleBOM(quad)) {
foundEncoding = true;
} else {
/* If no BOM, need to auto-detect based on first char;
* this works since it must be 7-bit ascii (wrt. unicode
* compatible encodings, only ones JSON can be transferred
* over)
*/
// UTF-32?
if (checkUTF32(quad)) {
foundEncoding = true;
} else if (checkUTF16(quad >>> 16)) {
foundEncoding = true;
}
}
} else if (ensureLoaded(2)) {
int i16 = ((_inputBuffer[_inputPtr] & 0xFF) << 8)
| (_inputBuffer[_inputPtr+1] & 0xFF);
if (checkUTF16(i16)) {
foundEncoding = true;
}
}
JsonEncoding enc;
/* Not found yet? As per specs, this means it must be UTF-8. */
if (!foundEncoding) {
enc = JsonEncoding.UTF8;
} else {
switch (_bytesPerChar) {
case 1: enc = JsonEncoding.UTF8;
break;
case 2: enc = _bigEndian ? JsonEncoding.UTF16_BE : JsonEncoding.UTF16_LE;
break;
case 4: enc = _bigEndian ? JsonEncoding.UTF32_BE : JsonEncoding.UTF32_LE;
break;
default: throw new RuntimeException("Internal error"); // should never get here
}
}
_context.setEncoding(enc);
return enc;
}
/**
* Helper method that may be called to see if given {@link DataInput}
* has BOM marker, and if so, to skip it.
* @throws IOException
*
* @since 2.8
*/
public static int skipUTF8BOM(DataInput input) throws IOException
{
int b = input.readUnsignedByte();
if (b != 0xEF) {
return b;
}
// since this is not legal byte in JSON otherwise, except
// that we do get BOM; if not, report error
b = input.readUnsignedByte();
if (b != 0xBB) {
throw new IOException("Unexpected byte 0x"+Integer.toHexString(b)
+" following 0xEF; should get 0xBB as part of UTF-8 BOM");
}
b = input.readUnsignedByte();
if (b != 0xBF) {
throw new IOException("Unexpected byte 0x"+Integer.toHexString(b)
+" following 0xEF 0xBB; should get 0xBF as part of UTF-8 BOM");
}
return input.readUnsignedByte();
}
/*
/**********************************************************
/* Constructing a Reader
/**********************************************************
*/
@SuppressWarnings("resource")
public Reader constructReader() throws IOException
{
JsonEncoding enc = _context.getEncoding();
switch (enc.bits()) {
case 8: // only in non-common case where we don't want to do direct mapping
case 16:
{
// First: do we have a Stream? If not, need to create one:
InputStream in = _in;
if (in == null) {
in = new ByteArrayInputStream(_inputBuffer, _inputPtr, _inputEnd);
} else {
/* Also, if we have any read but unused input (usually true),
* need to merge that input in:
*/
if (_inputPtr < _inputEnd) {
in = new MergedStream(_context, in, _inputBuffer, _inputPtr, _inputEnd);
}
}
return new InputStreamReader(in, enc.getJavaName());
}
case 32:
return new UTF32Reader(_context, _in, _inputBuffer, _inputPtr, _inputEnd,
_context.getEncoding().isBigEndian());
}
throw new RuntimeException("Internal error"); // should never get here
}
public JsonParser constructParser(int parserFeatures, ObjectCodec codec,
ByteQuadsCanonicalizer rootByteSymbols, CharsToNameCanonicalizer rootCharSymbols,
int factoryFeatures) throws IOException
{
int prevInputPtr = _inputPtr;
JsonEncoding enc = detectEncoding();
int bytesProcessed = _inputPtr - prevInputPtr;
if (enc == JsonEncoding.UTF8) {
/* and without canonicalization, byte-based approach is not performant; just use std UTF-8 reader
* (which is ok for larger input; not so hot for smaller; but this is not a common case)
*/
if (JsonFactory.Feature.CANONICALIZE_FIELD_NAMES.enabledIn(factoryFeatures)) {
ByteQuadsCanonicalizer can = rootByteSymbols.makeChild(factoryFeatures);
return new UTF8StreamJsonParser(_context, parserFeatures, _in, codec, can,
_inputBuffer, _inputPtr, _inputEnd, bytesProcessed, _bufferRecyclable);
}
}
return new ReaderBasedJsonParser(_context, parserFeatures, constructReader(), codec,
rootCharSymbols.makeChild(factoryFeatures));
}
/*
/**********************************************************
/* Encoding detection for data format auto-detection
/**********************************************************
*/
/**
* Current implementation is not as thorough as other functionality
* ({@link com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper});
* supports UTF-8, for example. But it should work, for now, and can
* be improved as necessary.
*/
public static MatchStrength hasJSONFormat(InputAccessor acc) throws IOException
{
// Ideally we should see "[" or "{"; but if not, we'll accept double-quote (String)
// in future could also consider accepting non-standard matches?
if (!acc.hasMoreBytes()) {
return MatchStrength.INCONCLUSIVE;
}
byte b = acc.nextByte();
// Very first thing, a UTF-8 BOM?
if (b == UTF8_BOM_1) { // yes, looks like UTF-8 BOM
if (!acc.hasMoreBytes()) {
return MatchStrength.INCONCLUSIVE;
}
if (acc.nextByte() != UTF8_BOM_2) {
return MatchStrength.NO_MATCH;
}
if (!acc.hasMoreBytes()) {
return MatchStrength.INCONCLUSIVE;
}
if (acc.nextByte() != UTF8_BOM_3) {
return MatchStrength.NO_MATCH;
}
if (!acc.hasMoreBytes()) {
return MatchStrength.INCONCLUSIVE;
}
b = acc.nextByte();
}
// Then possible leading space
int ch = skipSpace(acc, b);
if (ch < 0) {
return MatchStrength.INCONCLUSIVE;
}
// First, let's see if it looks like a structured type:
if (ch == '{') { // JSON object?
// Ideally we need to find either double-quote or closing bracket
ch = skipSpace(acc);
if (ch < 0) {
return MatchStrength.INCONCLUSIVE;
}
if (ch == '"' || ch == '}') {
return MatchStrength.SOLID_MATCH;
}
// ... should we allow non-standard? Let's not yet... can add if need be
return MatchStrength.NO_MATCH;
}
MatchStrength strength;
if (ch == '[') {
ch = skipSpace(acc);
if (ch < 0) {
return MatchStrength.INCONCLUSIVE;
}
// closing brackets is easy; but for now, let's also accept opening...
if (ch == ']' || ch == '[') {
return MatchStrength.SOLID_MATCH;
}
return MatchStrength.SOLID_MATCH;
} else {
// plain old value is not very convincing...
strength = MatchStrength.WEAK_MATCH;
}
if (ch == '"') { // string value
return strength;
}
if (ch <= '9' && ch >= '0') { // number
return strength;
}
if (ch == '-') { // negative number
ch = skipSpace(acc);
if (ch < 0) {
return MatchStrength.INCONCLUSIVE;
}
return (ch <= '9' && ch >= '0') ? strength : MatchStrength.NO_MATCH;
}
// or one of literals
if (ch == 'n') { // null
return tryMatch(acc, "ull", strength);
}
if (ch == 't') { // true
return tryMatch(acc, "rue", strength);
}
if (ch == 'f') { // false
return tryMatch(acc, "alse", strength);
}
return MatchStrength.NO_MATCH;
}
private static MatchStrength tryMatch(InputAccessor acc, String matchStr, MatchStrength fullMatchStrength)
throws IOException
{
for (int i = 0, len = matchStr.length(); i < len; ++i) {
if (!acc.hasMoreBytes()) {
return MatchStrength.INCONCLUSIVE;
}
if (acc.nextByte() != matchStr.charAt(i)) {
return MatchStrength.NO_MATCH;
}
}
return fullMatchStrength;
}
private static int skipSpace(InputAccessor acc) throws IOException
{
if (!acc.hasMoreBytes()) {
return -1;
}
return skipSpace(acc, acc.nextByte());
}
private static int skipSpace(InputAccessor acc, byte b) throws IOException
{
while (true) {
int ch = (int) b & 0xFF;
if (!(ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t')) {
return ch;
}
if (!acc.hasMoreBytes()) {
return -1;
}
b = acc.nextByte();
}
}
/*
/**********************************************************
/* Internal methods, parsing
/**********************************************************
*/
/**
* @return True if a BOM was succesfully found, and encoding
* thereby recognized.
*/
private boolean handleBOM(int quad) throws IOException
{
/* Handling of (usually) optional BOM (required for
* multi-byte formats); first 32-bit charsets:
*/
switch (quad) {
case 0x0000FEFF:
_bigEndian = true;
_inputPtr += 4;
_bytesPerChar = 4;
return true;
case 0xFFFE0000: // UCS-4, LE?
_inputPtr += 4;
_bytesPerChar = 4;
_bigEndian = false;
return true;
case 0x0000FFFE: // UCS-4, in-order...
reportWeirdUCS4("2143"); // throws exception
break; // never gets here
case 0xFEFF0000: // UCS-4, in-order...
reportWeirdUCS4("3412"); // throws exception
break; // never gets here
default:
}
// Ok, if not, how about 16-bit encoding BOMs?
int msw = quad >>> 16;
if (msw == 0xFEFF) { // UTF-16, BE
_inputPtr += 2;
_bytesPerChar = 2;
_bigEndian = true;
return true;
}
if (msw == 0xFFFE) { // UTF-16, LE
_inputPtr += 2;
_bytesPerChar = 2;
_bigEndian = false;
return true;
}
// And if not, then UTF-8 BOM?
if ((quad >>> 8) == 0xEFBBBF) { // UTF-8
_inputPtr += 3;
_bytesPerChar = 1;
_bigEndian = true; // doesn't really matter
return true;
}
return false;
}
private boolean checkUTF32(int quad) throws IOException
{
/* Handling of (usually) optional BOM (required for
* multi-byte formats); first 32-bit charsets:
*/
if ((quad >> 8) == 0) { // 0x000000?? -> UTF32-BE
_bigEndian = true;
} else if ((quad & 0x00FFFFFF) == 0) { // 0x??000000 -> UTF32-LE
_bigEndian = false;
} else if ((quad & ~0x00FF0000) == 0) { // 0x00??0000 -> UTF32-in-order
reportWeirdUCS4("3412");
} else if ((quad & ~0x0000FF00) == 0) { // 0x0000??00 -> UTF32-in-order
reportWeirdUCS4("2143");
} else {
// Can not be valid UTF-32 encoded JSON...
return false;
}
// Not BOM (just regular content), nothing to skip past:
//_inputPtr += 4;
_bytesPerChar = 4;
return true;
}
private boolean checkUTF16(int i16)
{
if ((i16 & 0xFF00) == 0) { // UTF-16BE
_bigEndian = true;
} else if ((i16 & 0x00FF) == 0) { // UTF-16LE
_bigEndian = false;
} else { // nope, not UTF-16
return false;
}
// Not BOM (just regular content), nothing to skip past:
//_inputPtr += 2;
_bytesPerChar = 2;
return true;
}
/*
/**********************************************************
/* Internal methods, problem reporting
/**********************************************************
*/
private void reportWeirdUCS4(String type) throws IOException {
throw new CharConversionException("Unsupported UCS-4 endianness ("+type+") detected");
}
/*
/**********************************************************
/* Internal methods, raw input access
/**********************************************************
*/
protected boolean ensureLoaded(int minimum) throws IOException {
/* Let's assume here buffer has enough room -- this will always
* be true for the limited used this method gets
*/
int gotten = (_inputEnd - _inputPtr);
while (gotten < minimum) {
int count;
if (_in == null) { // block source
count = -1;
} else {
count = _in.read(_inputBuffer, _inputEnd, _inputBuffer.length - _inputEnd);
}
if (count < 1) {
return false;
}
_inputEnd += count;
gotten += count;
}
return true;
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/DupDetector.java 0000664 0000000 0000000 00000004744 13561642473 0032406 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.json;
import java.util.*;
import com.fasterxml.jackson.core.*;
/**
* Helper class used if
* {@link com.fasterxml.jackson.core.JsonParser.Feature#STRICT_DUPLICATE_DETECTION}
* is enabled.
* Optimized to try to limit memory usage and processing overhead for smallest
* entries, but without adding trashing (immutable objects would achieve optimal
* memory usage but lead to significant number of discarded temp objects for
* scopes with large number of entries). Another consideration is trying to limit
* actual number of compiled classes as it contributes significantly to overall
* jar size (due to linkage etc).
*
* @since 2.3
*/
public class DupDetector
{
/**
* We need to store a back-reference here to parser/generator.
*/
protected final Object _source;
protected String _firstName;
protected String _secondName;
/**
* Lazily constructed set of names already seen within this context.
*/
protected HashSet
* NOTE: not all sub-classes make use of this setting.
*/
protected int _maximumNonEscapedChar;
/**
* Definition of custom character escapes to use for generators created
* by this factory, if any. If null, standard data format specific
* escapes are used.
*/
protected CharacterEscapes _characterEscapes;
/*
/**********************************************************
/* Configuration, other
/**********************************************************
*/
/**
* Separator to use, if any, between root-level values.
*
* @since 2.1
*/
protected SerializableString _rootValueSeparator
= DefaultPrettyPrinter.DEFAULT_ROOT_VALUE_SEPARATOR;
/**
* Flag that is set if quoting is not to be added around
* JSON Object property names.
*
* @since 2.7
*/
protected boolean _cfgUnqNames;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
@SuppressWarnings("deprecation")
public JsonGeneratorImpl(IOContext ctxt, int features, ObjectCodec codec)
{
super(features, codec);
_ioContext = ctxt;
if (Feature.ESCAPE_NON_ASCII.enabledIn(features)) {
// inlined `setHighestNonEscapedChar()`
_maximumNonEscapedChar = 127;
}
_cfgUnqNames = !Feature.QUOTE_FIELD_NAMES.enabledIn(features);
}
/*
/**********************************************************
/* Versioned
/**********************************************************
*/
@Override
public Version version() {
return VersionUtil.versionFor(getClass());
}
/*
/**********************************************************
/* Overridden configuration methods
/**********************************************************
*/
@SuppressWarnings("deprecation")
@Override
public JsonGenerator enable(Feature f) {
super.enable(f);
if (f == Feature.QUOTE_FIELD_NAMES) {
_cfgUnqNames = false;
}
return this;
}
@SuppressWarnings("deprecation")
@Override
public JsonGenerator disable(Feature f) {
super.disable(f);
if (f == Feature.QUOTE_FIELD_NAMES) {
_cfgUnqNames = true;
}
return this;
}
@SuppressWarnings("deprecation")
@Override
protected void _checkStdFeatureChanges(int newFeatureFlags, int changedFeatures) {
super._checkStdFeatureChanges(newFeatureFlags, changedFeatures);
_cfgUnqNames = !Feature.QUOTE_FIELD_NAMES.enabledIn(newFeatureFlags);
}
@Override
public JsonGenerator setHighestNonEscapedChar(int charCode) {
_maximumNonEscapedChar = (charCode < 0) ? 0 : charCode;
return this;
}
@Override
public int getHighestEscapedChar() {
return _maximumNonEscapedChar;
}
@Override
public JsonGenerator setCharacterEscapes(CharacterEscapes esc)
{
_characterEscapes = esc;
if (esc == null) { // revert to standard escapes
_outputEscapes = sOutputEscapes;
} else {
_outputEscapes = esc.getEscapeCodesForAscii();
}
return this;
}
/**
* Method for accessing custom escapes factory uses for {@link JsonGenerator}s
* it creates.
*/
@Override
public CharacterEscapes getCharacterEscapes() {
return _characterEscapes;
}
@Override
public JsonGenerator setRootValueSeparator(SerializableString sep) {
_rootValueSeparator = sep;
return this;
}
/*
/**********************************************************
/* Partial API
/**********************************************************
*/
// // Overrides just to make things final, to possibly help with inlining
@Override
public final void writeStringField(String fieldName, String value) throws IOException
{
writeFieldName(fieldName);
writeString(value);
}
/*
/**********************************************************
/* Shared helper methods
/**********************************************************
*/
protected void _verifyPrettyValueWrite(String typeMsg, int status) throws IOException
{
// If we have a pretty printer, it knows what to do:
switch (status) {
case JsonWriteContext.STATUS_OK_AFTER_COMMA: // array
_cfgPrettyPrinter.writeArrayValueSeparator(this);
break;
case JsonWriteContext.STATUS_OK_AFTER_COLON:
_cfgPrettyPrinter.writeObjectFieldValueSeparator(this);
break;
case JsonWriteContext.STATUS_OK_AFTER_SPACE:
_cfgPrettyPrinter.writeRootValueSeparator(this);
break;
case JsonWriteContext.STATUS_OK_AS_IS:
// First entry, but of which context?
if (_writeContext.inArray()) {
_cfgPrettyPrinter.beforeArrayValues(this);
} else if (_writeContext.inObject()) {
_cfgPrettyPrinter.beforeObjectEntries(this);
}
break;
case JsonWriteContext.STATUS_EXPECT_NAME:
_reportCantWriteValueExpectName(typeMsg);
break;
default:
_throwInternal();
break;
}
}
protected void _reportCantWriteValueExpectName(String typeMsg) throws IOException
{
_reportError(String.format("Can not %s, expecting field name (context: %s)",
typeMsg, _writeContext.typeDesc()));
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/JsonReadContext.java 0000664 0000000 0000000 00000014157 13561642473 0033235 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.json;
import com.fasterxml.jackson.core.*;
/**
* Extension of {@link JsonStreamContext}, which implements
* core methods needed, and also exposes
* more complete API to parser implementation classes.
*/
public final class JsonReadContext extends JsonStreamContext
{
// // // Configuration
/**
* Parent context for this context; null for root context.
*/
protected final JsonReadContext _parent;
// // // Optional duplicate detection
protected DupDetector _dups;
/*
/**********************************************************
/* Simple instance reuse slots; speeds up things
/* a bit (10-15%) for docs with lots of small
/* arrays/objects (for which allocation was
/* visible in profile stack frames)
/**********************************************************
*/
protected JsonReadContext _child;
/*
/**********************************************************
/* Location/state information (minus source reference)
/**********************************************************
*/
protected String _currentName;
/**
* @since 2.5
*/
protected Object _currentValue;
protected int _lineNr;
protected int _columnNr;
/*
/**********************************************************
/* Instance construction, config, reuse
/**********************************************************
*/
public JsonReadContext(JsonReadContext parent, DupDetector dups, int type, int lineNr, int colNr) {
super();
_parent = parent;
_dups = dups;
_type = type;
_lineNr = lineNr;
_columnNr = colNr;
_index = -1;
}
protected void reset(int type, int lineNr, int colNr) {
_type = type;
_index = -1;
_lineNr = lineNr;
_columnNr = colNr;
_currentName = null;
_currentValue = null;
if (_dups != null) {
_dups.reset();
}
}
/*
public void trackDups(JsonParser p) {
_dups = DupDetector.rootDetector(p);
}
*/
public JsonReadContext withDupDetector(DupDetector dups) {
_dups = dups;
return this;
}
@Override
public Object getCurrentValue() {
return _currentValue;
}
@Override
public void setCurrentValue(Object v) {
_currentValue = v;
}
/*
/**********************************************************
/* Factory methods
/**********************************************************
*/
public static JsonReadContext createRootContext(int lineNr, int colNr, DupDetector dups) {
return new JsonReadContext(null, dups, TYPE_ROOT, lineNr, colNr);
}
public static JsonReadContext createRootContext(DupDetector dups) {
return new JsonReadContext(null, dups, TYPE_ROOT, 1, 0);
}
public JsonReadContext createChildArrayContext(int lineNr, int colNr) {
JsonReadContext ctxt = _child;
if (ctxt == null) {
_child = ctxt = new JsonReadContext(this,
(_dups == null) ? null : _dups.child(), TYPE_ARRAY, lineNr, colNr);
} else {
ctxt.reset(TYPE_ARRAY, lineNr, colNr);
}
return ctxt;
}
public JsonReadContext createChildObjectContext(int lineNr, int colNr) {
JsonReadContext ctxt = _child;
if (ctxt == null) {
_child = ctxt = new JsonReadContext(this,
(_dups == null) ? null : _dups.child(), TYPE_OBJECT, lineNr, colNr);
return ctxt;
}
ctxt.reset(TYPE_OBJECT, lineNr, colNr);
return ctxt;
}
/*
/**********************************************************
/* Abstract method implementations, overrides
/**********************************************************
*/
@Override public String getCurrentName() { return _currentName; }
// @since 2.9
@Override public boolean hasCurrentName() { return _currentName != null; }
@Override public JsonReadContext getParent() { return _parent; }
@Override
public JsonLocation getStartLocation(Object srcRef) {
// We don't keep track of offsets at this level (only reader does)
long totalChars = -1L;
return new JsonLocation(srcRef, totalChars, _lineNr, _columnNr);
}
/*
/**********************************************************
/* Extended API
/**********************************************************
*/
/**
* Method that can be used to both clear the accumulated references
* (specifically value set with {@link #setCurrentValue(Object)})
* that should not be retained, and returns parent (as would
* {@link #getParent()} do). Typically called when closing the active
* context when encountering {@link JsonToken#END_ARRAY} or
* {@link JsonToken#END_OBJECT}.
*
* @since 2.7
*/
public JsonReadContext clearAndGetParent() {
_currentValue = null;
// could also clear the current name, but seems cheap enough to leave?
return _parent;
}
public DupDetector getDupDetector() {
return _dups;
}
/*
/**********************************************************
/* State changes
/**********************************************************
*/
public boolean expectComma() {
/* Assumption here is that we will be getting a value (at least
* before calling this method again), and
* so will auto-increment index to avoid having to do another call
*/
int ix = ++_index; // starts from -1
return (_type != TYPE_ROOT && ix > 0);
}
public void setCurrentName(String name) throws JsonProcessingException {
_currentName = name;
if (_dups != null) { _checkDup(_dups, name); }
}
private void _checkDup(DupDetector dd, String name) throws JsonProcessingException {
if (dd.isDup(name)) {
Object src = dd.getSource();
throw new JsonParseException(((src instanceof JsonParser) ? ((JsonParser) src) : null),
"Duplicate field '"+name+"'");
}
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/JsonReadFeature.java 0000664 0000000 0000000 00000021031 13561642473 0033171 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.json;
import com.fasterxml.jackson.core.*;
/**
* Token reader (parser) features specific to JSON backend.
* Eventual replacement for JSON-specific {@link com.fasterxml.jackson.core.JsonParser.Feature}s.
*
* @since 2.10
*/
public enum JsonReadFeature
implements FormatFeature
{
// // // Support for non-standard data format constructs: comments
/**
* Feature that determines whether parser will allow use
* of Java/C/C++ style comments (both '/'+'*' and
* '//' varieties) within parsed content or not.
*
* Since JSON specification does not mention comments as legal
* construct,
* this is a non-standard feature; however, in the wild
* this is extensively used. As such, feature is
* disabled by default for parsers and must be
* explicitly enabled.
*/
ALLOW_JAVA_COMMENTS(false, JsonParser.Feature.ALLOW_COMMENTS),
/**
* Feature that determines whether parser will allow use
* of YAML comments, ones starting with '#' and continuing
* until the end of the line. This commenting style is common
* with scripting languages as well.
*
* Since JSON specification does not mention comments as legal
* construct,
* this is a non-standard feature. As such, feature is
* disabled by default for parsers and must be
* explicitly enabled.
*/
ALLOW_YAML_COMMENTS(false, JsonParser.Feature.ALLOW_YAML_COMMENTS),
// // // Support for non-standard data format constructs: quoting/escaping
/**
* Feature that determines whether parser will allow use
* of single quotes (apostrophe, character '\'') for
* quoting Strings (names and String values). If so,
* this is in addition to other acceptable markers.
*
* Since JSON specification requires use of double quotes for
* field names,
* this is a non-standard feature, and as such disabled by default.
*/
ALLOW_SINGLE_QUOTES(false, JsonParser.Feature.ALLOW_SINGLE_QUOTES),
/**
* Feature that determines whether parser will allow use
* of unquoted field names (which is allowed by Javascript,
* but not by JSON specification).
*
* Since JSON specification requires use of double quotes for
* field names,
* this is a non-standard feature, and as such disabled by default.
*/
ALLOW_UNQUOTED_FIELD_NAMES(false, JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES),
/**
* Feature that determines whether parser will allow
* JSON Strings to contain unescaped control characters
* (ASCII characters with value less than 32, including
* tab and line feed characters) or not.
* If feature is set false, an exception is thrown if such a
* character is encountered.
*
* Since JSON specification requires quoting for all control characters,
* this is a non-standard feature, and as such disabled by default.
*/
@SuppressWarnings("deprecation")
ALLOW_UNESCAPED_CONTROL_CHARS(false, JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS),
/**
* Feature that can be enabled to accept quoting of all character
* using backslash quoting mechanism: if not enabled, only characters
* that are explicitly listed by JSON specification can be thus
* escaped (see JSON spec for small list of these characters)
*
* Since JSON specification requires quoting for all control characters,
* this is a non-standard feature, and as such disabled by default.
*/
@SuppressWarnings("deprecation")
ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(false, JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER),
// // // Support for non-standard data format constructs: number representations
/**
* Feature that determines whether parser will allow
* JSON integral numbers to start with additional (ignorable)
* zeroes (like: 000001). If enabled, no exception is thrown, and extra
* nulls are silently ignored (and not included in textual representation
* exposed via {@link JsonParser#getText}).
*
* Since JSON specification does not allow leading zeroes,
* this is a non-standard feature, and as such disabled by default.
*/
@SuppressWarnings("deprecation")
ALLOW_LEADING_ZEROS_FOR_NUMBERS(false, JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS),
/**
* Feature that allows parser to recognize set of
* "Not-a-Number" (NaN) tokens as legal floating number
* values (similar to how many other data formats and
* programming language source code allows it).
* Specific subset contains values that
* XML Schema
* (see section 3.2.4.1, Lexical Representation)
* allows (tokens are quoted contents, not including quotes):
*
* Since JSON specification does not allow use of such values,
* this is a non-standard feature, and as such disabled by default.
*/
@SuppressWarnings("deprecation")
ALLOW_NON_NUMERIC_NUMBERS(false, JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS),
// // // Support for non-standard data format constructs: array/value separators
/**
* Feature allows the support for "missing" values in a JSON array: missing
* value meaning sequence of two commas, without value in-between but only
* optional white space.
* Enabling this feature will expose "missing" values as {@link JsonToken#VALUE_NULL}
* tokens, which typically become Java nulls in arrays and {@link java.util.Collection}
* in data-binding.
*
* For example, enabling this feature will represent a JSON array
* Since the JSON specification does not allow missing values this is a non-compliant JSON
* feature and is disabled by default.
*/
@SuppressWarnings("deprecation")
ALLOW_MISSING_VALUES(false, JsonParser.Feature.ALLOW_MISSING_VALUES),
/**
* Feature that determines whether {@link JsonParser} will allow for a single trailing
* comma following the final value (in an Array) or member (in an Object). These commas
* will simply be ignored.
*
* For example, when this feature is enabled,
* When combined with
* Since the JSON specification does not permit trailing commas, this is a non-standard
* feature, and as such disabled by default.
*/
@SuppressWarnings("deprecation")
ALLOW_TRAILING_COMMA(false, JsonParser.Feature.ALLOW_TRAILING_COMMA),
;
final private boolean _defaultState;
final private int _mask;
/**
* For backwards compatibility we may need to map to one of existing {@link JsonParser.Feature}s;
* if so, this is the feature to enable/disable.
*/
final private JsonParser.Feature _mappedFeature;
/**
* Method that calculates bit set (flags) of all features that
* are enabled by default.
*/
public static int collectDefaults()
{
int flags = 0;
for (JsonReadFeature f : values()) {
if (f.enabledByDefault()) {
flags |= f.getMask();
}
}
return flags;
}
private JsonReadFeature(boolean defaultState,
JsonParser.Feature mapTo) {
_defaultState = defaultState;
_mask = (1 << ordinal());
_mappedFeature = mapTo;
}
@Override
public boolean enabledByDefault() { return _defaultState; }
@Override
public int getMask() { return _mask; }
@Override
public boolean enabledIn(int flags) { return (flags & _mask) != 0; }
public JsonParser.Feature mappedFeature() { return _mappedFeature; }
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/JsonWriteContext.java0000664 0000000 0000000 00000016756 13561642473 0033463 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.json;
import com.fasterxml.jackson.core.*;
/**
* Extension of {@link JsonStreamContext}, which implements
* core methods needed, and also exposes
* more complete API to generator implementation classes.
*/
public class JsonWriteContext extends JsonStreamContext
{
// // // Return values for writeValue()
public final static int STATUS_OK_AS_IS = 0;
public final static int STATUS_OK_AFTER_COMMA = 1;
public final static int STATUS_OK_AFTER_COLON = 2;
public final static int STATUS_OK_AFTER_SPACE = 3; // in root context
public final static int STATUS_EXPECT_VALUE = 4;
public final static int STATUS_EXPECT_NAME = 5;
/**
* Parent context for this context; null for root context.
*/
protected final JsonWriteContext _parent;
// // // Optional duplicate detection
protected DupDetector _dups;
/*
/**********************************************************
/* Simple instance reuse slots; speed up things a bit (10-15%)
/* for docs with lots of small arrays/objects
/**********************************************************
*/
protected JsonWriteContext _child;
/*
/**********************************************************
/* Location/state information (minus source reference)
/**********************************************************
*/
/**
* Name of the field of which value is to be written; only
* used for OBJECT contexts
*/
protected String _currentName;
/**
* @since 2.5
*/
protected Object _currentValue;
/**
* Marker used to indicate that we just wrote a name, and
* now expect a value to write
*/
protected boolean _gotName;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
protected JsonWriteContext(int type, JsonWriteContext parent, DupDetector dups) {
super();
_type = type;
_parent = parent;
_dups = dups;
_index = -1;
}
/* @since 2.10 */
protected JsonWriteContext(int type, JsonWriteContext parent, DupDetector dups,
Object currValue) {
super();
_type = type;
_parent = parent;
_dups = dups;
_index = -1;
_currentValue = currValue;
}
protected JsonWriteContext reset(int type) {
_type = type;
_index = -1;
_currentName = null;
_gotName = false;
_currentValue = null;
if (_dups != null) { _dups.reset(); }
return this;
}
/* @since 2.10 */
protected JsonWriteContext reset(int type, Object currValue) {
_type = type;
_index = -1;
_currentName = null;
_gotName = false;
_currentValue = currValue;
if (_dups != null) { _dups.reset(); }
return this;
}
public JsonWriteContext withDupDetector(DupDetector dups) {
_dups = dups;
return this;
}
@Override
public Object getCurrentValue() {
return _currentValue;
}
@Override
public void setCurrentValue(Object v) {
_currentValue = v;
}
/*
/**********************************************************
/* Factory methods
/**********************************************************
*/
/**
* @deprecated Since 2.3; use method that takes argument
*/
@Deprecated
public static JsonWriteContext createRootContext() { return createRootContext(null); }
public static JsonWriteContext createRootContext(DupDetector dd) {
return new JsonWriteContext(TYPE_ROOT, null, dd);
}
public JsonWriteContext createChildArrayContext() {
JsonWriteContext ctxt = _child;
if (ctxt == null) {
_child = ctxt = new JsonWriteContext(TYPE_ARRAY, this,
(_dups == null) ? null : _dups.child());
return ctxt;
}
return ctxt.reset(TYPE_ARRAY);
}
/* @since 2.10 */
public JsonWriteContext createChildArrayContext(Object currValue) {
JsonWriteContext ctxt = _child;
if (ctxt == null) {
_child = ctxt = new JsonWriteContext(TYPE_ARRAY, this,
(_dups == null) ? null : _dups.child(), currValue);
return ctxt;
}
return ctxt.reset(TYPE_ARRAY, currValue);
}
public JsonWriteContext createChildObjectContext() {
JsonWriteContext ctxt = _child;
if (ctxt == null) {
_child = ctxt = new JsonWriteContext(TYPE_OBJECT, this,
(_dups == null) ? null : _dups.child());
return ctxt;
}
return ctxt.reset(TYPE_OBJECT);
}
/* @since 2.10 */
public JsonWriteContext createChildObjectContext(Object currValue) {
JsonWriteContext ctxt = _child;
if (ctxt == null) {
_child = ctxt = new JsonWriteContext(TYPE_OBJECT, this,
(_dups == null) ? null : _dups.child(), currValue);
return ctxt;
}
return ctxt.reset(TYPE_OBJECT, currValue);
}
@Override public final JsonWriteContext getParent() { return _parent; }
@Override public final String getCurrentName() { return _currentName; }
// @since 2.9
@Override public boolean hasCurrentName() { return _currentName != null; }
/**
* Method that can be used to both clear the accumulated references
* (specifically value set with {@link #setCurrentValue(Object)})
* that should not be retained, and returns parent (as would
* {@link #getParent()} do). Typically called when closing the active
* context when encountering {@link JsonToken#END_ARRAY} or
* {@link JsonToken#END_OBJECT}.
*
* @since 2.7
*/
public JsonWriteContext clearAndGetParent() {
_currentValue = null;
// could also clear the current name, but seems cheap enough to leave?
return _parent;
}
public DupDetector getDupDetector() {
return _dups;
}
/**
* Method that writer is to call before it writes a field name.
*
* @return Index of the field entry (0-based)
*/
public int writeFieldName(String name) throws JsonProcessingException {
if ((_type != TYPE_OBJECT) || _gotName) {
return STATUS_EXPECT_VALUE;
}
_gotName = true;
_currentName = name;
if (_dups != null) { _checkDup(_dups, name); }
return (_index < 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_COMMA;
}
private final void _checkDup(DupDetector dd, String name) throws JsonProcessingException {
if (dd.isDup(name)) {
Object src = dd.getSource();
throw new JsonGenerationException("Duplicate field '"+name+"'",
((src instanceof JsonGenerator) ? ((JsonGenerator) src) : null));
}
}
public int writeValue() {
// Most likely, object:
if (_type == TYPE_OBJECT) {
if (!_gotName) {
return STATUS_EXPECT_NAME;
}
_gotName = false;
++_index;
return STATUS_OK_AFTER_COLON;
}
// Ok, array?
if (_type == TYPE_ARRAY) {
int ix = _index;
++_index;
return (ix < 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_COMMA;
}
// Nope, root context
// No commas within root context, but need space
++_index;
return (_index == 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_SPACE;
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/JsonWriteFeature.java0000664 0000000 0000000 00000012077 13561642473 0033422 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.json;
import com.fasterxml.jackson.core.*;
/**
* Token writer features specific to JSON backend.
*
* @since 2.10
*/
public enum JsonWriteFeature
implements FormatFeature
{
// // // Support for non-standard data format constructs: comments
// // Quoting/ecsaping-related features
/**
* Feature that determines whether JSON Object field names are
* quoted using double-quotes, as specified by JSON specification
* or not. Ability to disable quoting was added to support use
* cases where they are not usually expected, which most commonly
* occurs when used straight from Javascript.
*
* Feature is enabled by default (since it is required by JSON specification).
*/
@SuppressWarnings("deprecation")
QUOTE_FIELD_NAMES(true, JsonGenerator.Feature.QUOTE_FIELD_NAMES),
/**
* Feature that determines whether "NaN" ("not a number", that is, not
* real number) float/double values are output as JSON strings.
* The values checked are Double.Nan,
* Double.POSITIVE_INFINITY and Double.NEGATIVE_INIFINTY (and
* associated Float values).
* If feature is disabled, these numbers are still output using
* associated literal values, resulting in non-conforming
* output.
*
* Feature is enabled by default.
*/
@SuppressWarnings("deprecation")
WRITE_NAN_AS_STRINGS(true, JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS),
/**
* Feature that forces all regular number values to be written as JSON Strings,
* instead of as JSON Numbers.
* Default state is 'false', meaning that Java numbers are to
* be serialized using basic numeric representation but
* if enabled all such numeric values are instead written out as
* JSON Strings instead.
*
* One use case is to avoid problems with Javascript limitations:
* since Javascript standard specifies that all number handling
* should be done using 64-bit IEEE 754 floating point values,
* result being that some 64-bit integer values can not be
* accurately represent (as mantissa is only 51 bit wide).
*
* Feature is disabled by default.
*/
@SuppressWarnings("deprecation")
WRITE_NUMBERS_AS_STRINGS(false, JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS),
/**
* Feature that specifies that all characters beyond 7-bit ASCII
* range (i.e. code points of 128 and above) need to be output
* using format-specific escapes (for JSON, backslash escapes),
* if format uses escaping mechanisms (which is generally true
* for textual formats but not for binary formats).
*
* Feature is disabled by default.
*/
@SuppressWarnings("deprecation")
ESCAPE_NON_ASCII(false, JsonGenerator.Feature.ESCAPE_NON_ASCII),
//23-Nov-2015, tatu: for [core#223], if and when it gets implemented
/*
* Feature that specifies handling of UTF-8 content that contains
* characters beyond BMP (Basic Multilingual Plane), which are
* represented in UCS-2 (Java internal character encoding) as two
* "surrogate" characters. If feature is enabled, these surrogate
* pairs are separately escaped using backslash escapes; if disabled,
* native output (4-byte UTF-8 sequence, or, with char-backed output
* targets, writing of surrogates as is which is typically converted
* by {@link java.io.Writer} into 4-byte UTF-8 sequence eventually)
* is used.
*
* Note that the original JSON specification suggests use of escaping;
* but that this is not correct from standard UTF-8 handling perspective.
* Because of two competing goals, this feature was added to allow either
* behavior to be used, but defaulting to UTF-8 specification compliant
* mode.
*
* Feature is disabled by default.
*/
// ESCAPE_UTF8_SURROGATES(false, JsonGenerator.Feature.ESCAPE_UTF8_SURROGATES),
;
final private boolean _defaultState;
final private int _mask;
/**
* For backwards compatibility we may need to map to one of existing {@link JsonGenerator.Feature}s;
* if so, this is the feature to enable/disable.
*/
final private JsonGenerator.Feature _mappedFeature;
/**
* Method that calculates bit set (flags) of all features that
* are enabled by default.
*/
public static int collectDefaults()
{
int flags = 0;
for (JsonWriteFeature f : values()) {
if (f.enabledByDefault()) {
flags |= f.getMask();
}
}
return flags;
}
private JsonWriteFeature(boolean defaultState,
JsonGenerator.Feature mapTo) {
_defaultState = defaultState;
_mask = (1 << ordinal());
_mappedFeature = mapTo;
}
@Override
public boolean enabledByDefault() { return _defaultState; }
@Override
public int getMask() { return _mask; }
@Override
public boolean enabledIn(int flags) { return (flags & _mask) != 0; }
public JsonGenerator.Feature mappedFeature() { return _mappedFeature; }
}
PackageVersion.java.in 0000664 0000000 0000000 00000001107 13561642473 0033401 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json package @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.java 0000664 0000000 0000000 00000305147 13561642473 0034256 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json package com.fasterxml.jackson.core.json;
import java.io.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.base.ParserBase;
import com.fasterxml.jackson.core.io.CharTypes;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer;
import com.fasterxml.jackson.core.util.*;
import static com.fasterxml.jackson.core.JsonTokenId.*;
/**
* This is a concrete implementation of {@link JsonParser}, which is
* based on a {@link java.io.Reader} to handle low-level character
* conversion tasks.
*/
public class ReaderBasedJsonParser // final in 2.3, earlier
extends ParserBase
{
@SuppressWarnings("deprecation")
private final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask();
@SuppressWarnings("deprecation")
private final static int FEAT_MASK_LEADING_ZEROS = Feature.ALLOW_NUMERIC_LEADING_ZEROS.getMask();
@SuppressWarnings("deprecation")
private final static int FEAT_MASK_NON_NUM_NUMBERS = Feature.ALLOW_NON_NUMERIC_NUMBERS.getMask();
@SuppressWarnings("deprecation")
private final static int FEAT_MASK_ALLOW_MISSING = Feature.ALLOW_MISSING_VALUES.getMask();
private final static int FEAT_MASK_ALLOW_SINGLE_QUOTES = Feature.ALLOW_SINGLE_QUOTES.getMask();
private final static int FEAT_MASK_ALLOW_UNQUOTED_NAMES = Feature.ALLOW_UNQUOTED_FIELD_NAMES.getMask();
private final static int FEAT_MASK_ALLOW_JAVA_COMMENTS = Feature.ALLOW_COMMENTS.getMask();
private final static int FEAT_MASK_ALLOW_YAML_COMMENTS = Feature.ALLOW_YAML_COMMENTS.getMask();
// Latin1 encoding is not supported, but we do use 8-bit subset for
// pre-processing task, to simplify first pass, keep it fast.
protected final static int[] _icLatin1 = CharTypes.getInputCodeLatin1();
/*
/**********************************************************
/* Input configuration
/**********************************************************
*/
/**
* Reader that can be used for reading more content, if one
* buffer from input source, but in some cases pre-loaded buffer
* is handed to the parser.
*/
protected Reader _reader;
/**
* Current buffer from which data is read; generally data is read into
* buffer from input source.
*/
protected char[] _inputBuffer;
/**
* Flag that indicates whether the input buffer is recycable (and
* needs to be returned to recycler once we are done) or not.
*
* If it is not, it also means that parser can NOT modify underlying
* buffer.
*/
protected boolean _bufferRecyclable;
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
protected ObjectCodec _objectCodec;
final protected CharsToNameCanonicalizer _symbols;
final protected int _hashSeed;
/*
/**********************************************************
/* Parsing state
/**********************************************************
*/
/**
* Flag that indicates that the current token has not yet
* been fully processed, and needs to be finished for
* some access (or skipped to obtain the next token)
*/
protected boolean _tokenIncomplete;
/**
* Value of {@link #_inputPtr} at the time when the first character of
* name token was read. Used for calculating token location when requested;
* combined with {@link #_currInputProcessed}, may be updated appropriately
* as needed.
*
* @since 2.7
*/
protected long _nameStartOffset;
/**
* @since 2.7
*/
protected int _nameStartRow;
/**
* @since 2.7
*/
protected int _nameStartCol;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
/**
* Method called when caller wants to provide input buffer directly,
* and it may or may not be recyclable use standard recycle context.
*
* @since 2.4
*/
public ReaderBasedJsonParser(IOContext ctxt, int features, Reader r,
ObjectCodec codec, CharsToNameCanonicalizer st,
char[] inputBuffer, int start, int end,
boolean bufferRecyclable)
{
super(ctxt, features);
_reader = r;
_inputBuffer = inputBuffer;
_inputPtr = start;
_inputEnd = end;
_objectCodec = codec;
_symbols = st;
_hashSeed = st.hashSeed();
_bufferRecyclable = bufferRecyclable;
}
/**
* Method called when input comes as a {@link java.io.Reader}, and buffer allocation
* can be done using default mechanism.
*/
public ReaderBasedJsonParser(IOContext ctxt, int features, Reader r,
ObjectCodec codec, CharsToNameCanonicalizer st)
{
super(ctxt, features);
_reader = r;
_inputBuffer = ctxt.allocTokenBuffer();
_inputPtr = 0;
_inputEnd = 0;
_objectCodec = codec;
_symbols = st;
_hashSeed = st.hashSeed();
_bufferRecyclable = true;
}
/*
/**********************************************************
/* Base method defs, overrides
/**********************************************************
*/
@Override public ObjectCodec getCodec() { return _objectCodec; }
@Override public void setCodec(ObjectCodec c) { _objectCodec = c; }
@Override
public int releaseBuffered(Writer w) throws IOException {
int count = _inputEnd - _inputPtr;
if (count < 1) { return 0; }
// let's just advance ptr to end
int origPtr = _inputPtr;
w.write(_inputBuffer, origPtr, count);
return count;
}
@Override public Object getInputSource() { return _reader; }
@Deprecated // since 2.8
protected char getNextChar(String eofMsg) throws IOException {
return getNextChar(eofMsg, null);
}
protected char getNextChar(String eofMsg, JsonToken forToken) throws IOException {
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOF(eofMsg, forToken);
}
}
return _inputBuffer[_inputPtr++];
}
@Override
protected void _closeInput() throws IOException {
/* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
* on the underlying Reader, unless we "own" it, or auto-closing
* feature is enabled.
* One downside is that when using our optimized
* Reader (granted, we only do that for UTF-32...) this
* means that buffer recycling won't work correctly.
*/
if (_reader != null) {
if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_SOURCE)) {
_reader.close();
}
_reader = null;
}
}
/**
* Method called to release internal buffers owned by the base
* reader. This may be called along with {@link #_closeInput} (for
* example, when explicitly closing this reader instance), or
* separately (if need be).
*/
@Override
protected void _releaseBuffers() throws IOException {
super._releaseBuffers();
// merge new symbols, if any
_symbols.release();
// and release buffers, if they are recyclable ones
if (_bufferRecyclable) {
char[] buf = _inputBuffer;
if (buf != null) {
_inputBuffer = null;
_ioContext.releaseTokenBuffer(buf);
}
}
}
/*
/**********************************************************
/* Low-level access, supporting
/**********************************************************
*/
protected void _loadMoreGuaranteed() throws IOException {
if (!_loadMore()) { _reportInvalidEOF(); }
}
protected boolean _loadMore() throws IOException
{
final int bufSize = _inputEnd;
if (_reader != null) {
int count = _reader.read(_inputBuffer, 0, _inputBuffer.length);
if (count > 0) {
_inputPtr = 0;
_inputEnd = count;
_currInputProcessed += bufSize;
_currInputRowStart -= bufSize;
// 26-Nov-2015, tatu: Since name-offset requires it too, must offset
// this increase to avoid "moving" name-offset, resulting most likely
// in negative value, which is fine as combine value remains unchanged.
_nameStartOffset -= bufSize;
return true;
}
// End of input
_closeInput();
// Should never return 0, so let's fail
if (count == 0) {
throw new IOException("Reader returned 0 characters when trying to read "+_inputEnd);
}
}
return false;
}
/*
/**********************************************************
/* Public API, data access
/**********************************************************
*/
/**
* Method for accessing textual representation of the current event;
* if no current event (before first call to {@link #nextToken}, or
* after encountering end-of-input), returns null.
* Method can be called for any event.
*/
@Override
public final String getText() throws IOException
{
JsonToken t = _currToken;
if (t == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
return _textBuffer.contentsAsString();
}
return _getText2(t);
}
@Override // since 2.8
public int getText(Writer writer) throws IOException
{
JsonToken t = _currToken;
if (t == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
return _textBuffer.contentsToWriter(writer);
}
if (t == JsonToken.FIELD_NAME) {
String n = _parsingContext.getCurrentName();
writer.write(n);
return n.length();
}
if (t != null) {
if (t.isNumeric()) {
return _textBuffer.contentsToWriter(writer);
}
char[] ch = t.asCharArray();
writer.write(ch);
return ch.length;
}
return 0;
}
// // // Let's override default impls for improved performance
// @since 2.1
@Override
public final String getValueAsString() throws IOException
{
if (_currToken == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
return _textBuffer.contentsAsString();
}
if (_currToken == JsonToken.FIELD_NAME) {
return getCurrentName();
}
return super.getValueAsString(null);
}
// @since 2.1
@Override
public final String getValueAsString(String defValue) throws IOException {
if (_currToken == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
return _textBuffer.contentsAsString();
}
if (_currToken == JsonToken.FIELD_NAME) {
return getCurrentName();
}
return super.getValueAsString(defValue);
}
protected final String _getText2(JsonToken t) {
if (t == null) {
return null;
}
switch (t.id()) {
case ID_FIELD_NAME:
return _parsingContext.getCurrentName();
case ID_STRING:
// fall through
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return _textBuffer.contentsAsString();
default:
return t.asString();
}
}
@Override
public final char[] getTextCharacters() throws IOException
{
if (_currToken != null) { // null only before/after document
switch (_currToken.id()) {
case ID_FIELD_NAME:
if (!_nameCopied) {
String name = _parsingContext.getCurrentName();
int nameLen = name.length();
if (_nameCopyBuffer == null) {
_nameCopyBuffer = _ioContext.allocNameCopyBuffer(nameLen);
} else if (_nameCopyBuffer.length < nameLen) {
_nameCopyBuffer = new char[nameLen];
}
name.getChars(0, nameLen, _nameCopyBuffer, 0);
_nameCopied = true;
}
return _nameCopyBuffer;
case ID_STRING:
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
// fall through
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return _textBuffer.getTextBuffer();
default:
return _currToken.asCharArray();
}
}
return null;
}
@Override
public final int getTextLength() throws IOException
{
if (_currToken != null) { // null only before/after document
switch (_currToken.id()) {
case ID_FIELD_NAME:
return _parsingContext.getCurrentName().length();
case ID_STRING:
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
// fall through
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return _textBuffer.size();
default:
return _currToken.asCharArray().length;
}
}
return 0;
}
@Override
public final int getTextOffset() throws IOException
{
// Most have offset of 0, only some may have other values:
if (_currToken != null) {
switch (_currToken.id()) {
case ID_FIELD_NAME:
return 0;
case ID_STRING:
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
// fall through
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return _textBuffer.getTextOffset();
default:
}
}
return 0;
}
@Override
public byte[] getBinaryValue(Base64Variant b64variant) throws IOException
{
if ((_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) && (_binaryValue != null)) {
return _binaryValue;
}
if (_currToken != JsonToken.VALUE_STRING) {
_reportError("Current token ("+_currToken+") not VALUE_STRING or VALUE_EMBEDDED_OBJECT, can not access as binary");
}
// To ensure that we won't see inconsistent data, better clear up state
if (_tokenIncomplete) {
try {
_binaryValue = _decodeBase64(b64variant);
} catch (IllegalArgumentException iae) {
throw _constructError("Failed to decode VALUE_STRING as base64 ("+b64variant+"): "+iae.getMessage());
}
/* let's clear incomplete only now; allows for accessing other
* textual content in error cases
*/
_tokenIncomplete = false;
} else { // may actually require conversion...
if (_binaryValue == null) {
@SuppressWarnings("resource")
ByteArrayBuilder builder = _getByteArrayBuilder();
_decodeBase64(getText(), builder, b64variant);
_binaryValue = builder.toByteArray();
}
}
return _binaryValue;
}
@Override
public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException
{
// if we have already read the token, just use whatever we may have
if (!_tokenIncomplete || _currToken != JsonToken.VALUE_STRING) {
byte[] b = getBinaryValue(b64variant);
out.write(b);
return b.length;
}
// otherwise do "real" incremental parsing...
byte[] buf = _ioContext.allocBase64Buffer();
try {
return _readBinary(b64variant, out, buf);
} finally {
_ioContext.releaseBase64Buffer(buf);
}
}
protected int _readBinary(Base64Variant b64variant, OutputStream out, byte[] buffer) throws IOException
{
int outputPtr = 0;
final int outputEnd = buffer.length - 3;
int outputCount = 0;
while (true) {
// first, we'll skip preceding white space, if any
char ch;
do {
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
} while (ch <= INT_SPACE);
int bits = b64variant.decodeBase64Char(ch);
if (bits < 0) { // reached the end, fair and square?
if (ch == '"') {
break;
}
bits = _decodeBase64Escape(b64variant, ch, 0);
if (bits < 0) { // white space to skip
continue;
}
}
// enough room? If not, flush
if (outputPtr > outputEnd) {
outputCount += outputPtr;
out.write(buffer, 0, outputPtr);
outputPtr = 0;
}
int decodedData = bits;
// then second base64 char; can't get padding yet, nor ws
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
bits = _decodeBase64Escape(b64variant, ch, 1);
}
decodedData = (decodedData << 6) | bits;
// third base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
bits = b64variant.decodeBase64Char(ch);
// First branch: can get padding (-> 1 byte)
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// as per [JACKSON-631], could also just be 'missing' padding
if (ch == '"') {
decodedData >>= 4;
buffer[outputPtr++] = (byte) decodedData;
if (b64variant.usesPadding()) {
--_inputPtr; // to keep parser state bit more consistent
_handleBase64MissingPadding(b64variant);
}
break;
}
bits = _decodeBase64Escape(b64variant, ch, 2);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
// Ok, must get padding
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
if (!b64variant.usesPaddingChar(ch)) {
if (_decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) {
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
}
}
// Got 12 bits, only need 8, need to shift
decodedData >>= 4;
buffer[outputPtr++] = (byte) decodedData;
continue;
}
}
// Nope, 2 or 3 bytes
decodedData = (decodedData << 6) | bits;
// fourth and last base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// as per [JACKSON-631], could also just be 'missing' padding
if (ch == '"') {
decodedData >>= 2;
buffer[outputPtr++] = (byte) (decodedData >> 8);
buffer[outputPtr++] = (byte) decodedData;
if (b64variant.usesPadding()) {
--_inputPtr; // to keep parser state bit more consistent
_handleBase64MissingPadding(b64variant);
}
break;
}
bits = _decodeBase64Escape(b64variant, ch, 3);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
/* With padding we only get 2 bytes; but we have
* to shift it a bit so it is identical to triplet
* case with partial output.
* 3 chars gives 3x6 == 18 bits, of which 2 are
* dummies, need to discard:
*/
decodedData >>= 2;
buffer[outputPtr++] = (byte) (decodedData >> 8);
buffer[outputPtr++] = (byte) decodedData;
continue;
}
}
// otherwise, our triplet is now complete
decodedData = (decodedData << 6) | bits;
buffer[outputPtr++] = (byte) (decodedData >> 16);
buffer[outputPtr++] = (byte) (decodedData >> 8);
buffer[outputPtr++] = (byte) decodedData;
}
_tokenIncomplete = false;
if (outputPtr > 0) {
outputCount += outputPtr;
out.write(buffer, 0, outputPtr);
}
return outputCount;
}
/*
/**********************************************************
/* Public API, traversal
/**********************************************************
*/
/**
* @return Next token from the stream, if any found, or null
* to indicate end-of-input
*/
@Override
public final JsonToken nextToken() throws IOException
{
/* First: field names are special -- we will always tokenize
* (part of) value along with field name to simplify
* state handling. If so, can and need to use secondary token:
*/
if (_currToken == JsonToken.FIELD_NAME) {
return _nextAfterName();
}
// But if we didn't already have a name, and (partially?) decode number,
// need to ensure no numeric information is leaked
_numTypesValid = NR_UNKNOWN;
if (_tokenIncomplete) {
_skipString(); // only strings can be partial
}
int i = _skipWSOrEnd();
if (i < 0) { // end-of-input
// Should actually close/release things
// like input source, symbol table and recyclable buffers now.
close();
return (_currToken = null);
}
// clear any data retained so far
_binaryValue = null;
// Closing scope?
if (i == INT_RBRACKET || i == INT_RCURLY) {
_closeScope(i);
return _currToken;
}
// Nope: do we then expect a comma?
if (_parsingContext.expectComma()) {
i = _skipComma(i);
// Was that a trailing comma?
if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
if ((i == INT_RBRACKET) || (i == INT_RCURLY)) {
_closeScope(i);
return _currToken;
}
}
}
/* And should we now have a name? Always true for Object contexts, since
* the intermediate 'expect-value' state is never retained.
*/
boolean inObject = _parsingContext.inObject();
if (inObject) {
// First, field name itself:
_updateNameLocation();
String name = (i == INT_QUOTE) ? _parseName() : _handleOddName(i);
_parsingContext.setCurrentName(name);
_currToken = JsonToken.FIELD_NAME;
i = _skipColon();
}
_updateLocation();
// Ok: we must have a value... what is it?
JsonToken t;
switch (i) {
case '"':
_tokenIncomplete = true;
t = JsonToken.VALUE_STRING;
break;
case '[':
if (!inObject) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
}
t = JsonToken.START_ARRAY;
break;
case '{':
if (!inObject) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
t = JsonToken.START_OBJECT;
break;
case '}':
// Error: } is not valid at this point; valid closers have
// been handled earlier
_reportUnexpectedChar(i, "expected a value");
case 't':
_matchTrue();
t = JsonToken.VALUE_TRUE;
break;
case 'f':
_matchFalse();
t = JsonToken.VALUE_FALSE;
break;
case 'n':
_matchNull();
t = JsonToken.VALUE_NULL;
break;
case '-':
/* Should we have separate handling for plus? Although
* it is not allowed per se, it may be erroneously used,
* and could be indicate by a more specific error message.
*/
t = _parseNegNumber();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
t = _parsePosNumber(i);
break;
default:
t = _handleOddValue(i);
break;
}
if (inObject) {
_nextToken = t;
return _currToken;
}
_currToken = t;
return t;
}
private final JsonToken _nextAfterName()
{
_nameCopied = false; // need to invalidate if it was copied
JsonToken t = _nextToken;
_nextToken = null;
// !!! 16-Nov-2015, tatu: TODO: fix [databind#37], copy next location to current here
// Also: may need to start new context?
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return (_currToken = t);
}
@Override
public void finishToken() throws IOException {
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
}
/*
/**********************************************************
/* Public API, nextXxx() overrides
/**********************************************************
*/
// Implemented since 2.7
@Override
public boolean nextFieldName(SerializableString sstr) throws IOException
{
// // // Note: most of code below is copied from nextToken()
_numTypesValid = NR_UNKNOWN;
if (_currToken == JsonToken.FIELD_NAME) {
_nextAfterName();
return false;
}
if (_tokenIncomplete) {
_skipString();
}
int i = _skipWSOrEnd();
if (i < 0) {
close();
_currToken = null;
return false;
}
_binaryValue = null;
// Closing scope?
if (i == INT_RBRACKET || i == INT_RCURLY) {
_closeScope(i);
return false;
}
if (_parsingContext.expectComma()) {
i = _skipComma(i);
// Was that a trailing comma?
if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
if ((i == INT_RBRACKET) || (i == INT_RCURLY)) {
_closeScope(i);
return false;
}
}
}
if (!_parsingContext.inObject()) {
_updateLocation();
_nextTokenNotInObject(i);
return false;
}
_updateNameLocation();
if (i == INT_QUOTE) {
// when doing literal match, must consider escaping:
char[] nameChars = sstr.asQuotedChars();
final int len = nameChars.length;
// Require 4 more bytes for faster skipping of colon that follows name
if ((_inputPtr + len + 4) < _inputEnd) { // maybe...
// first check length match by
final int end = _inputPtr+len;
if (_inputBuffer[end] == '"') {
int offset = 0;
int ptr = _inputPtr;
while (true) {
if (ptr == end) { // yes, match!
_parsingContext.setCurrentName(sstr.getValue());
_isNextTokenNameYes(_skipColonFast(ptr+1));
return true;
}
if (nameChars[offset] != _inputBuffer[ptr]) {
break;
}
++offset;
++ptr;
}
}
}
}
return _isNextTokenNameMaybe(i, sstr.getValue());
}
@Override
public String nextFieldName() throws IOException
{
// // // Note: this is almost a verbatim copy of nextToken() (minus comments)
_numTypesValid = NR_UNKNOWN;
if (_currToken == JsonToken.FIELD_NAME) {
_nextAfterName();
return null;
}
if (_tokenIncomplete) {
_skipString();
}
int i = _skipWSOrEnd();
if (i < 0) {
close();
_currToken = null;
return null;
}
_binaryValue = null;
if (i == INT_RBRACKET || i == INT_RCURLY) {
_closeScope(i);
return null;
}
if (_parsingContext.expectComma()) {
i = _skipComma(i);
if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
if ((i == INT_RBRACKET) || (i == INT_RCURLY)) {
_closeScope(i);
return null;
}
}
}
if (!_parsingContext.inObject()) {
_updateLocation();
_nextTokenNotInObject(i);
return null;
}
_updateNameLocation();
String name = (i == INT_QUOTE) ? _parseName() : _handleOddName(i);
_parsingContext.setCurrentName(name);
_currToken = JsonToken.FIELD_NAME;
i = _skipColon();
_updateLocation();
if (i == INT_QUOTE) {
_tokenIncomplete = true;
_nextToken = JsonToken.VALUE_STRING;
return name;
}
// Ok: we must have a value... what is it?
JsonToken t;
switch (i) {
case '-':
t = _parseNegNumber();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
t = _parsePosNumber(i);
break;
case 'f':
_matchFalse();
t = JsonToken.VALUE_FALSE;
break;
case 'n':
_matchNull();
t = JsonToken.VALUE_NULL;
break;
case 't':
_matchTrue();
t = JsonToken.VALUE_TRUE;
break;
case '[':
t = JsonToken.START_ARRAY;
break;
case '{':
t = JsonToken.START_OBJECT;
break;
default:
t = _handleOddValue(i);
break;
}
_nextToken = t;
return name;
}
private final void _isNextTokenNameYes(int i) throws IOException
{
_currToken = JsonToken.FIELD_NAME;
_updateLocation();
switch (i) {
case '"':
_tokenIncomplete = true;
_nextToken = JsonToken.VALUE_STRING;
return;
case '[':
_nextToken = JsonToken.START_ARRAY;
return;
case '{':
_nextToken = JsonToken.START_OBJECT;
return;
case 't':
_matchToken("true", 1);
_nextToken = JsonToken.VALUE_TRUE;
return;
case 'f':
_matchToken("false", 1);
_nextToken = JsonToken.VALUE_FALSE;
return;
case 'n':
_matchToken("null", 1);
_nextToken = JsonToken.VALUE_NULL;
return;
case '-':
_nextToken = _parseNegNumber();
return;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
_nextToken = _parsePosNumber(i);
return;
}
_nextToken = _handleOddValue(i);
}
protected boolean _isNextTokenNameMaybe(int i, String nameToMatch) throws IOException
{
// // // and this is back to standard nextToken()
String name = (i == INT_QUOTE) ? _parseName() : _handleOddName(i);
_parsingContext.setCurrentName(name);
_currToken = JsonToken.FIELD_NAME;
i = _skipColon();
_updateLocation();
if (i == INT_QUOTE) {
_tokenIncomplete = true;
_nextToken = JsonToken.VALUE_STRING;
return nameToMatch.equals(name);
}
// Ok: we must have a value... what is it?
JsonToken t;
switch (i) {
case '-':
t = _parseNegNumber();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
t = _parsePosNumber(i);
break;
case 'f':
_matchFalse();
t = JsonToken.VALUE_FALSE;
break;
case 'n':
_matchNull();
t = JsonToken.VALUE_NULL;
break;
case 't':
_matchTrue();
t = JsonToken.VALUE_TRUE;
break;
case '[':
t = JsonToken.START_ARRAY;
break;
case '{':
t = JsonToken.START_OBJECT;
break;
default:
t = _handleOddValue(i);
break;
}
_nextToken = t;
return nameToMatch.equals(name);
}
private final JsonToken _nextTokenNotInObject(int i) throws IOException
{
if (i == INT_QUOTE) {
_tokenIncomplete = true;
return (_currToken = JsonToken.VALUE_STRING);
}
switch (i) {
case '[':
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
return (_currToken = JsonToken.START_ARRAY);
case '{':
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
return (_currToken = JsonToken.START_OBJECT);
case 't':
_matchToken("true", 1);
return (_currToken = JsonToken.VALUE_TRUE);
case 'f':
_matchToken("false", 1);
return (_currToken = JsonToken.VALUE_FALSE);
case 'n':
_matchToken("null", 1);
return (_currToken = JsonToken.VALUE_NULL);
case '-':
return (_currToken = _parseNegNumber());
/* Should we have separate handling for plus? Although
* it is not allowed per se, it may be erroneously used,
* and could be indicated by a more specific error message.
*/
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return (_currToken = _parsePosNumber(i));
/*
* This check proceeds only if the Feature.ALLOW_MISSING_VALUES is enabled
* The Check is for missing values. Incase of missing values in an array, the next token will be either ',' or ']'.
* This case, decrements the already incremented _inputPtr in the buffer in case of comma(,)
* so that the existing flow goes back to checking the next token which will be comma again and
* it continues the parsing.
* Also the case returns NULL as current token in case of ',' or ']'.
*/
case ',':
case ']':
if ((_features & FEAT_MASK_ALLOW_MISSING) != 0) {
--_inputPtr;
return (_currToken = JsonToken.VALUE_NULL);
}
}
return (_currToken = _handleOddValue(i));
}
// note: identical to one in UTF8StreamJsonParser
@Override
public final String nextTextValue() throws IOException
{
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString();
}
return _textBuffer.contentsAsString();
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return null;
}
// !!! TODO: optimize this case as well
return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
}
// note: identical to one in Utf8StreamParser
@Override
public final int nextIntValue(int defaultValue) throws IOException
{
if (_currToken == JsonToken.FIELD_NAME) {
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_NUMBER_INT) {
return getIntValue();
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return defaultValue;
}
// !!! TODO: optimize this case as well
return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
}
// note: identical to one in Utf8StreamParser
@Override
public final long nextLongValue(long defaultValue) throws IOException
{
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_NUMBER_INT) {
return getLongValue();
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return defaultValue;
}
// !!! TODO: optimize this case as well
return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
}
// note: identical to one in UTF8StreamJsonParser
@Override
public final Boolean nextBooleanValue() throws IOException
{
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_TRUE) {
return Boolean.TRUE;
}
if (t == JsonToken.VALUE_FALSE) {
return Boolean.FALSE;
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return null;
}
JsonToken t = nextToken();
if (t != null) {
int id = t.id();
if (id == ID_TRUE) return Boolean.TRUE;
if (id == ID_FALSE) return Boolean.FALSE;
}
return null;
}
/*
/**********************************************************
/* Internal methods, number parsing
/**********************************************************
*/
/**
* Initial parsing method for number values. It needs to be able
* to parse enough input to be able to determine whether the
* value is to be considered a simple integer value, or a more
* generic decimal value: latter of which needs to be expressed
* as a floating point number. The basic rule is that if the number
* has no fractional or exponential part, it is an integer; otherwise
* a floating point number.
*
* Because much of input has to be processed in any case, no partial
* parsing is done: all input text will be stored for further
* processing. However, actual numeric value conversion will be
* deferred, since it is usually the most complicated and costliest
* part of processing.
*/
protected final JsonToken _parsePosNumber(int ch) throws IOException
{
/* Although we will always be complete with respect to textual
* representation (that is, all characters will be parsed),
* actual conversion to a number is deferred. Thus, need to
* note that no representations are valid yet
*/
int ptr = _inputPtr;
int startPtr = ptr-1; // to include digit already read
final int inputLen = _inputEnd;
// One special case, leading zero(es):
if (ch == INT_0) {
return _parseNumber2(false, startPtr);
}
/* First, let's see if the whole number is contained within
* the input buffer unsplit. This should be the common case;
* and to simplify processing, we will just reparse contents
* in the alternative case (number split on buffer boundary)
*/
int intLen = 1; // already got one
// First let's get the obligatory integer part:
int_loop:
while (true) {
if (ptr >= inputLen) {
_inputPtr = startPtr;
return _parseNumber2(false, startPtr);
}
ch = (int) _inputBuffer[ptr++];
if (ch < INT_0 || ch > INT_9) {
break int_loop;
}
++intLen;
}
if (ch == INT_PERIOD || ch == INT_e || ch == INT_E) {
_inputPtr = ptr;
return _parseFloat(ch, startPtr, ptr, false, intLen);
}
// Got it all: let's add to text buffer for parsing, access
--ptr; // need to push back following separator
_inputPtr = ptr;
// As per #105, need separating space between root values; check here
if (_parsingContext.inRoot()) {
_verifyRootSpace(ch);
}
int len = ptr-startPtr;
_textBuffer.resetWithShared(_inputBuffer, startPtr, len);
return resetInt(false, intLen);
}
private final JsonToken _parseFloat(int ch, int startPtr, int ptr, boolean neg, int intLen)
throws IOException
{
final int inputLen = _inputEnd;
int fractLen = 0;
// And then see if we get other parts
if (ch == '.') { // yes, fraction
fract_loop:
while (true) {
if (ptr >= inputLen) {
return _parseNumber2(neg, startPtr);
}
ch = (int) _inputBuffer[ptr++];
if (ch < INT_0 || ch > INT_9) {
break fract_loop;
}
++fractLen;
}
// must be followed by sequence of ints, one minimum
if (fractLen == 0) {
reportUnexpectedNumberChar(ch, "Decimal point not followed by a digit");
}
}
int expLen = 0;
if (ch == 'e' || ch == 'E') { // and/or exponent
if (ptr >= inputLen) {
_inputPtr = startPtr;
return _parseNumber2(neg, startPtr);
}
// Sign indicator?
ch = (int) _inputBuffer[ptr++];
if (ch == INT_MINUS || ch == INT_PLUS) { // yup, skip for now
if (ptr >= inputLen) {
_inputPtr = startPtr;
return _parseNumber2(neg, startPtr);
}
ch = (int) _inputBuffer[ptr++];
}
while (ch <= INT_9 && ch >= INT_0) {
++expLen;
if (ptr >= inputLen) {
_inputPtr = startPtr;
return _parseNumber2(neg, startPtr);
}
ch = (int) _inputBuffer[ptr++];
}
// must be followed by sequence of ints, one minimum
if (expLen == 0) {
reportUnexpectedNumberChar(ch, "Exponent indicator not followed by a digit");
}
}
--ptr; // need to push back following separator
_inputPtr = ptr;
// As per #105, need separating space between root values; check here
if (_parsingContext.inRoot()) {
_verifyRootSpace(ch);
}
int len = ptr-startPtr;
_textBuffer.resetWithShared(_inputBuffer, startPtr, len);
// And there we have it!
return resetFloat(neg, intLen, fractLen, expLen);
}
protected final JsonToken _parseNegNumber() throws IOException
{
int ptr = _inputPtr;
int startPtr = ptr-1; // to include sign/digit already read
final int inputLen = _inputEnd;
if (ptr >= inputLen) {
return _parseNumber2(true, startPtr);
}
int ch = _inputBuffer[ptr++];
// First check: must have a digit to follow minus sign
if (ch > INT_9 || ch < INT_0) {
_inputPtr = ptr;
return _handleInvalidNumberStart(ch, true);
}
// One special case, leading zero(es):
if (ch == INT_0) {
return _parseNumber2(true, startPtr);
}
int intLen = 1; // already got one
// First let's get the obligatory integer part:
int_loop:
while (true) {
if (ptr >= inputLen) {
return _parseNumber2(true, startPtr);
}
ch = (int) _inputBuffer[ptr++];
if (ch < INT_0 || ch > INT_9) {
break int_loop;
}
++intLen;
}
if (ch == INT_PERIOD || ch == INT_e || ch == INT_E) {
_inputPtr = ptr;
return _parseFloat(ch, startPtr, ptr, true, intLen);
}
--ptr;
_inputPtr = ptr;
if (_parsingContext.inRoot()) {
_verifyRootSpace(ch);
}
int len = ptr-startPtr;
_textBuffer.resetWithShared(_inputBuffer, startPtr, len);
return resetInt(true, intLen);
}
/**
* Method called to parse a number, when the primary parse
* method has failed to parse it, due to it being split on
* buffer boundary. As a result code is very similar, except
* that it has to explicitly copy contents to the text buffer
* instead of just sharing the main input buffer.
*/
private final JsonToken _parseNumber2(boolean neg, int startPtr) throws IOException
{
_inputPtr = neg ? (startPtr+1) : startPtr;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
int outPtr = 0;
// Need to prepend sign?
if (neg) {
outBuf[outPtr++] = '-';
}
// This is the place to do leading-zero check(s) too:
int intLen = 0;
char c = (_inputPtr < _inputEnd) ? _inputBuffer[_inputPtr++]
: getNextChar("No digit following minus sign", JsonToken.VALUE_NUMBER_INT);
if (c == '0') {
c = _verifyNoLeadingZeroes();
}
boolean eof = false;
// Ok, first the obligatory integer part:
int_loop:
while (c >= '0' && c <= '9') {
++intLen;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = c;
if (_inputPtr >= _inputEnd && !_loadMore()) {
// EOF is legal for main level int values
c = CHAR_NULL;
eof = true;
break int_loop;
}
c = _inputBuffer[_inputPtr++];
}
// Also, integer part is not optional
if (intLen == 0) {
return _handleInvalidNumberStart(c, neg);
}
int fractLen = 0;
// And then see if we get other parts
if (c == '.') { // yes, fraction
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = c;
fract_loop:
while (true) {
if (_inputPtr >= _inputEnd && !_loadMore()) {
eof = true;
break fract_loop;
}
c = _inputBuffer[_inputPtr++];
if (c < INT_0 || c > INT_9) {
break fract_loop;
}
++fractLen;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = c;
}
// must be followed by sequence of ints, one minimum
if (fractLen == 0) {
reportUnexpectedNumberChar(c, "Decimal point not followed by a digit");
}
}
int expLen = 0;
if (c == 'e' || c == 'E') { // exponent?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = c;
// Not optional, can require that we get one more char
c = (_inputPtr < _inputEnd) ? _inputBuffer[_inputPtr++]
: getNextChar("expected a digit for number exponent");
// Sign indicator?
if (c == '-' || c == '+') {
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = c;
// Likewise, non optional:
c = (_inputPtr < _inputEnd) ? _inputBuffer[_inputPtr++]
: getNextChar("expected a digit for number exponent");
}
exp_loop:
while (c <= INT_9 && c >= INT_0) {
++expLen;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = c;
if (_inputPtr >= _inputEnd && !_loadMore()) {
eof = true;
break exp_loop;
}
c = _inputBuffer[_inputPtr++];
}
// must be followed by sequence of ints, one minimum
if (expLen == 0) {
reportUnexpectedNumberChar(c, "Exponent indicator not followed by a digit");
}
}
// Ok; unless we hit end-of-input, need to push last char read back
if (!eof) {
--_inputPtr;
if (_parsingContext.inRoot()) {
_verifyRootSpace(c);
}
}
_textBuffer.setCurrentLength(outPtr);
// And there we have it!
return reset(neg, intLen, fractLen, expLen);
}
/**
* Method called when we have seen one zero, and want to ensure
* it is not followed by another
*/
private final char _verifyNoLeadingZeroes() throws IOException
{
// Fast case first:
if (_inputPtr < _inputEnd) {
char ch = _inputBuffer[_inputPtr];
// if not followed by a number (probably '.'); return zero as is, to be included
if (ch < '0' || ch > '9') {
return '0';
}
}
// and offline the less common case
return _verifyNLZ2();
}
private char _verifyNLZ2() throws IOException
{
if (_inputPtr >= _inputEnd && !_loadMore()) {
return '0';
}
char ch = _inputBuffer[_inputPtr];
if (ch < '0' || ch > '9') {
return '0';
}
if ((_features & FEAT_MASK_LEADING_ZEROS) == 0) {
reportInvalidNumber("Leading zeroes not allowed");
}
// if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number)
++_inputPtr; // Leading zero to be skipped
if (ch == INT_0) {
while (_inputPtr < _inputEnd || _loadMore()) {
ch = _inputBuffer[_inputPtr];
if (ch < '0' || ch > '9') { // followed by non-number; retain one zero
return '0';
}
++_inputPtr; // skip previous zero
if (ch != '0') { // followed by other number; return
break;
}
}
}
return ch;
}
/**
* Method called if expected numeric value (due to leading sign) does not
* look like a number
*/
protected JsonToken _handleInvalidNumberStart(int ch, boolean negative) throws IOException
{
if (ch == 'I') {
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOFInValue(JsonToken.VALUE_NUMBER_INT);
}
}
ch = _inputBuffer[_inputPtr++];
if (ch == 'N') {
String match = negative ? "-INF" :"+INF";
_matchToken(match, 3);
if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) {
return resetAsNaN(match, negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
}
_reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
} else if (ch == 'n') {
String match = negative ? "-Infinity" :"+Infinity";
_matchToken(match, 3);
if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) {
return resetAsNaN(match, negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
}
_reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
}
}
reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value");
return null;
}
/**
* Method called to ensure that a root-value is followed by a space
* token.
*
* NOTE: caller MUST ensure there is at least one character available;
* and that input pointer is AT given char (not past)
*/
private final void _verifyRootSpace(int ch) throws IOException
{
// caller had pushed it back, before calling; reset
++_inputPtr;
switch (ch) {
case ' ':
case '\t':
return;
case '\r':
_skipCR();
return;
case '\n':
++_currInputRow;
_currInputRowStart = _inputPtr;
return;
}
_reportMissingRootWS(ch);
}
/*
/**********************************************************
/* Internal methods, secondary parsing
/**********************************************************
*/
protected final String _parseName() throws IOException
{
// First: let's try to see if we have a simple name: one that does
// not cross input buffer boundary, and does not contain escape sequences.
int ptr = _inputPtr;
int hash = _hashSeed;
final int[] codes = _icLatin1;
while (ptr < _inputEnd) {
int ch = _inputBuffer[ptr];
if (ch < codes.length && codes[ch] != 0) {
if (ch == '"') {
int start = _inputPtr;
_inputPtr = ptr+1; // to skip the quote
return _symbols.findSymbol(_inputBuffer, start, ptr - start, hash);
}
break;
}
hash = (hash * CharsToNameCanonicalizer.HASH_MULT) + ch;
++ptr;
}
int start = _inputPtr;
_inputPtr = ptr;
return _parseName2(start, hash, INT_QUOTE);
}
private String _parseName2(int startPtr, int hash, int endChar) throws IOException
{
_textBuffer.resetWithShared(_inputBuffer, startPtr, (_inputPtr - startPtr));
/* Output pointers; calls will also ensure that the buffer is
* not shared and has room for at least one more char.
*/
char[] outBuf = _textBuffer.getCurrentSegment();
int outPtr = _textBuffer.getCurrentSegmentSize();
while (true) {
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOF(" in field name", JsonToken.FIELD_NAME);
}
}
char c = _inputBuffer[_inputPtr++];
int i = (int) c;
if (i <= INT_BACKSLASH) {
if (i == INT_BACKSLASH) {
/* Although chars outside of BMP are to be escaped as
* an UTF-16 surrogate pair, does that affect decoding?
* For now let's assume it does not.
*/
c = _decodeEscaped();
} else if (i <= endChar) {
if (i == endChar) {
break;
}
if (i < INT_SPACE) {
_throwUnquotedSpace(i, "name");
}
}
}
hash = (hash * CharsToNameCanonicalizer.HASH_MULT) + c;
// Ok, let's add char to output:
outBuf[outPtr++] = c;
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
}
_textBuffer.setCurrentLength(outPtr);
{
TextBuffer tb = _textBuffer;
char[] buf = tb.getTextBuffer();
int start = tb.getTextOffset();
int len = tb.size();
return _symbols.findSymbol(buf, start, len, hash);
}
}
/**
* Method called when we see non-white space character other
* than double quote, when expecting a field name.
* In standard mode will just throw an expection; but
* in non-standard modes may be able to parse name.
*/
protected String _handleOddName(int i) throws IOException
{
// [JACKSON-173]: allow single quotes
if (i == '\'' && (_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) {
return _parseAposName();
}
// [JACKSON-69]: allow unquoted names if feature enabled:
if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) {
_reportUnexpectedChar(i, "was expecting double-quote to start field name");
}
final int[] codes = CharTypes.getInputCodeLatin1JsNames();
final int maxCode = codes.length;
// Also: first char must be a valid name char, but NOT be number
boolean firstOk;
if (i < maxCode) { // identifier, or a number ([Issue#102])
firstOk = (codes[i] == 0);
} else {
firstOk = Character.isJavaIdentifierPart((char) i);
}
if (!firstOk) {
_reportUnexpectedChar(i, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name");
}
int ptr = _inputPtr;
int hash = _hashSeed;
final int inputLen = _inputEnd;
if (ptr < inputLen) {
do {
int ch = _inputBuffer[ptr];
if (ch < maxCode) {
if (codes[ch] != 0) {
int start = _inputPtr-1; // -1 to bring back first char
_inputPtr = ptr;
return _symbols.findSymbol(_inputBuffer, start, ptr - start, hash);
}
} else if (!Character.isJavaIdentifierPart((char) ch)) {
int start = _inputPtr-1; // -1 to bring back first char
_inputPtr = ptr;
return _symbols.findSymbol(_inputBuffer, start, ptr - start, hash);
}
hash = (hash * CharsToNameCanonicalizer.HASH_MULT) + ch;
++ptr;
} while (ptr < inputLen);
}
int start = _inputPtr-1;
_inputPtr = ptr;
return _handleOddName2(start, hash, codes);
}
protected String _parseAposName() throws IOException
{
// Note: mostly copy of_parseFieldName
int ptr = _inputPtr;
int hash = _hashSeed;
final int inputLen = _inputEnd;
if (ptr < inputLen) {
final int[] codes = _icLatin1;
final int maxCode = codes.length;
do {
int ch = _inputBuffer[ptr];
if (ch == '\'') {
int start = _inputPtr;
_inputPtr = ptr+1; // to skip the quote
return _symbols.findSymbol(_inputBuffer, start, ptr - start, hash);
}
if (ch < maxCode && codes[ch] != 0) {
break;
}
hash = (hash * CharsToNameCanonicalizer.HASH_MULT) + ch;
++ptr;
} while (ptr < inputLen);
}
int start = _inputPtr;
_inputPtr = ptr;
return _parseName2(start, hash, '\'');
}
/**
* Method for handling cases where first non-space character
* of an expected value token is not legal for standard JSON content.
*/
protected JsonToken _handleOddValue(int i) throws IOException
{
// Most likely an error, unless we are to allow single-quote-strings
switch (i) {
case '\'':
/* Allow single quotes? Unlike with regular Strings, we'll eagerly parse
* contents; this so that there'sno need to store information on quote char used.
* Also, no separation to fast/slow parsing; we'll just do
* one regular (~= slowish) parsing, to keep code simple
*/
if ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) {
return _handleApos();
}
break;
case ']':
/* 28-Mar-2016: [core#116]: If Feature.ALLOW_MISSING_VALUES is enabled
* we may allow "missing values", that is, encountering a trailing
* comma or closing marker where value would be expected
*/
if (!_parsingContext.inArray()) {
break;
}
// fall through
case ',':
if ((_features & FEAT_MASK_ALLOW_MISSING) != 0) {
--_inputPtr;
return JsonToken.VALUE_NULL;
}
break;
case 'N':
_matchToken("NaN", 1);
if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) {
return resetAsNaN("NaN", Double.NaN);
}
_reportError("Non-standard token 'NaN': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
break;
case 'I':
_matchToken("Infinity", 1);
if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) {
return resetAsNaN("Infinity", Double.POSITIVE_INFINITY);
}
_reportError("Non-standard token 'Infinity': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
break;
case '+': // note: '-' is taken as number
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOFInValue(JsonToken.VALUE_NUMBER_INT);
}
}
return _handleInvalidNumberStart(_inputBuffer[_inputPtr++], false);
}
// [core#77] Try to decode most likely token
if (Character.isJavaIdentifierStart(i)) {
_reportInvalidToken(""+((char) i), _validJsonTokenList());
}
// but if it doesn't look like a token:
_reportUnexpectedChar(i, "expected a valid value "+_validJsonValueList());
return null;
}
protected JsonToken _handleApos() throws IOException
{
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
int outPtr = _textBuffer.getCurrentSegmentSize();
while (true) {
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOF(": was expecting closing quote for a string value",
JsonToken.VALUE_STRING);
}
}
char c = _inputBuffer[_inputPtr++];
int i = (int) c;
if (i <= '\\') {
if (i == '\\') {
/* Although chars outside of BMP are to be escaped as
* an UTF-16 surrogate pair, does that affect decoding?
* For now let's assume it does not.
*/
c = _decodeEscaped();
} else if (i <= '\'') {
if (i == '\'') {
break;
}
if (i < INT_SPACE) {
_throwUnquotedSpace(i, "string value");
}
}
}
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
// Ok, let's add char to output:
outBuf[outPtr++] = c;
}
_textBuffer.setCurrentLength(outPtr);
return JsonToken.VALUE_STRING;
}
private String _handleOddName2(int startPtr, int hash, int[] codes) throws IOException
{
_textBuffer.resetWithShared(_inputBuffer, startPtr, (_inputPtr - startPtr));
char[] outBuf = _textBuffer.getCurrentSegment();
int outPtr = _textBuffer.getCurrentSegmentSize();
final int maxCode = codes.length;
while (true) {
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) { // acceptable for now (will error out later)
break;
}
}
char c = _inputBuffer[_inputPtr];
int i = (int) c;
if (i < maxCode) {
if (codes[i] != 0) {
break;
}
} else if (!Character.isJavaIdentifierPart(c)) {
break;
}
++_inputPtr;
hash = (hash * CharsToNameCanonicalizer.HASH_MULT) + i;
// Ok, let's add char to output:
outBuf[outPtr++] = c;
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
}
_textBuffer.setCurrentLength(outPtr);
{
TextBuffer tb = _textBuffer;
char[] buf = tb.getTextBuffer();
int start = tb.getTextOffset();
int len = tb.size();
return _symbols.findSymbol(buf, start, len, hash);
}
}
@Override
protected final void _finishString() throws IOException
{
/* First: let's try to see if we have simple String value: one
* that does not cross input buffer boundary, and does not
* contain escape sequences.
*/
int ptr = _inputPtr;
final int inputLen = _inputEnd;
if (ptr < inputLen) {
final int[] codes = _icLatin1;
final int maxCode = codes.length;
do {
int ch = _inputBuffer[ptr];
if (ch < maxCode && codes[ch] != 0) {
if (ch == '"') {
_textBuffer.resetWithShared(_inputBuffer, _inputPtr, (ptr-_inputPtr));
_inputPtr = ptr+1;
// Yes, we got it all
return;
}
break;
}
++ptr;
} while (ptr < inputLen);
}
// Either ran out of input, or bumped into an escape sequence...
_textBuffer.resetWithCopy(_inputBuffer, _inputPtr, (ptr-_inputPtr));
_inputPtr = ptr;
_finishString2();
}
protected void _finishString2() throws IOException
{
char[] outBuf = _textBuffer.getCurrentSegment();
int outPtr = _textBuffer.getCurrentSegmentSize();
final int[] codes = _icLatin1;
final int maxCode = codes.length;
while (true) {
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOF(": was expecting closing quote for a string value",
JsonToken.VALUE_STRING);
}
}
char c = _inputBuffer[_inputPtr++];
int i = (int) c;
if (i < maxCode && codes[i] != 0) {
if (i == INT_QUOTE) {
break;
} else if (i == INT_BACKSLASH) {
/* Although chars outside of BMP are to be escaped as
* an UTF-16 surrogate pair, does that affect decoding?
* For now let's assume it does not.
*/
c = _decodeEscaped();
} else if (i < INT_SPACE) {
_throwUnquotedSpace(i, "string value");
} // anything else?
}
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
// Ok, let's add char to output:
outBuf[outPtr++] = c;
}
_textBuffer.setCurrentLength(outPtr);
}
/**
* Method called to skim through rest of unparsed String value,
* if it is not needed. This can be done bit faster if contents
* need not be stored for future access.
*/
protected final void _skipString() throws IOException
{
_tokenIncomplete = false;
int inPtr = _inputPtr;
int inLen = _inputEnd;
char[] inBuf = _inputBuffer;
while (true) {
if (inPtr >= inLen) {
_inputPtr = inPtr;
if (!_loadMore()) {
_reportInvalidEOF(": was expecting closing quote for a string value",
JsonToken.VALUE_STRING);
}
inPtr = _inputPtr;
inLen = _inputEnd;
}
char c = inBuf[inPtr++];
int i = (int) c;
if (i <= INT_BACKSLASH) {
if (i == INT_BACKSLASH) {
// Although chars outside of BMP are to be escaped as an UTF-16 surrogate pair,
// does that affect decoding? For now let's assume it does not.
_inputPtr = inPtr;
/*c = */ _decodeEscaped();
inPtr = _inputPtr;
inLen = _inputEnd;
} else if (i <= INT_QUOTE) {
if (i == INT_QUOTE) {
_inputPtr = inPtr;
break;
}
if (i < INT_SPACE) {
_inputPtr = inPtr;
_throwUnquotedSpace(i, "string value");
}
}
}
}
}
/*
/**********************************************************
/* Internal methods, other parsing
/**********************************************************
*/
/**
* We actually need to check the character value here
* (to see if we have \n following \r).
*/
protected final void _skipCR() throws IOException {
if (_inputPtr < _inputEnd || _loadMore()) {
if (_inputBuffer[_inputPtr] == '\n') {
++_inputPtr;
}
}
++_currInputRow;
_currInputRowStart = _inputPtr;
}
private final int _skipColon() throws IOException
{
if ((_inputPtr + 4) >= _inputEnd) {
return _skipColon2(false);
}
char c = _inputBuffer[_inputPtr];
if (c == ':') { // common case, no leading space
int i = _inputBuffer[++_inputPtr];
if (i > INT_SPACE) { // nor trailing
if (i == INT_SLASH || i == INT_HASH) {
return _skipColon2(true);
}
++_inputPtr;
return i;
}
if (i == INT_SPACE || i == INT_TAB) {
i = (int) _inputBuffer[++_inputPtr];
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
return _skipColon2(true);
}
++_inputPtr;
return i;
}
}
return _skipColon2(true); // true -> skipped colon
}
if (c == ' ' || c == '\t') {
c = _inputBuffer[++_inputPtr];
}
if (c == ':') {
int i = _inputBuffer[++_inputPtr];
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
return _skipColon2(true);
}
++_inputPtr;
return i;
}
if (i == INT_SPACE || i == INT_TAB) {
i = (int) _inputBuffer[++_inputPtr];
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
return _skipColon2(true);
}
++_inputPtr;
return i;
}
}
return _skipColon2(true);
}
return _skipColon2(false);
}
private final int _skipColon2(boolean gotColon) throws IOException
{
while (_inputPtr < _inputEnd || _loadMore()) {
int i = (int) _inputBuffer[_inputPtr++];
if (i > INT_SPACE) {
if (i == INT_SLASH) {
_skipComment();
continue;
}
if (i == INT_HASH) {
if (_skipYAMLComment()) {
continue;
}
}
if (gotColon) {
return i;
}
if (i != INT_COLON) {
_reportUnexpectedChar(i, "was expecting a colon to separate field name and value");
}
gotColon = true;
continue;
}
if (i < INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
_reportInvalidEOF(" within/between "+_parsingContext.typeDesc()+" entries",
null);
return -1;
}
// Variant called when we know there's at least 4 more bytes available
private final int _skipColonFast(int ptr) throws IOException
{
int i = (int) _inputBuffer[ptr++];
if (i == INT_COLON) { // common case, no leading space
i = _inputBuffer[ptr++];
if (i > INT_SPACE) { // nor trailing
if (i != INT_SLASH && i != INT_HASH) {
_inputPtr = ptr;
return i;
}
} else if (i == INT_SPACE || i == INT_TAB) {
i = (int) _inputBuffer[ptr++];
if (i > INT_SPACE) {
if (i != INT_SLASH && i != INT_HASH) {
_inputPtr = ptr;
return i;
}
}
}
_inputPtr = ptr-1;
return _skipColon2(true); // true -> skipped colon
}
if (i == INT_SPACE || i == INT_TAB) {
i = _inputBuffer[ptr++];
}
boolean gotColon = (i == INT_COLON);
if (gotColon) {
i = _inputBuffer[ptr++];
if (i > INT_SPACE) {
if (i != INT_SLASH && i != INT_HASH) {
_inputPtr = ptr;
return i;
}
} else if (i == INT_SPACE || i == INT_TAB) {
i = (int) _inputBuffer[ptr++];
if (i > INT_SPACE) {
if (i != INT_SLASH && i != INT_HASH) {
_inputPtr = ptr;
return i;
}
}
}
}
_inputPtr = ptr-1;
return _skipColon2(gotColon);
}
// Primary loop: no reloading, comment handling
private final int _skipComma(int i) throws IOException
{
if (i != INT_COMMA) {
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
while (_inputPtr < _inputEnd) {
i = (int) _inputBuffer[_inputPtr++];
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
--_inputPtr;
return _skipAfterComma2();
}
return i;
}
if (i < INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
return _skipAfterComma2();
}
private final int _skipAfterComma2() throws IOException
{
while (_inputPtr < _inputEnd || _loadMore()) {
int i = (int) _inputBuffer[_inputPtr++];
if (i > INT_SPACE) {
if (i == INT_SLASH) {
_skipComment();
continue;
}
if (i == INT_HASH) {
if (_skipYAMLComment()) {
continue;
}
}
return i;
}
if (i < INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
throw _constructError("Unexpected end-of-input within/between "+_parsingContext.typeDesc()+" entries");
}
private final int _skipWSOrEnd() throws IOException
{
// Let's handle first character separately since it is likely that
// it is either non-whitespace; or we have longer run of white space
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
return _eofAsNextChar();
}
}
int i = _inputBuffer[_inputPtr++];
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
--_inputPtr;
return _skipWSOrEnd2();
}
return i;
}
if (i != INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
while (_inputPtr < _inputEnd) {
i = (int) _inputBuffer[_inputPtr++];
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
--_inputPtr;
return _skipWSOrEnd2();
}
return i;
}
if (i != INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
return _skipWSOrEnd2();
}
private int _skipWSOrEnd2() throws IOException
{
while (true) {
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) { // We ran out of input...
return _eofAsNextChar();
}
}
int i = (int) _inputBuffer[_inputPtr++];
if (i > INT_SPACE) {
if (i == INT_SLASH) {
_skipComment();
continue;
}
if (i == INT_HASH) {
if (_skipYAMLComment()) {
continue;
}
}
return i;
} else if (i != INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
}
private void _skipComment() throws IOException
{
if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) {
_reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)");
}
// First: check which comment (if either) it is:
if (_inputPtr >= _inputEnd && !_loadMore()) {
_reportInvalidEOF(" in a comment", null);
}
char c = _inputBuffer[_inputPtr++];
if (c == '/') {
_skipLine();
} else if (c == '*') {
_skipCComment();
} else {
_reportUnexpectedChar(c, "was expecting either '*' or '/' for a comment");
}
}
private void _skipCComment() throws IOException
{
// Ok: need the matching '*/'
while ((_inputPtr < _inputEnd) || _loadMore()) {
int i = (int) _inputBuffer[_inputPtr++];
if (i <= '*') {
if (i == '*') { // end?
if ((_inputPtr >= _inputEnd) && !_loadMore()) {
break;
}
if (_inputBuffer[_inputPtr] == INT_SLASH) {
++_inputPtr;
return;
}
continue;
}
if (i < INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
}
_reportInvalidEOF(" in a comment", null);
}
private boolean _skipYAMLComment() throws IOException
{
if ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) == 0) {
return false;
}
_skipLine();
return true;
}
private void _skipLine() throws IOException
{
// Ok: need to find EOF or linefeed
while ((_inputPtr < _inputEnd) || _loadMore()) {
int i = (int) _inputBuffer[_inputPtr++];
if (i < INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
break;
} else if (i == INT_CR) {
_skipCR();
break;
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
}
@Override
protected char _decodeEscaped() throws IOException
{
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOF(" in character escape sequence", JsonToken.VALUE_STRING);
}
}
char c = _inputBuffer[_inputPtr++];
switch ((int) c) {
// First, ones that are mapped
case 'b':
return '\b';
case 't':
return '\t';
case 'n':
return '\n';
case 'f':
return '\f';
case 'r':
return '\r';
// And these are to be returned as they are
case '"':
case '/':
case '\\':
return c;
case 'u': // and finally hex-escaped
break;
default:
return _handleUnrecognizedCharacterEscape(c);
}
// Ok, a hex escape. Need 4 characters
int value = 0;
for (int i = 0; i < 4; ++i) {
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOF(" in character escape sequence", JsonToken.VALUE_STRING);
}
}
int ch = (int) _inputBuffer[_inputPtr++];
int digit = CharTypes.charToHex(ch);
if (digit < 0) {
_reportUnexpectedChar(ch, "expected a hex-digit for character escape sequence");
}
value = (value << 4) | digit;
}
return (char) value;
}
private final void _matchTrue() throws IOException {
int ptr = _inputPtr;
if ((ptr + 3) < _inputEnd) {
final char[] b = _inputBuffer;
if (b[ptr] == 'r' && b[++ptr] == 'u' && b[++ptr] == 'e') {
char c = b[++ptr];
if (c < '0' || c == ']' || c == '}') { // expected/allowed chars
_inputPtr = ptr;
return;
}
}
}
// buffer boundary, or problem, offline
_matchToken("true", 1);
}
private final void _matchFalse() throws IOException {
int ptr = _inputPtr;
if ((ptr + 4) < _inputEnd) {
final char[] b = _inputBuffer;
if (b[ptr] == 'a' && b[++ptr] == 'l' && b[++ptr] == 's' && b[++ptr] == 'e') {
char c = b[++ptr];
if (c < '0' || c == ']' || c == '}') { // expected/allowed chars
_inputPtr = ptr;
return;
}
}
}
// buffer boundary, or problem, offline
_matchToken("false", 1);
}
private final void _matchNull() throws IOException {
int ptr = _inputPtr;
if ((ptr + 3) < _inputEnd) {
final char[] b = _inputBuffer;
if (b[ptr] == 'u' && b[++ptr] == 'l' && b[++ptr] == 'l') {
char c = b[++ptr];
if (c < '0' || c == ']' || c == '}') { // expected/allowed chars
_inputPtr = ptr;
return;
}
}
}
// buffer boundary, or problem, offline
_matchToken("null", 1);
}
/**
* Helper method for checking whether input matches expected token
*/
protected final void _matchToken(String matchStr, int i) throws IOException
{
final int len = matchStr.length();
if ((_inputPtr + len) >= _inputEnd) {
_matchToken2(matchStr, i);
return;
}
do {
if (_inputBuffer[_inputPtr] != matchStr.charAt(i)) {
_reportInvalidToken(matchStr.substring(0, i));
}
++_inputPtr;
} while (++i < len);
int ch = _inputBuffer[_inputPtr];
if (ch >= '0' && ch != ']' && ch != '}') { // expected/allowed chars
_checkMatchEnd(matchStr, i, ch);
}
}
private final void _matchToken2(String matchStr, int i) throws IOException
{
final int len = matchStr.length();
do {
if (((_inputPtr >= _inputEnd) && !_loadMore())
|| (_inputBuffer[_inputPtr] != matchStr.charAt(i))) {
_reportInvalidToken(matchStr.substring(0, i));
}
++_inputPtr;
} while (++i < len);
// but let's also ensure we either get EOF, or non-alphanum char...
if (_inputPtr >= _inputEnd && !_loadMore()) {
return;
}
int ch = _inputBuffer[_inputPtr];
if (ch >= '0' && ch != ']' && ch != '}') { // expected/allowed chars
_checkMatchEnd(matchStr, i, ch);
}
}
private final void _checkMatchEnd(String matchStr, int i, int c) throws IOException {
// but actually only alphanums are problematic
char ch = (char) c;
if (Character.isJavaIdentifierPart(ch)) {
_reportInvalidToken(matchStr.substring(0, i));
}
}
/*
/**********************************************************
/* Binary access
/**********************************************************
*/
/**
* Efficient handling for incremental parsing of base64-encoded
* textual content.
*/
@SuppressWarnings("resource")
protected byte[] _decodeBase64(Base64Variant b64variant) throws IOException
{
ByteArrayBuilder builder = _getByteArrayBuilder();
//main_loop:
while (true) {
// first, we'll skip preceding white space, if any
char ch;
do {
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
} while (ch <= INT_SPACE);
int bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
if (ch == '"') { // reached the end, fair and square?
return builder.toByteArray();
}
bits = _decodeBase64Escape(b64variant, ch, 0);
if (bits < 0) { // white space to skip
continue;
}
}
int decodedData = bits;
// then second base64 char; can't get padding yet, nor ws
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
bits = _decodeBase64Escape(b64variant, ch, 1);
}
decodedData = (decodedData << 6) | bits;
// third base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
bits = b64variant.decodeBase64Char(ch);
// First branch: can get padding (-> 1 byte)
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// as per [JACKSON-631], could also just be 'missing' padding
if (ch == '"') {
decodedData >>= 4;
builder.append(decodedData);
if (b64variant.usesPadding()) {
--_inputPtr; // to keep parser state bit more consistent
_handleBase64MissingPadding(b64variant);
}
return builder.toByteArray();
}
bits = _decodeBase64Escape(b64variant, ch, 2);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
// Ok, must get more padding chars, then
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
if (!b64variant.usesPaddingChar(ch)) {
if (_decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) {
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
}
}
// Got 12 bits, only need 8, need to shift
decodedData >>= 4;
builder.append(decodedData);
continue;
}
// otherwise we got escaped other char, to be processed below
}
// Nope, 2 or 3 bytes
decodedData = (decodedData << 6) | bits;
// fourth and last base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++];
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// as per [JACKSON-631], could also just be 'missing' padding
if (ch == '"') {
decodedData >>= 2;
builder.appendTwoBytes(decodedData);
if (b64variant.usesPadding()) {
--_inputPtr; // to keep parser state bit more consistent
_handleBase64MissingPadding(b64variant);
}
return builder.toByteArray();
}
bits = _decodeBase64Escape(b64variant, ch, 3);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
// With padding we only get 2 bytes; but we have
// to shift it a bit so it is identical to triplet
// case with partial output.
// 3 chars gives 3x6 == 18 bits, of which 2 are
// dummies, need to discard:
decodedData >>= 2;
builder.appendTwoBytes(decodedData);
continue;
}
// otherwise we got escaped other char, to be processed below
}
// otherwise, our triplet is now complete
decodedData = (decodedData << 6) | bits;
builder.appendThreeBytes(decodedData);
}
}
/*
/**********************************************************
/* Internal methods, location updating (refactored in 2.7)
/**********************************************************
*/
@Override
public JsonLocation getTokenLocation()
{
if (_currToken == JsonToken.FIELD_NAME) {
long total = _currInputProcessed + (_nameStartOffset-1);
return new JsonLocation(_getSourceReference(),
-1L, total, _nameStartRow, _nameStartCol);
}
return new JsonLocation(_getSourceReference(),
-1L, _tokenInputTotal-1, _tokenInputRow, _tokenInputCol);
}
@Override
public JsonLocation getCurrentLocation() {
int col = _inputPtr - _currInputRowStart + 1; // 1-based
return new JsonLocation(_getSourceReference(),
-1L, _currInputProcessed + _inputPtr,
_currInputRow, col);
}
// @since 2.7
private final void _updateLocation()
{
int ptr = _inputPtr;
_tokenInputTotal = _currInputProcessed + ptr;
_tokenInputRow = _currInputRow;
_tokenInputCol = ptr - _currInputRowStart;
}
// @since 2.7
private final void _updateNameLocation()
{
int ptr = _inputPtr;
_nameStartOffset = ptr;
_nameStartRow = _currInputRow;
_nameStartCol = ptr - _currInputRowStart;
}
/*
/**********************************************************
/* Error reporting
/**********************************************************
*/
protected void _reportInvalidToken(String matchedPart) throws IOException {
_reportInvalidToken(matchedPart, _validJsonTokenList());
}
protected void _reportInvalidToken(String matchedPart, String msg) throws IOException
{
/* Let's just try to find what appears to be the token, using
* regular Java identifier character rules. It's just a heuristic,
* nothing fancy here.
*/
StringBuilder sb = new StringBuilder(matchedPart);
while ((_inputPtr < _inputEnd) || _loadMore()) {
char c = _inputBuffer[_inputPtr];
if (!Character.isJavaIdentifierPart(c)) {
break;
}
++_inputPtr;
sb.append(c);
if (sb.length() >= MAX_ERROR_TOKEN_LENGTH) {
sb.append("...");
break;
}
}
_reportError("Unrecognized token '%s': was expecting %s", sb, msg);
}
/*
/**********************************************************
/* Internal methods, other
/**********************************************************
*/
private void _closeScope(int i) throws JsonParseException {
if (i == INT_RBRACKET) {
_updateLocation();
if (!_parsingContext.inArray()) {
_reportMismatchedEndMarker(i, '}');
}
_parsingContext = _parsingContext.clearAndGetParent();
_currToken = JsonToken.END_ARRAY;
}
if (i == INT_RCURLY) {
_updateLocation();
if (!_parsingContext.inObject()) {
_reportMismatchedEndMarker(i, ']');
}
_parsingContext = _parsingContext.clearAndGetParent();
_currToken = JsonToken.END_OBJECT;
}
}
}
UTF8DataInputJsonParser.java 0000664 0000000 0000000 00000306120 13561642473 0034445 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json package com.fasterxml.jackson.core.json;
import java.io.*;
import java.util.Arrays;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.base.ParserBase;
import com.fasterxml.jackson.core.io.CharTypes;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
import com.fasterxml.jackson.core.util.*;
import static com.fasterxml.jackson.core.JsonTokenId.*;
/**
* This is a concrete implementation of {@link JsonParser}, which is
* based on a {@link java.io.DataInput} as the input source.
*
* Due to limitations in look-ahead (basically there's none), as well
* as overhead of reading content mostly byte-by-byte,
* there are some
* minor differences from regular streaming parsing. Specifically:
*
* Because much of input has to be processed in any case, no partial
* parsing is done: all input text will be stored for further
* processing. However, actual numeric value conversion will be
* deferred, since it is usually the most complicated and costliest
* part of processing.
*/
protected JsonToken _parsePosNumber(int c) throws IOException
{
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
int outPtr;
// One special case: if first char is 0, must not be followed by a digit.
// Gets bit tricky as we only want to retain 0 if it's the full value
if (c == INT_0) {
c = _handleLeadingZeroes();
if (c <= INT_9 && c >= INT_0) { // skip if followed by digit
outPtr = 0;
} else {
outBuf[0] = '0';
outPtr = 1;
}
} else {
outBuf[0] = (char) c;
c = _inputData.readUnsignedByte();
outPtr = 1;
}
int intLen = outPtr;
// With this, we have a nice and tight loop:
while (c <= INT_9 && c >= INT_0) {
++intLen;
outBuf[outPtr++] = (char) c;
c = _inputData.readUnsignedByte();
}
if (c == '.' || c == 'e' || c == 'E') {
return _parseFloat(outBuf, outPtr, c, false, intLen);
}
_textBuffer.setCurrentLength(outPtr);
// As per [core#105], need separating space between root values; check here
if (_parsingContext.inRoot()) {
_verifyRootSpace();
} else {
_nextByte = c;
}
// And there we have it!
return resetInt(false, intLen);
}
protected JsonToken _parseNegNumber() throws IOException
{
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
int outPtr = 0;
// Need to prepend sign?
outBuf[outPtr++] = '-';
int c = _inputData.readUnsignedByte();
outBuf[outPtr++] = (char) c;
// Note: must be followed by a digit
if (c <= INT_0) {
// One special case: if first char is 0 need to check no leading zeroes
if (c == INT_0) {
c = _handleLeadingZeroes();
} else {
return _handleInvalidNumberStart(c, true);
}
} else {
if (c > INT_9) {
return _handleInvalidNumberStart(c, true);
}
c = _inputData.readUnsignedByte();
}
// Ok: we can first just add digit we saw first:
int intLen = 1;
// With this, we have a nice and tight loop:
while (c <= INT_9 && c >= INT_0) {
++intLen;
outBuf[outPtr++] = (char) c;
c = _inputData.readUnsignedByte();
}
if (c == '.' || c == 'e' || c == 'E') {
return _parseFloat(outBuf, outPtr, c, true, intLen);
}
_textBuffer.setCurrentLength(outPtr);
// As per [core#105], need separating space between root values; check here
_nextByte = c;
if (_parsingContext.inRoot()) {
_verifyRootSpace();
}
// And there we have it!
return resetInt(true, intLen);
}
/**
* Method called when we have seen one zero, and want to ensure
* it is not followed by another, or, if leading zeroes allowed,
* skipped redundant ones.
*
* @return Character immediately following zeroes
*/
private final int _handleLeadingZeroes() throws IOException
{
int ch = _inputData.readUnsignedByte();
// if not followed by a number (probably '.'); return zero as is, to be included
if (ch < INT_0 || ch > INT_9) {
return ch;
}
// we may want to allow leading zeroes them, after all...
if ((_features & FEAT_MASK_LEADING_ZEROS) == 0) {
reportInvalidNumber("Leading zeroes not allowed");
}
// if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number)
while (ch == INT_0) {
ch = _inputData.readUnsignedByte();
}
return ch;
}
private final JsonToken _parseFloat(char[] outBuf, int outPtr, int c,
boolean negative, int integerPartLength) throws IOException
{
int fractLen = 0;
// And then see if we get other parts
if (c == INT_PERIOD) { // yes, fraction
outBuf[outPtr++] = (char) c;
fract_loop:
while (true) {
c = _inputData.readUnsignedByte();
if (c < INT_0 || c > INT_9) {
break fract_loop;
}
++fractLen;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
}
// must be followed by sequence of ints, one minimum
if (fractLen == 0) {
reportUnexpectedNumberChar(c, "Decimal point not followed by a digit");
}
}
int expLen = 0;
if (c == INT_e || c == INT_E) { // exponent?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
c = _inputData.readUnsignedByte();
// Sign indicator?
if (c == '-' || c == '+') {
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
c = _inputData.readUnsignedByte();
}
while (c <= INT_9 && c >= INT_0) {
++expLen;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
c = _inputData.readUnsignedByte();
}
// must be followed by sequence of ints, one minimum
if (expLen == 0) {
reportUnexpectedNumberChar(c, "Exponent indicator not followed by a digit");
}
}
// Ok; unless we hit end-of-input, need to push last char read back
// As per #105, need separating space between root values; check here
_nextByte = c;
if (_parsingContext.inRoot()) {
_verifyRootSpace();
}
_textBuffer.setCurrentLength(outPtr);
// And there we have it!
return resetFloat(negative, integerPartLength, fractLen, expLen);
}
/**
* Method called to ensure that a root-value is followed by a space token,
* if possible.
*
* NOTE: with {@link DataInput} source, not really feasible, up-front.
* If we did want, we could rearrange things to require space before
* next read, but initially let's just do nothing.
*/
private final void _verifyRootSpace() throws IOException
{
int ch = _nextByte;
if (ch <= INT_SPACE) {
_nextByte = -1;
if (ch == INT_CR || ch == INT_LF) {
++_currInputRow;
}
return;
}
_reportMissingRootWS(ch);
}
/*
/**********************************************************
/* Internal methods, secondary parsing
/**********************************************************
*/
protected final String _parseName(int i) throws IOException
{
if (i != INT_QUOTE) {
return _handleOddName(i);
}
// If so, can also unroll loops nicely
/* 25-Nov-2008, tatu: This may seem weird, but here we do
* NOT want to worry about UTF-8 decoding. Rather, we'll
* assume that part is ok (if not it will get caught
* later on), and just handle quotes and backslashes here.
*/
final int[] codes = _icLatin1;
int q = _inputData.readUnsignedByte();
if (codes[q] == 0) {
i = _inputData.readUnsignedByte();
if (codes[i] == 0) {
q = (q << 8) | i;
i = _inputData.readUnsignedByte();
if (codes[i] == 0) {
q = (q << 8) | i;
i = _inputData.readUnsignedByte();
if (codes[i] == 0) {
q = (q << 8) | i;
i = _inputData.readUnsignedByte();
if (codes[i] == 0) {
_quad1 = q;
return _parseMediumName(i);
}
if (i == INT_QUOTE) { // 4 byte/char case or broken
return findName(q, 4);
}
return parseName(q, i, 4);
}
if (i == INT_QUOTE) { // 3 byte/char case or broken
return findName(q, 3);
}
return parseName(q, i, 3);
}
if (i == INT_QUOTE) { // 2 byte/char case or broken
return findName(q, 2);
}
return parseName(q, i, 2);
}
if (i == INT_QUOTE) { // one byte/char case or broken
return findName(q, 1);
}
return parseName(q, i, 1);
}
if (q == INT_QUOTE) { // special case, ""
return "";
}
return parseName(0, q, 0); // quoting or invalid char
}
private final String _parseMediumName(int q2) throws IOException
{
final int[] codes = _icLatin1;
// Ok, got 5 name bytes so far
int i = _inputData.readUnsignedByte();
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 5 bytes
return findName(_quad1, q2, 1);
}
return parseName(_quad1, q2, i, 1); // quoting or invalid char
}
q2 = (q2 << 8) | i;
i = _inputData.readUnsignedByte();
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 6 bytes
return findName(_quad1, q2, 2);
}
return parseName(_quad1, q2, i, 2);
}
q2 = (q2 << 8) | i;
i = _inputData.readUnsignedByte();
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 7 bytes
return findName(_quad1, q2, 3);
}
return parseName(_quad1, q2, i, 3);
}
q2 = (q2 << 8) | i;
i = _inputData.readUnsignedByte();
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 8 bytes
return findName(_quad1, q2, 4);
}
return parseName(_quad1, q2, i, 4);
}
return _parseMediumName2(i, q2);
}
private final String _parseMediumName2(int q3, final int q2) throws IOException
{
final int[] codes = _icLatin1;
// Got 9 name bytes so far
int i = _inputData.readUnsignedByte();
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 9 bytes
return findName(_quad1, q2, q3, 1);
}
return parseName(_quad1, q2, q3, i, 1);
}
q3 = (q3 << 8) | i;
i = _inputData.readUnsignedByte();
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 10 bytes
return findName(_quad1, q2, q3, 2);
}
return parseName(_quad1, q2, q3, i, 2);
}
q3 = (q3 << 8) | i;
i = _inputData.readUnsignedByte();
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 11 bytes
return findName(_quad1, q2, q3, 3);
}
return parseName(_quad1, q2, q3, i, 3);
}
q3 = (q3 << 8) | i;
i = _inputData.readUnsignedByte();
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 12 bytes
return findName(_quad1, q2, q3, 4);
}
return parseName(_quad1, q2, q3, i, 4);
}
return _parseLongName(i, q2, q3);
}
private final String _parseLongName(int q, final int q2, int q3) throws IOException
{
_quadBuffer[0] = _quad1;
_quadBuffer[1] = q2;
_quadBuffer[2] = q3;
// As explained above, will ignore UTF-8 encoding at this point
final int[] codes = _icLatin1;
int qlen = 3;
while (true) {
int i = _inputData.readUnsignedByte();
if (codes[i] != 0) {
if (i == INT_QUOTE) {
return findName(_quadBuffer, qlen, q, 1);
}
return parseEscapedName(_quadBuffer, qlen, q, i, 1);
}
q = (q << 8) | i;
i = _inputData.readUnsignedByte();
if (codes[i] != 0) {
if (i == INT_QUOTE) {
return findName(_quadBuffer, qlen, q, 2);
}
return parseEscapedName(_quadBuffer, qlen, q, i, 2);
}
q = (q << 8) | i;
i = _inputData.readUnsignedByte();
if (codes[i] != 0) {
if (i == INT_QUOTE) {
return findName(_quadBuffer, qlen, q, 3);
}
return parseEscapedName(_quadBuffer, qlen, q, i, 3);
}
q = (q << 8) | i;
i = _inputData.readUnsignedByte();
if (codes[i] != 0) {
if (i == INT_QUOTE) {
return findName(_quadBuffer, qlen, q, 4);
}
return parseEscapedName(_quadBuffer, qlen, q, i, 4);
}
// Nope, no end in sight. Need to grow quad array etc
if (qlen >= _quadBuffer.length) {
_quadBuffer = _growArrayBy(_quadBuffer, qlen);
}
_quadBuffer[qlen++] = q;
q = i;
}
}
private final String parseName(int q1, int ch, int lastQuadBytes) throws IOException {
return parseEscapedName(_quadBuffer, 0, q1, ch, lastQuadBytes);
}
private final String parseName(int q1, int q2, int ch, int lastQuadBytes) throws IOException {
_quadBuffer[0] = q1;
return parseEscapedName(_quadBuffer, 1, q2, ch, lastQuadBytes);
}
private final String parseName(int q1, int q2, int q3, int ch, int lastQuadBytes) throws IOException {
_quadBuffer[0] = q1;
_quadBuffer[1] = q2;
return parseEscapedName(_quadBuffer, 2, q3, ch, lastQuadBytes);
}
/**
* Slower parsing method which is generally branched to when
* an escape sequence is detected (or alternatively for long
* names, one crossing input buffer boundary).
* Needs to be able to handle more exceptional cases, gets slower,
* and hance is offlined to a separate method.
*/
protected final String parseEscapedName(int[] quads, int qlen, int currQuad, int ch,
int currQuadBytes) throws IOException
{
/* 25-Nov-2008, tatu: This may seem weird, but here we do not want to worry about
* UTF-8 decoding yet. Rather, we'll assume that part is ok (if not it will get
* caught later on), and just handle quotes and backslashes here.
*/
final int[] codes = _icLatin1;
while (true) {
if (codes[ch] != 0) {
if (ch == INT_QUOTE) { // we are done
break;
}
// Unquoted white space?
if (ch != INT_BACKSLASH) {
// As per [JACKSON-208], call can now return:
_throwUnquotedSpace(ch, "name");
} else {
// Nope, escape sequence
ch = _decodeEscaped();
}
/* Oh crap. May need to UTF-8 (re-)encode it, if it's
* beyond 7-bit ascii. Gets pretty messy.
* If this happens often, may want to use different name
* canonicalization to avoid these hits.
*/
if (ch > 127) {
// Ok, we'll need room for first byte right away
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = _growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
if (ch < 0x800) { // 2-byte
currQuad = (currQuad << 8) | (0xc0 | (ch >> 6));
++currQuadBytes;
// Second byte gets output below:
} else { // 3 bytes; no need to worry about surrogates here
currQuad = (currQuad << 8) | (0xe0 | (ch >> 12));
++currQuadBytes;
// need room for middle byte?
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = _growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
currQuad = (currQuad << 8) | (0x80 | ((ch >> 6) & 0x3f));
++currQuadBytes;
}
// And same last byte in both cases, gets output below:
ch = 0x80 | (ch & 0x3f);
}
}
// Ok, we have one more byte to add at any rate:
if (currQuadBytes < 4) {
++currQuadBytes;
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = _growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
currQuadBytes = 1;
}
ch = _inputData.readUnsignedByte();
}
if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = _growArrayBy(quads, quads.length);
}
quads[qlen++] = pad(currQuad, currQuadBytes);
}
String name = _symbols.findName(quads, qlen);
if (name == null) {
name = addName(quads, qlen, currQuadBytes);
}
return name;
}
/**
* Method called when we see non-white space character other
* than double quote, when expecting a field name.
* In standard mode will just throw an exception; but
* in non-standard modes may be able to parse name.
*/
protected String _handleOddName(int ch) throws IOException
{
if (ch == '\'' && (_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) {
return _parseAposName();
}
if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) {
char c = (char) _decodeCharForError(ch);
_reportUnexpectedChar(c, "was expecting double-quote to start field name");
}
/* Also: note that although we use a different table here,
* it does NOT handle UTF-8 decoding. It'll just pass those
* high-bit codes as acceptable for later decoding.
*/
final int[] codes = CharTypes.getInputCodeUtf8JsNames();
// Also: must start with a valid character...
if (codes[ch] != 0) {
_reportUnexpectedChar(ch, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name");
}
/* Ok, now; instead of ultra-optimizing parsing here (as with
* regular JSON names), let's just use the generic "slow"
* variant. Can measure its impact later on if need be
*/
int[] quads = _quadBuffer;
int qlen = 0;
int currQuad = 0;
int currQuadBytes = 0;
while (true) {
// Ok, we have one more byte to add at any rate:
if (currQuadBytes < 4) {
++currQuadBytes;
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = _growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
currQuadBytes = 1;
}
ch = _inputData.readUnsignedByte();
if (codes[ch] != 0) {
break;
}
}
// Note: we must "push back" character read here for future consumption
_nextByte = ch;
if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = _growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
}
String name = _symbols.findName(quads, qlen);
if (name == null) {
name = addName(quads, qlen, currQuadBytes);
}
return name;
}
/* Parsing to allow optional use of non-standard single quotes.
* Plenty of duplicated code;
* main reason being to try to avoid slowing down fast path
* for valid JSON -- more alternatives, more code, generally
* bit slower execution.
*/
protected String _parseAposName() throws IOException
{
int ch = _inputData.readUnsignedByte();
if (ch == '\'') { // special case, ''
return "";
}
int[] quads = _quadBuffer;
int qlen = 0;
int currQuad = 0;
int currQuadBytes = 0;
// Copied from parseEscapedFieldName, with minor mods:
final int[] codes = _icLatin1;
while (true) {
if (ch == '\'') {
break;
}
// additional check to skip handling of double-quotes
if (ch != '"' && codes[ch] != 0) {
if (ch != '\\') {
// Unquoted white space?
// As per [JACKSON-208], call can now return:
_throwUnquotedSpace(ch, "name");
} else {
// Nope, escape sequence
ch = _decodeEscaped();
}
/* Oh crap. May need to UTF-8 (re-)encode it, if it's beyond
* 7-bit ASCII. Gets pretty messy. If this happens often, may want
* to use different name canonicalization to avoid these hits.
*/
if (ch > 127) {
// Ok, we'll need room for first byte right away
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = _growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
if (ch < 0x800) { // 2-byte
currQuad = (currQuad << 8) | (0xc0 | (ch >> 6));
++currQuadBytes;
// Second byte gets output below:
} else { // 3 bytes; no need to worry about surrogates here
currQuad = (currQuad << 8) | (0xe0 | (ch >> 12));
++currQuadBytes;
// need room for middle byte?
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = _growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
currQuad = (currQuad << 8) | (0x80 | ((ch >> 6) & 0x3f));
++currQuadBytes;
}
// And same last byte in both cases, gets output below:
ch = 0x80 | (ch & 0x3f);
}
}
// Ok, we have one more byte to add at any rate:
if (currQuadBytes < 4) {
++currQuadBytes;
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = _growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
currQuadBytes = 1;
}
ch = _inputData.readUnsignedByte();
}
if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = _growArrayBy(quads, quads.length);
}
quads[qlen++] = pad(currQuad, currQuadBytes);
}
String name = _symbols.findName(quads, qlen);
if (name == null) {
name = addName(quads, qlen, currQuadBytes);
}
return name;
}
/*
/**********************************************************
/* Internal methods, symbol (name) handling
/**********************************************************
*/
private final String findName(int q1, int lastQuadBytes) throws JsonParseException
{
q1 = pad(q1, lastQuadBytes);
// Usually we'll find it from the canonical symbol table already
String name = _symbols.findName(q1);
if (name != null) {
return name;
}
// If not, more work. We'll need add stuff to buffer
_quadBuffer[0] = q1;
return addName(_quadBuffer, 1, lastQuadBytes);
}
private final String findName(int q1, int q2, int lastQuadBytes) throws JsonParseException
{
q2 = pad(q2, lastQuadBytes);
// Usually we'll find it from the canonical symbol table already
String name = _symbols.findName(q1, q2);
if (name != null) {
return name;
}
// If not, more work. We'll need add stuff to buffer
_quadBuffer[0] = q1;
_quadBuffer[1] = q2;
return addName(_quadBuffer, 2, lastQuadBytes);
}
private final String findName(int q1, int q2, int q3, int lastQuadBytes) throws JsonParseException
{
q3 = pad(q3, lastQuadBytes);
String name = _symbols.findName(q1, q2, q3);
if (name != null) {
return name;
}
int[] quads = _quadBuffer;
quads[0] = q1;
quads[1] = q2;
quads[2] = pad(q3, lastQuadBytes);
return addName(quads, 3, lastQuadBytes);
}
private final String findName(int[] quads, int qlen, int lastQuad, int lastQuadBytes) throws JsonParseException
{
if (qlen >= quads.length) {
_quadBuffer = quads = _growArrayBy(quads, quads.length);
}
quads[qlen++] = pad(lastQuad, lastQuadBytes);
String name = _symbols.findName(quads, qlen);
if (name == null) {
return addName(quads, qlen, lastQuadBytes);
}
return name;
}
/**
* This is the main workhorse method used when we take a symbol
* table miss. It needs to demultiplex individual bytes, decode
* multi-byte chars (if any), and then construct Name instance
* and add it to the symbol table.
*/
private final String addName(int[] quads, int qlen, int lastQuadBytes) throws JsonParseException
{
/* Ok: must decode UTF-8 chars. No other validation is
* needed, since unescaping has been done earlier as necessary
* (as well as error reporting for unescaped control chars)
*/
// 4 bytes per quad, except last one maybe less
int byteLen = (qlen << 2) - 4 + lastQuadBytes;
/* And last one is not correctly aligned (leading zero bytes instead
* need to shift a bit, instead of trailing). Only need to shift it
* for UTF-8 decoding; need revert for storage (since key will not
* be aligned, to optimize lookup speed)
*/
int lastQuad;
if (lastQuadBytes < 4) {
lastQuad = quads[qlen-1];
// 8/16/24 bit left shift
quads[qlen-1] = (lastQuad << ((4 - lastQuadBytes) << 3));
} else {
lastQuad = 0;
}
// Need some working space, TextBuffer works well:
char[] cbuf = _textBuffer.emptyAndGetCurrentSegment();
int cix = 0;
for (int ix = 0; ix < byteLen; ) {
int ch = quads[ix >> 2]; // current quad, need to shift+mask
int byteIx = (ix & 3);
ch = (ch >> ((3 - byteIx) << 3)) & 0xFF;
++ix;
if (ch > 127) { // multi-byte
int needed;
if ((ch & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
ch &= 0x1F;
needed = 1;
} else if ((ch & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
ch &= 0x0F;
needed = 2;
} else if ((ch & 0xF8) == 0xF0) { // 4 bytes; double-char with surrogates and all...
ch &= 0x07;
needed = 3;
} else { // 5- and 6-byte chars not valid xml chars
_reportInvalidInitial(ch);
needed = ch = 1; // never really gets this far
}
if ((ix + needed) > byteLen) {
_reportInvalidEOF(" in field name", JsonToken.FIELD_NAME);
}
// Ok, always need at least one more:
int ch2 = quads[ix >> 2]; // current quad, need to shift+mask
byteIx = (ix & 3);
ch2 = (ch2 >> ((3 - byteIx) << 3));
++ix;
if ((ch2 & 0xC0) != 0x080) {
_reportInvalidOther(ch2);
}
ch = (ch << 6) | (ch2 & 0x3F);
if (needed > 1) {
ch2 = quads[ix >> 2];
byteIx = (ix & 3);
ch2 = (ch2 >> ((3 - byteIx) << 3));
++ix;
if ((ch2 & 0xC0) != 0x080) {
_reportInvalidOther(ch2);
}
ch = (ch << 6) | (ch2 & 0x3F);
if (needed > 2) { // 4 bytes? (need surrogates on output)
ch2 = quads[ix >> 2];
byteIx = (ix & 3);
ch2 = (ch2 >> ((3 - byteIx) << 3));
++ix;
if ((ch2 & 0xC0) != 0x080) {
_reportInvalidOther(ch2 & 0xFF);
}
ch = (ch << 6) | (ch2 & 0x3F);
}
}
if (needed > 2) { // surrogate pair? once again, let's output one here, one later on
ch -= 0x10000; // to normalize it starting with 0x0
if (cix >= cbuf.length) {
cbuf = _textBuffer.expandCurrentSegment();
}
cbuf[cix++] = (char) (0xD800 + (ch >> 10));
ch = 0xDC00 | (ch & 0x03FF);
}
}
if (cix >= cbuf.length) {
cbuf = _textBuffer.expandCurrentSegment();
}
cbuf[cix++] = (char) ch;
}
// Ok. Now we have the character array, and can construct the String
String baseName = new String(cbuf, 0, cix);
// And finally, un-align if necessary
if (lastQuadBytes < 4) {
quads[qlen-1] = lastQuad;
}
return _symbols.addName(baseName, quads, qlen);
}
/*
/**********************************************************
/* Internal methods, String value parsing
/**********************************************************
*/
@Override
protected void _finishString() throws IOException
{
int outPtr = 0;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
final int[] codes = _icUTF8;
final int outEnd = outBuf.length;
do {
int c = _inputData.readUnsignedByte();
if (codes[c] != 0) {
if (c == INT_QUOTE) {
_textBuffer.setCurrentLength(outPtr);
return;
}
_finishString2(outBuf, outPtr, c);
return;
}
outBuf[outPtr++] = (char) c;
} while (outPtr < outEnd);
_finishString2(outBuf, outPtr, _inputData.readUnsignedByte());
}
private String _finishAndReturnString() throws IOException
{
int outPtr = 0;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
final int[] codes = _icUTF8;
final int outEnd = outBuf.length;
do {
int c = _inputData.readUnsignedByte();
if (codes[c] != 0) {
if (c == INT_QUOTE) {
return _textBuffer.setCurrentAndReturn(outPtr);
}
_finishString2(outBuf, outPtr, c);
return _textBuffer.contentsAsString();
}
outBuf[outPtr++] = (char) c;
} while (outPtr < outEnd);
_finishString2(outBuf, outPtr, _inputData.readUnsignedByte());
return _textBuffer.contentsAsString();
}
private final void _finishString2(char[] outBuf, int outPtr, int c)
throws IOException
{
// Here we do want to do full decoding, hence:
final int[] codes = _icUTF8;
int outEnd = outBuf.length;
main_loop:
for (;; c = _inputData.readUnsignedByte()) {
// Then the tight ASCII non-funny-char loop:
while (codes[c] == 0) {
if (outPtr >= outEnd) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
outEnd = outBuf.length;
}
outBuf[outPtr++] = (char) c;
c = _inputData.readUnsignedByte();
}
// Ok: end marker, escape or multi-byte?
if (c == INT_QUOTE) {
break main_loop;
}
switch (codes[c]) {
case 1: // backslash
c = _decodeEscaped();
break;
case 2: // 2-byte UTF
c = _decodeUtf8_2(c);
break;
case 3: // 3-byte UTF
c = _decodeUtf8_3(c);
break;
case 4: // 4-byte UTF
c = _decodeUtf8_4(c);
// Let's add first part right away:
outBuf[outPtr++] = (char) (0xD800 | (c >> 10));
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
outEnd = outBuf.length;
}
c = 0xDC00 | (c & 0x3FF);
// And let the other char output down below
break;
default:
if (c < INT_SPACE) {
_throwUnquotedSpace(c, "string value");
} else {
// Is this good enough error message?
_reportInvalidChar(c);
}
}
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
outEnd = outBuf.length;
}
// Ok, let's add char to output:
outBuf[outPtr++] = (char) c;
}
_textBuffer.setCurrentLength(outPtr);
}
/**
* Method called to skim through rest of unparsed String value,
* if it is not needed. This can be done bit faster if contents
* need not be stored for future access.
*/
protected void _skipString() throws IOException
{
_tokenIncomplete = false;
// Need to be fully UTF-8 aware here:
final int[] codes = _icUTF8;
main_loop:
while (true) {
int c;
ascii_loop:
while (true) {
c = _inputData.readUnsignedByte();
if (codes[c] != 0) {
break ascii_loop;
}
}
// Ok: end marker, escape or multi-byte?
if (c == INT_QUOTE) {
break main_loop;
}
switch (codes[c]) {
case 1: // backslash
_decodeEscaped();
break;
case 2: // 2-byte UTF
_skipUtf8_2();
break;
case 3: // 3-byte UTF
_skipUtf8_3();
break;
case 4: // 4-byte UTF
_skipUtf8_4();
break;
default:
if (c < INT_SPACE) {
_throwUnquotedSpace(c, "string value");
} else {
// Is this good enough error message?
_reportInvalidChar(c);
}
}
}
}
/**
* Method for handling cases where first non-space character
* of an expected value token is not legal for standard JSON content.
*/
protected JsonToken _handleUnexpectedValue(int c)
throws IOException
{
// Most likely an error, unless we are to allow single-quote-strings
switch (c) {
case ']':
if (!_parsingContext.inArray()) {
break;
}
// fall through
case ',':
/* !!! TODO: 08-May-2016, tatu: To support `Feature.ALLOW_MISSING_VALUES` would
* need handling here...
*/
if ((_features & FEAT_MASK_ALLOW_MISSING) != 0) {
// _inputPtr--;
_nextByte = c;
return JsonToken.VALUE_NULL;
}
// fall through
case '}':
// Error: neither is valid at this point; valid closers have
// been handled earlier
_reportUnexpectedChar(c, "expected a value");
case '\'':
if ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) {
return _handleApos();
}
break;
case 'N':
_matchToken("NaN", 1);
if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) {
return resetAsNaN("NaN", Double.NaN);
}
_reportError("Non-standard token 'NaN': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
break;
case 'I':
_matchToken("Infinity", 1);
if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) {
return resetAsNaN("Infinity", Double.POSITIVE_INFINITY);
}
_reportError("Non-standard token 'Infinity': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
break;
case '+': // note: '-' is taken as number
return _handleInvalidNumberStart(_inputData.readUnsignedByte(), false);
}
// [core#77] Try to decode most likely token
if (Character.isJavaIdentifierStart(c)) {
_reportInvalidToken(c, ""+((char) c), _validJsonTokenList());
}
// but if it doesn't look like a token:
_reportUnexpectedChar(c, "expected a valid value "+_validJsonValueList());
return null;
}
protected JsonToken _handleApos() throws IOException
{
int c = 0;
// Otherwise almost verbatim copy of _finishString()
int outPtr = 0;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
// Here we do want to do full decoding, hence:
final int[] codes = _icUTF8;
main_loop:
while (true) {
// Then the tight ascii non-funny-char loop:
ascii_loop:
while (true) {
int outEnd = outBuf.length;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
outEnd = outBuf.length;
}
do {
c = _inputData.readUnsignedByte();
if (c == '\'') {
break main_loop;
}
if (codes[c] != 0) {
break ascii_loop;
}
outBuf[outPtr++] = (char) c;
} while (outPtr < outEnd);
}
switch (codes[c]) {
case 1: // backslash
c = _decodeEscaped();
break;
case 2: // 2-byte UTF
c = _decodeUtf8_2(c);
break;
case 3: // 3-byte UTF
c = _decodeUtf8_3(c);
break;
case 4: // 4-byte UTF
c = _decodeUtf8_4(c);
// Let's add first part right away:
outBuf[outPtr++] = (char) (0xD800 | (c >> 10));
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
c = 0xDC00 | (c & 0x3FF);
// And let the other char output down below
break;
default:
if (c < INT_SPACE) {
_throwUnquotedSpace(c, "string value");
}
// Is this good enough error message?
_reportInvalidChar(c);
}
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
// Ok, let's add char to output:
outBuf[outPtr++] = (char) c;
}
_textBuffer.setCurrentLength(outPtr);
return JsonToken.VALUE_STRING;
}
/**
* Method called if expected numeric value (due to leading sign) does not
* look like a number
*/
protected JsonToken _handleInvalidNumberStart(int ch, boolean neg)
throws IOException
{
while (ch == 'I') {
ch = _inputData.readUnsignedByte();
String match;
if (ch == 'N') {
match = neg ? "-INF" :"+INF";
} else if (ch == 'n') {
match = neg ? "-Infinity" :"+Infinity";
} else {
break;
}
_matchToken(match, 3);
if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) {
return resetAsNaN(match, neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
}
_reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
}
reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value");
return null;
}
protected final void _matchToken(String matchStr, int i) throws IOException
{
final int len = matchStr.length();
do {
int ch = _inputData.readUnsignedByte();
if (ch != matchStr.charAt(i)) {
_reportInvalidToken(ch, matchStr.substring(0, i));
}
} while (++i < len);
int ch = _inputData.readUnsignedByte();
if (ch >= '0' && ch != ']' && ch != '}') { // expected/allowed chars
_checkMatchEnd(matchStr, i, ch);
}
_nextByte = ch;
}
private final void _checkMatchEnd(String matchStr, int i, int ch) throws IOException {
// but actually only alphanums are problematic
char c = (char) _decodeCharForError(ch);
if (Character.isJavaIdentifierPart(c)) {
_reportInvalidToken(c, matchStr.substring(0, i));
}
}
/*
/**********************************************************
/* Internal methods, ws skipping, escape/unescape
/**********************************************************
*/
private final int _skipWS() throws IOException
{
int i = _nextByte;
if (i < 0) {
i = _inputData.readUnsignedByte();
} else {
_nextByte = -1;
}
while (true) {
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
return _skipWSComment(i);
}
return i;
} else {
// 06-May-2016, tatu: Could verify validity of WS, but for now why bother.
// ... but line number is useful thingy
if (i == INT_CR || i == INT_LF) {
++_currInputRow;
}
}
i = _inputData.readUnsignedByte();
}
}
/**
* Alternative to {@link #_skipWS} that handles possible {@link EOFException}
* caused by trying to read past the end of {@link InputData}.
*
* @since 2.9
*/
private final int _skipWSOrEnd() throws IOException
{
int i = _nextByte;
if (i < 0) {
try {
i = _inputData.readUnsignedByte();
} catch (EOFException e) {
return _eofAsNextChar();
}
} else {
_nextByte = -1;
}
while (true) {
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
return _skipWSComment(i);
}
return i;
} else {
// 06-May-2016, tatu: Could verify validity of WS, but for now why bother.
// ... but line number is useful thingy
if (i == INT_CR || i == INT_LF) {
++_currInputRow;
}
}
try {
i = _inputData.readUnsignedByte();
} catch (EOFException e) {
return _eofAsNextChar();
}
}
}
private final int _skipWSComment(int i) throws IOException
{
while (true) {
if (i > INT_SPACE) {
if (i == INT_SLASH) {
_skipComment();
} else if (i == INT_HASH) {
if (!_skipYAMLComment()) {
return i;
}
} else {
return i;
}
} else {
// 06-May-2016, tatu: Could verify validity of WS, but for now why bother.
// ... but line number is useful thingy
if (i == INT_CR || i == INT_LF) {
++_currInputRow;
}
/*
if ((i != INT_SPACE) && (i != INT_LF) && (i != INT_CR)) {
_throwInvalidSpace(i);
}
*/
}
i = _inputData.readUnsignedByte();
}
}
private final int _skipColon() throws IOException
{
int i = _nextByte;
if (i < 0) {
i = _inputData.readUnsignedByte();
} else {
_nextByte = -1;
}
// Fast path: colon with optional single-space/tab before and/or after:
if (i == INT_COLON) { // common case, no leading space
i = _inputData.readUnsignedByte();
if (i > INT_SPACE) { // nor trailing
if (i == INT_SLASH || i == INT_HASH) {
return _skipColon2(i, true);
}
return i;
}
if (i == INT_SPACE || i == INT_TAB) {
i = _inputData.readUnsignedByte();
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
return _skipColon2(i, true);
}
return i;
}
}
return _skipColon2(i, true); // true -> skipped colon
}
if (i == INT_SPACE || i == INT_TAB) {
i = _inputData.readUnsignedByte();
}
if (i == INT_COLON) {
i = _inputData.readUnsignedByte();
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
return _skipColon2(i, true);
}
return i;
}
if (i == INT_SPACE || i == INT_TAB) {
i = _inputData.readUnsignedByte();
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
return _skipColon2(i, true);
}
return i;
}
}
return _skipColon2(i, true);
}
return _skipColon2(i, false);
}
private final int _skipColon2(int i, boolean gotColon) throws IOException
{
for (;; i = _inputData.readUnsignedByte()) {
if (i > INT_SPACE) {
if (i == INT_SLASH) {
_skipComment();
continue;
}
if (i == INT_HASH) {
if (_skipYAMLComment()) {
continue;
}
}
if (gotColon) {
return i;
}
if (i != INT_COLON) {
_reportUnexpectedChar(i, "was expecting a colon to separate field name and value");
}
gotColon = true;
} else {
// 06-May-2016, tatu: Could verify validity of WS, but for now why bother.
// ... but line number is useful thingy
if (i == INT_CR || i == INT_LF) {
++_currInputRow;
}
}
}
}
private final void _skipComment() throws IOException
{
if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) {
_reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)");
}
int c = _inputData.readUnsignedByte();
if (c == '/') {
_skipLine();
} else if (c == '*') {
_skipCComment();
} else {
_reportUnexpectedChar(c, "was expecting either '*' or '/' for a comment");
}
}
private final void _skipCComment() throws IOException
{
// Need to be UTF-8 aware here to decode content (for skipping)
final int[] codes = CharTypes.getInputCodeComment();
int i = _inputData.readUnsignedByte();
// Ok: need the matching '*/'
main_loop:
while (true) {
int code = codes[i];
if (code != 0) {
switch (code) {
case '*':
i = _inputData.readUnsignedByte();
if (i == INT_SLASH) {
return;
}
continue main_loop;
case INT_LF:
case INT_CR:
++_currInputRow;
break;
case 2: // 2-byte UTF
_skipUtf8_2();
break;
case 3: // 3-byte UTF
_skipUtf8_3();
break;
case 4: // 4-byte UTF
_skipUtf8_4();
break;
default: // e.g. -1
// Is this good enough error message?
_reportInvalidChar(i);
}
}
i = _inputData.readUnsignedByte();
}
}
private final boolean _skipYAMLComment() throws IOException
{
if ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) == 0) {
return false;
}
_skipLine();
return true;
}
/**
* Method for skipping contents of an input line; usually for CPP
* and YAML style comments.
*/
private final void _skipLine() throws IOException
{
// Ok: need to find EOF or linefeed
final int[] codes = CharTypes.getInputCodeComment();
while (true) {
int i = _inputData.readUnsignedByte();
int code = codes[i];
if (code != 0) {
switch (code) {
case INT_LF:
case INT_CR:
++_currInputRow;
return;
case '*': // nop for these comments
break;
case 2: // 2-byte UTF
_skipUtf8_2();
break;
case 3: // 3-byte UTF
_skipUtf8_3();
break;
case 4: // 4-byte UTF
_skipUtf8_4();
break;
default: // e.g. -1
if (code < 0) {
// Is this good enough error message?
_reportInvalidChar(i);
}
}
}
}
}
@Override
protected char _decodeEscaped() throws IOException
{
int c = _inputData.readUnsignedByte();
switch (c) {
// First, ones that are mapped
case 'b':
return '\b';
case 't':
return '\t';
case 'n':
return '\n';
case 'f':
return '\f';
case 'r':
return '\r';
// And these are to be returned as they are
case '"':
case '/':
case '\\':
return (char) c;
case 'u': // and finally hex-escaped
break;
default:
return _handleUnrecognizedCharacterEscape((char) _decodeCharForError(c));
}
// Ok, a hex escape. Need 4 characters
int value = 0;
for (int i = 0; i < 4; ++i) {
int ch = _inputData.readUnsignedByte();
int digit = CharTypes.charToHex(ch);
if (digit < 0) {
_reportUnexpectedChar(ch, "expected a hex-digit for character escape sequence");
}
value = (value << 4) | digit;
}
return (char) value;
}
protected int _decodeCharForError(int firstByte) throws IOException
{
int c = firstByte & 0xFF;
if (c > 0x7F) { // if >= 0, is ascii and fine as is
int needed;
// Ok; if we end here, we got multi-byte combination
if ((c & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
c &= 0x1F;
needed = 1;
} else if ((c & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
c &= 0x0F;
needed = 2;
} else if ((c & 0xF8) == 0xF0) {
// 4 bytes; double-char with surrogates and all...
c &= 0x07;
needed = 3;
} else {
_reportInvalidInitial(c & 0xFF);
needed = 1; // never gets here
}
int d = _inputData.readUnsignedByte();
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
c = (c << 6) | (d & 0x3F);
if (needed > 1) { // needed == 1 means 2 bytes total
d = _inputData.readUnsignedByte(); // 3rd byte
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
c = (c << 6) | (d & 0x3F);
if (needed > 2) { // 4 bytes? (need surrogates)
d = _inputData.readUnsignedByte();
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
c = (c << 6) | (d & 0x3F);
}
}
}
return c;
}
/*
/**********************************************************
/* Internal methods,UTF8 decoding
/**********************************************************
*/
private final int _decodeUtf8_2(int c) throws IOException
{
int d = _inputData.readUnsignedByte();
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
return ((c & 0x1F) << 6) | (d & 0x3F);
}
private final int _decodeUtf8_3(int c1) throws IOException
{
c1 &= 0x0F;
int d = _inputData.readUnsignedByte();
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
int c = (c1 << 6) | (d & 0x3F);
d = _inputData.readUnsignedByte();
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
c = (c << 6) | (d & 0x3F);
return c;
}
/**
* @return Character value minus 0x10000; this so that caller
* can readily expand it to actual surrogates
*/
private final int _decodeUtf8_4(int c) throws IOException
{
int d = _inputData.readUnsignedByte();
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
c = ((c & 0x07) << 6) | (d & 0x3F);
d = _inputData.readUnsignedByte();
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
c = (c << 6) | (d & 0x3F);
d = _inputData.readUnsignedByte();
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
/* note: won't change it to negative here, since caller
* already knows it'll need a surrogate
*/
return ((c << 6) | (d & 0x3F)) - 0x10000;
}
private final void _skipUtf8_2() throws IOException
{
int c = _inputData.readUnsignedByte();
if ((c & 0xC0) != 0x080) {
_reportInvalidOther(c & 0xFF);
}
}
/* Alas, can't heavily optimize skipping, since we still have to
* do validity checks...
*/
private final void _skipUtf8_3() throws IOException
{
//c &= 0x0F;
int c = _inputData.readUnsignedByte();
if ((c & 0xC0) != 0x080) {
_reportInvalidOther(c & 0xFF);
}
c = _inputData.readUnsignedByte();
if ((c & 0xC0) != 0x080) {
_reportInvalidOther(c & 0xFF);
}
}
private final void _skipUtf8_4() throws IOException
{
int d = _inputData.readUnsignedByte();
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
d = _inputData.readUnsignedByte();
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
d = _inputData.readUnsignedByte();
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
}
/*
/**********************************************************
/* Internal methods, error reporting
/**********************************************************
*/
protected void _reportInvalidToken(int ch, String matchedPart) throws IOException
{
_reportInvalidToken(ch, matchedPart, _validJsonTokenList());
}
protected void _reportInvalidToken(int ch, String matchedPart, String msg)
throws IOException
{
StringBuilder sb = new StringBuilder(matchedPart);
/* Let's just try to find what appears to be the token, using
* regular Java identifier character rules. It's just a heuristic,
* nothing fancy here (nor fast).
*/
while (true) {
char c = (char) _decodeCharForError(ch);
if (!Character.isJavaIdentifierPart(c)) {
break;
}
sb.append(c);
ch = _inputData.readUnsignedByte();
}
_reportError("Unrecognized token '"+sb.toString()+"': was expecting "+msg);
}
protected void _reportInvalidChar(int c)
throws JsonParseException
{
// Either invalid WS or illegal UTF-8 start char
if (c < INT_SPACE) {
_throwInvalidSpace(c);
}
_reportInvalidInitial(c);
}
protected void _reportInvalidInitial(int mask)
throws JsonParseException
{
_reportError("Invalid UTF-8 start byte 0x"+Integer.toHexString(mask));
}
private void _reportInvalidOther(int mask)
throws JsonParseException
{
_reportError("Invalid UTF-8 middle byte 0x"+Integer.toHexString(mask));
}
private static int[] _growArrayBy(int[] arr, int more)
{
if (arr == null) {
return new int[more];
}
return Arrays.copyOf(arr, arr.length + more);
}
/*
/**********************************************************
/* Internal methods, binary access
/**********************************************************
*/
/**
* Efficient handling for incremental parsing of base64-encoded
* textual content.
*/
@SuppressWarnings("resource")
protected final byte[] _decodeBase64(Base64Variant b64variant) throws IOException
{
ByteArrayBuilder builder = _getByteArrayBuilder();
//main_loop:
while (true) {
// first, we'll skip preceding white space, if any
int ch;
do {
ch = _inputData.readUnsignedByte();
} while (ch <= INT_SPACE);
int bits = b64variant.decodeBase64Char(ch);
if (bits < 0) { // reached the end, fair and square?
if (ch == INT_QUOTE) {
return builder.toByteArray();
}
bits = _decodeBase64Escape(b64variant, ch, 0);
if (bits < 0) { // white space to skip
continue;
}
}
int decodedData = bits;
// then second base64 char; can't get padding yet, nor ws
ch = _inputData.readUnsignedByte();
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
bits = _decodeBase64Escape(b64variant, ch, 1);
}
decodedData = (decodedData << 6) | bits;
// third base64 char; can be padding, but not ws
ch = _inputData.readUnsignedByte();
bits = b64variant.decodeBase64Char(ch);
// First branch: can get padding (-> 1 byte)
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// could also just be 'missing' padding
if (ch == INT_QUOTE) {
decodedData >>= 4;
builder.append(decodedData);
if (b64variant.usesPadding()) {
_handleBase64MissingPadding(b64variant);
}
return builder.toByteArray();
}
bits = _decodeBase64Escape(b64variant, ch, 2);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
ch = _inputData.readUnsignedByte();
if (!b64variant.usesPaddingChar(ch)) {
if ((ch != INT_BACKSLASH)
|| _decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) {
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
}
}
// Got 12 bits, only need 8, need to shift
decodedData >>= 4;
builder.append(decodedData);
continue;
}
}
// Nope, 2 or 3 bytes
decodedData = (decodedData << 6) | bits;
// fourth and last base64 char; can be padding, but not ws
ch = _inputData.readUnsignedByte();
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// could also just be 'missing' padding
if (ch == INT_QUOTE) {
decodedData >>= 2;
builder.appendTwoBytes(decodedData);
if (b64variant.usesPadding()) {
_handleBase64MissingPadding(b64variant);
}
return builder.toByteArray();
}
bits = _decodeBase64Escape(b64variant, ch, 3);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
/* With padding we only get 2 bytes; but we have
* to shift it a bit so it is identical to triplet
* case with partial output.
* 3 chars gives 3x6 == 18 bits, of which 2 are
* dummies, need to discard:
*/
decodedData >>= 2;
builder.appendTwoBytes(decodedData);
continue;
}
}
// otherwise, our triplet is now complete
decodedData = (decodedData << 6) | bits;
builder.appendThreeBytes(decodedData);
}
}
/*
/**********************************************************
/* Improved location updating (refactored in 2.7)
/**********************************************************
*/
@Override
public JsonLocation getTokenLocation() {
return new JsonLocation(_getSourceReference(), -1L, -1L, _tokenInputRow, -1);
}
@Override
public JsonLocation getCurrentLocation() {
return new JsonLocation(_getSourceReference(), -1L, -1L, _currInputRow, -1);
}
/*
/**********************************************************
/* Internal methods, other
/**********************************************************
*/
private void _closeScope(int i) throws JsonParseException {
if (i == INT_RBRACKET) {
if (!_parsingContext.inArray()) {
_reportMismatchedEndMarker(i, '}');
}
_parsingContext = _parsingContext.clearAndGetParent();
_currToken = JsonToken.END_ARRAY;
}
if (i == INT_RCURLY) {
if (!_parsingContext.inObject()) {
_reportMismatchedEndMarker(i, ']');
}
_parsingContext = _parsingContext.clearAndGetParent();
_currToken = JsonToken.END_OBJECT;
}
}
/**
* Helper method needed to fix [Issue#148], masking of 0x00 character
*/
private final static int pad(int q, int bytes) {
return (bytes == 4) ? q : (q | (-1 << (bytes << 3)));
}
}
UTF8JsonGenerator.java 0000664 0000000 0000000 00000224336 13561642473 0033335 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json package com.fasterxml.jackson.core.json;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.io.CharTypes;
import com.fasterxml.jackson.core.io.CharacterEscapes;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.io.NumberOutput;
public class UTF8JsonGenerator
extends JsonGeneratorImpl
{
private final static byte BYTE_u = (byte) 'u';
private final static byte BYTE_0 = (byte) '0';
private final static byte BYTE_LBRACKET = (byte) '[';
private final static byte BYTE_RBRACKET = (byte) ']';
private final static byte BYTE_LCURLY = (byte) '{';
private final static byte BYTE_RCURLY = (byte) '}';
private final static byte BYTE_BACKSLASH = (byte) '\\';
private final static byte BYTE_COMMA = (byte) ',';
private final static byte BYTE_COLON = (byte) ':';
// intermediate copies only made up to certain length...
private final static int MAX_BYTES_TO_BUFFER = 512;
private final static byte[] HEX_CHARS = CharTypes.copyHexBytes();
private final static byte[] NULL_BYTES = { 'n', 'u', 'l', 'l' };
private final static byte[] TRUE_BYTES = { 't', 'r', 'u', 'e' };
private final static byte[] FALSE_BYTES = { 'f', 'a', 'l', 's', 'e' };
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Underlying output stream used for writing JSON content.
*/
final protected OutputStream _outputStream;
/**
* Character used for quoting JSON Object property names
* and String values.
*
* @since 2.8
*/
protected byte _quoteChar;
/*
/**********************************************************
/* Output buffering
/**********************************************************
*/
/**
* Intermediate buffer in which contents are buffered before
* being written using {@link #_outputStream}.
*/
protected byte[] _outputBuffer;
/**
* Pointer to the position right beyond the last character to output
* (end marker; may be past the buffer)
*/
protected int _outputTail;
/**
* End marker of the output buffer; one past the last valid position
* within the buffer.
*/
protected final int _outputEnd;
/**
* Maximum number of
* If it is not, it also means that parser can NOT modify underlying
* buffer.
*/
protected boolean _bufferRecyclable;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
/**
* @deprecated Since 2.10
*/
@Deprecated
public UTF8StreamJsonParser(IOContext ctxt, int features, InputStream in,
ObjectCodec codec, ByteQuadsCanonicalizer sym,
byte[] inputBuffer, int start, int end,
boolean bufferRecyclable)
{
this(ctxt, features, in, codec, sym,
inputBuffer, start, end, 0, bufferRecyclable);
}
public UTF8StreamJsonParser(IOContext ctxt, int features, InputStream in,
ObjectCodec codec, ByteQuadsCanonicalizer sym,
byte[] inputBuffer, int start, int end, int bytesPreProcessed,
boolean bufferRecyclable)
{
super(ctxt, features);
_inputStream = in;
_objectCodec = codec;
_symbols = sym;
_inputBuffer = inputBuffer;
_inputPtr = start;
_inputEnd = end;
_currInputRowStart = start - bytesPreProcessed;
// If we have offset, need to omit that from byte offset, so:
_currInputProcessed = -start + bytesPreProcessed;
_bufferRecyclable = bufferRecyclable;
}
@Override
public ObjectCodec getCodec() {
return _objectCodec;
}
@Override
public void setCodec(ObjectCodec c) {
_objectCodec = c;
}
/*
/**********************************************************
/* Overrides for life-cycle
/**********************************************************
*/
@Override
public int releaseBuffered(OutputStream out) throws IOException
{
int count = _inputEnd - _inputPtr;
if (count < 1) {
return 0;
}
// let's just advance ptr to end
int origPtr = _inputPtr;
out.write(_inputBuffer, origPtr, count);
return count;
}
@Override
public Object getInputSource() {
return _inputStream;
}
/*
/**********************************************************
/* Overrides, low-level reading
/**********************************************************
*/
protected final boolean _loadMore() throws IOException
{
final int bufSize = _inputEnd;
if (_inputStream != null) {
int space = _inputBuffer.length;
if (space == 0) { // only occurs when we've been closed
return false;
}
int count = _inputStream.read(_inputBuffer, 0, space);
if (count > 0) {
_inputPtr = 0;
_inputEnd = count;
_currInputProcessed += _inputEnd;
_currInputRowStart -= _inputEnd;
// 26-Nov-2015, tatu: Since name-offset requires it too, must offset
// this increase to avoid "moving" name-offset, resulting most likely
// in negative value, which is fine as combine value remains unchanged.
_nameStartOffset -= bufSize;
return true;
}
// End of input
_closeInput();
// Should never return 0, so let's fail
if (count == 0) {
throw new IOException("InputStream.read() returned 0 characters when trying to read "+_inputBuffer.length+" bytes");
}
}
return false;
}
@Override
protected void _closeInput() throws IOException
{
// We are not to call close() on the underlying InputStream
// unless we "own" it, or auto-closing feature is enabled.
if (_inputStream != null) {
if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_SOURCE)) {
_inputStream.close();
}
_inputStream = null;
}
}
/**
* Method called to release internal buffers owned by the base
* reader. This may be called along with {@link #_closeInput} (for
* example, when explicitly closing this reader instance), or
* separately (if need be).
*/
@Override
protected void _releaseBuffers() throws IOException
{
super._releaseBuffers();
// Merge found symbols, if any:
_symbols.release();
if (_bufferRecyclable) {
byte[] buf = _inputBuffer;
if (buf != null) {
// Let's not set it to null; this way should get slightly more meaningful
// error messages in case someone closes parser indirectly, without realizing.
_inputBuffer = NO_BYTES;
_ioContext.releaseReadIOBuffer(buf);
}
}
}
/*
/**********************************************************
/* Public API, data access
/**********************************************************
*/
@Override
public String getText() throws IOException
{
if (_currToken == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
return _finishAndReturnString(); // only strings can be incomplete
}
return _textBuffer.contentsAsString();
}
return _getText2(_currToken);
}
@Override // since 2.8
public int getText(Writer writer) throws IOException
{
JsonToken t = _currToken;
if (t == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
return _textBuffer.contentsToWriter(writer);
}
if (t == JsonToken.FIELD_NAME) {
String n = _parsingContext.getCurrentName();
writer.write(n);
return n.length();
}
if (t != null) {
if (t.isNumeric()) {
return _textBuffer.contentsToWriter(writer);
}
char[] ch = t.asCharArray();
writer.write(ch);
return ch.length;
}
return 0;
}
// // // Let's override default impls for improved performance
// @since 2.1
@Override
public String getValueAsString() throws IOException
{
if (_currToken == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
return _finishAndReturnString(); // only strings can be incomplete
}
return _textBuffer.contentsAsString();
}
if (_currToken == JsonToken.FIELD_NAME) {
return getCurrentName();
}
return super.getValueAsString(null);
}
// @since 2.1
@Override
public String getValueAsString(String defValue) throws IOException
{
if (_currToken == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
return _finishAndReturnString(); // only strings can be incomplete
}
return _textBuffer.contentsAsString();
}
if (_currToken == JsonToken.FIELD_NAME) {
return getCurrentName();
}
return super.getValueAsString(defValue);
}
// since 2.6
@Override
public int getValueAsInt() throws IOException
{
JsonToken t = _currToken;
if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) {
// inlined 'getIntValue()'
if ((_numTypesValid & NR_INT) == 0) {
if (_numTypesValid == NR_UNKNOWN) {
return _parseIntValue();
}
if ((_numTypesValid & NR_INT) == 0) {
convertNumberToInt();
}
}
return _numberInt;
}
return super.getValueAsInt(0);
}
// since 2.6
@Override
public int getValueAsInt(int defValue) throws IOException
{
JsonToken t = _currToken;
if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) {
// inlined 'getIntValue()'
if ((_numTypesValid & NR_INT) == 0) {
if (_numTypesValid == NR_UNKNOWN) {
return _parseIntValue();
}
if ((_numTypesValid & NR_INT) == 0) {
convertNumberToInt();
}
}
return _numberInt;
}
return super.getValueAsInt(defValue);
}
protected final String _getText2(JsonToken t)
{
if (t == null) {
return null;
}
switch (t.id()) {
case ID_FIELD_NAME:
return _parsingContext.getCurrentName();
case ID_STRING:
// fall through
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return _textBuffer.contentsAsString();
default:
return t.asString();
}
}
@Override
public char[] getTextCharacters() throws IOException
{
if (_currToken != null) { // null only before/after document
switch (_currToken.id()) {
case ID_FIELD_NAME:
if (!_nameCopied) {
String name = _parsingContext.getCurrentName();
int nameLen = name.length();
if (_nameCopyBuffer == null) {
_nameCopyBuffer = _ioContext.allocNameCopyBuffer(nameLen);
} else if (_nameCopyBuffer.length < nameLen) {
_nameCopyBuffer = new char[nameLen];
}
name.getChars(0, nameLen, _nameCopyBuffer, 0);
_nameCopied = true;
}
return _nameCopyBuffer;
case ID_STRING:
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
// fall through
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return _textBuffer.getTextBuffer();
default:
return _currToken.asCharArray();
}
}
return null;
}
@Override
public int getTextLength() throws IOException
{
if (_currToken != null) { // null only before/after document
switch (_currToken.id()) {
case ID_FIELD_NAME:
return _parsingContext.getCurrentName().length();
case ID_STRING:
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
// fall through
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return _textBuffer.size();
default:
return _currToken.asCharArray().length;
}
}
return 0;
}
@Override
public int getTextOffset() throws IOException
{
// Most have offset of 0, only some may have other values:
if (_currToken != null) {
switch (_currToken.id()) {
case ID_FIELD_NAME:
return 0;
case ID_STRING:
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
// fall through
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return _textBuffer.getTextOffset();
default:
}
}
return 0;
}
@Override
public byte[] getBinaryValue(Base64Variant b64variant) throws IOException
{
if (_currToken != JsonToken.VALUE_STRING &&
(_currToken != JsonToken.VALUE_EMBEDDED_OBJECT || _binaryValue == null)) {
_reportError("Current token ("+_currToken+") not VALUE_STRING or VALUE_EMBEDDED_OBJECT, can not access as binary");
}
// To ensure that we won't see inconsistent data, better clear up state...
if (_tokenIncomplete) {
try {
_binaryValue = _decodeBase64(b64variant);
} catch (IllegalArgumentException iae) {
throw _constructError("Failed to decode VALUE_STRING as base64 ("+b64variant+"): "+iae.getMessage());
}
// let's clear incomplete only now; allows for accessing other textual content in error cases
_tokenIncomplete = false;
} else { // may actually require conversion...
if (_binaryValue == null) {
@SuppressWarnings("resource")
ByteArrayBuilder builder = _getByteArrayBuilder();
_decodeBase64(getText(), builder, b64variant);
_binaryValue = builder.toByteArray();
}
}
return _binaryValue;
}
@Override
public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException
{
// if we have already read the token, just use whatever we may have
if (!_tokenIncomplete || _currToken != JsonToken.VALUE_STRING) {
byte[] b = getBinaryValue(b64variant);
out.write(b);
return b.length;
}
// otherwise do "real" incremental parsing...
byte[] buf = _ioContext.allocBase64Buffer();
try {
return _readBinary(b64variant, out, buf);
} finally {
_ioContext.releaseBase64Buffer(buf);
}
}
protected int _readBinary(Base64Variant b64variant, OutputStream out,
byte[] buffer) throws IOException
{
int outputPtr = 0;
final int outputEnd = buffer.length - 3;
int outputCount = 0;
while (true) {
// first, we'll skip preceding white space, if any
int ch;
do {
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = (int) _inputBuffer[_inputPtr++] & 0xFF;
} while (ch <= INT_SPACE);
int bits = b64variant.decodeBase64Char(ch);
if (bits < 0) { // reached the end, fair and square?
if (ch == INT_QUOTE) {
break;
}
bits = _decodeBase64Escape(b64variant, ch, 0);
if (bits < 0) { // white space to skip
continue;
}
}
// enough room? If not, flush
if (outputPtr > outputEnd) {
outputCount += outputPtr;
out.write(buffer, 0, outputPtr);
outputPtr = 0;
}
int decodedData = bits;
// then second base64 char; can't get padding yet, nor ws
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
bits = _decodeBase64Escape(b64variant, ch, 1);
}
decodedData = (decodedData << 6) | bits;
// third base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
bits = b64variant.decodeBase64Char(ch);
// First branch: can get padding (-> 1 byte)
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// as per [JACKSON-631], could also just be 'missing' padding
if (ch == INT_QUOTE) {
decodedData >>= 4;
buffer[outputPtr++] = (byte) decodedData;
if (b64variant.usesPadding()) {
--_inputPtr; // to keep parser state bit more consistent
_handleBase64MissingPadding(b64variant);
}
break;
}
bits = _decodeBase64Escape(b64variant, ch, 2);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
// Ok, must get padding
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
if (!b64variant.usesPaddingChar(ch)) {
if (_decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) {
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
}
}
// Got 12 bits, only need 8, need to shift
decodedData >>= 4;
buffer[outputPtr++] = (byte) decodedData;
continue;
}
}
// Nope, 2 or 3 bytes
decodedData = (decodedData << 6) | bits;
// fourth and last base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// as per [JACKSON-631], could also just be 'missing' padding
if (ch == INT_QUOTE) {
decodedData >>= 2;
buffer[outputPtr++] = (byte) (decodedData >> 8);
buffer[outputPtr++] = (byte) decodedData;
if (b64variant.usesPadding()) {
--_inputPtr; // to keep parser state bit more consistent
_handleBase64MissingPadding(b64variant);
}
break;
}
bits = _decodeBase64Escape(b64variant, ch, 3);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
/* With padding we only get 2 bytes; but we have
* to shift it a bit so it is identical to triplet
* case with partial output.
* 3 chars gives 3x6 == 18 bits, of which 2 are
* dummies, need to discard:
*/
decodedData >>= 2;
buffer[outputPtr++] = (byte) (decodedData >> 8);
buffer[outputPtr++] = (byte) decodedData;
continue;
}
}
// otherwise, our triplet is now complete
decodedData = (decodedData << 6) | bits;
buffer[outputPtr++] = (byte) (decodedData >> 16);
buffer[outputPtr++] = (byte) (decodedData >> 8);
buffer[outputPtr++] = (byte) decodedData;
}
_tokenIncomplete = false;
if (outputPtr > 0) {
outputCount += outputPtr;
out.write(buffer, 0, outputPtr);
}
return outputCount;
}
/*
/**********************************************************
/* Public API, traversal, basic
/**********************************************************
*/
/**
* @return Next token from the stream, if any found, or null
* to indicate end-of-input
*/
@Override
public JsonToken nextToken() throws IOException
{
/* First: field names are special -- we will always tokenize
* (part of) value along with field name to simplify
* state handling. If so, can and need to use secondary token:
*/
if (_currToken == JsonToken.FIELD_NAME) {
return _nextAfterName();
}
// But if we didn't already have a name, and (partially?) decode number,
// need to ensure no numeric information is leaked
_numTypesValid = NR_UNKNOWN;
if (_tokenIncomplete) {
_skipString(); // only strings can be partial
}
int i = _skipWSOrEnd();
if (i < 0) { // end-of-input
// Close/release things like input source, symbol table and recyclable buffers
close();
return (_currToken = null);
}
// clear any data retained so far
_binaryValue = null;
// Closing scope?
if (i == INT_RBRACKET) {
_closeArrayScope();
return (_currToken = JsonToken.END_ARRAY);
}
if (i == INT_RCURLY) {
_closeObjectScope();
return (_currToken = JsonToken.END_OBJECT);
}
// Nope: do we then expect a comma?
if (_parsingContext.expectComma()) {
if (i != INT_COMMA) {
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
i = _skipWS();
// Was that a trailing comma?
if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
if ((i == INT_RBRACKET) || (i == INT_RCURLY)) {
return _closeScope(i);
}
}
}
/* And should we now have a name? Always true for Object contexts
* since the intermediate 'expect-value' state is never retained.
*/
if (!_parsingContext.inObject()) {
_updateLocation();
return _nextTokenNotInObject(i);
}
// So first parse the field name itself:
_updateNameLocation();
String n = _parseName(i);
_parsingContext.setCurrentName(n);
_currToken = JsonToken.FIELD_NAME;
i = _skipColon();
_updateLocation();
// Ok: we must have a value... what is it? Strings are very common, check first:
if (i == INT_QUOTE) {
_tokenIncomplete = true;
_nextToken = JsonToken.VALUE_STRING;
return _currToken;
}
JsonToken t;
switch (i) {
case '-':
t = _parseNegNumber();
break;
// Should we have separate handling for plus? Although it is not allowed per se,
// it may be erroneously used, and could be indicate by a more specific error message.
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
t = _parsePosNumber(i);
break;
case 'f':
_matchFalse();
t = JsonToken.VALUE_FALSE;
break;
case 'n':
_matchNull();
t = JsonToken.VALUE_NULL;
break;
case 't':
_matchTrue();
t = JsonToken.VALUE_TRUE;
break;
case '[':
t = JsonToken.START_ARRAY;
break;
case '{':
t = JsonToken.START_OBJECT;
break;
default:
t = _handleUnexpectedValue(i);
}
_nextToken = t;
return _currToken;
}
private final JsonToken _nextTokenNotInObject(int i) throws IOException
{
if (i == INT_QUOTE) {
_tokenIncomplete = true;
return (_currToken = JsonToken.VALUE_STRING);
}
switch (i) {
case '[':
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
return (_currToken = JsonToken.START_ARRAY);
case '{':
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
return (_currToken = JsonToken.START_OBJECT);
case 't':
_matchTrue();
return (_currToken = JsonToken.VALUE_TRUE);
case 'f':
_matchFalse();
return (_currToken = JsonToken.VALUE_FALSE);
case 'n':
_matchNull();
return (_currToken = JsonToken.VALUE_NULL);
case '-':
return (_currToken = _parseNegNumber());
// Should we have separate handling for plus? Although it is not allowed per se,
// it may be erroneously used, and could be indicate by a more specific error message.
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return (_currToken = _parsePosNumber(i));
}
return (_currToken = _handleUnexpectedValue(i));
}
private final JsonToken _nextAfterName()
{
_nameCopied = false; // need to invalidate if it was copied
JsonToken t = _nextToken;
_nextToken = null;
// !!! 16-Nov-2015, tatu: TODO: fix [databind#37], copy next location to current here
// Also: may need to start new context?
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return (_currToken = t);
}
@Override
public void finishToken() throws IOException {
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
}
/*
/**********************************************************
/* Public API, traversal, nextXxxValue/nextFieldName
/**********************************************************
*/
@Override
public boolean nextFieldName(SerializableString str) throws IOException
{
// // // Note: most of code below is copied from nextToken()
_numTypesValid = NR_UNKNOWN;
if (_currToken == JsonToken.FIELD_NAME) { // can't have name right after name
_nextAfterName();
return false;
}
if (_tokenIncomplete) {
_skipString();
}
int i = _skipWSOrEnd();
if (i < 0) { // end-of-input
close();
_currToken = null;
return false;
}
_binaryValue = null;
// Closing scope?
if (i == INT_RBRACKET) {
_closeArrayScope();
_currToken = JsonToken.END_ARRAY;
return false;
}
if (i == INT_RCURLY) {
_closeObjectScope();
_currToken = JsonToken.END_OBJECT;
return false;
}
// Nope: do we then expect a comma?
if (_parsingContext.expectComma()) {
if (i != INT_COMMA) {
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
i = _skipWS();
// Was that a trailing comma?
if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
if ((i == INT_RBRACKET) || (i == INT_RCURLY)) {
_closeScope(i);
return false;
}
}
}
if (!_parsingContext.inObject()) {
_updateLocation();
_nextTokenNotInObject(i);
return false;
}
// // // This part differs, name parsing
_updateNameLocation();
if (i == INT_QUOTE) {
// when doing literal match, must consider escaping:
byte[] nameBytes = str.asQuotedUTF8();
final int len = nameBytes.length;
// 22-May-2014, tatu: Actually, let's require 4 more bytes for faster skipping
// of colon that follows name
if ((_inputPtr + len + 4) < _inputEnd) { // maybe...
// first check length match by
final int end = _inputPtr+len;
if (_inputBuffer[end] == INT_QUOTE) {
int offset = 0;
int ptr = _inputPtr;
while (true) {
if (ptr == end) { // yes, match!
_parsingContext.setCurrentName(str.getValue());
i = _skipColonFast(ptr+1);
_isNextTokenNameYes(i);
return true;
}
if (nameBytes[offset] != _inputBuffer[ptr]) {
break;
}
++offset;
++ptr;
}
}
}
}
return _isNextTokenNameMaybe(i, str);
}
@Override
public String nextFieldName() throws IOException
{
// // // Note: this is almost a verbatim copy of nextToken()
_numTypesValid = NR_UNKNOWN;
if (_currToken == JsonToken.FIELD_NAME) {
_nextAfterName();
return null;
}
if (_tokenIncomplete) {
_skipString();
}
int i = _skipWSOrEnd();
if (i < 0) {
close();
_currToken = null;
return null;
}
_binaryValue = null;
if (i == INT_RBRACKET) {
_closeArrayScope();
_currToken = JsonToken.END_ARRAY;
return null;
}
if (i == INT_RCURLY) {
_closeObjectScope();
_currToken = JsonToken.END_OBJECT;
return null;
}
// Nope: do we then expect a comma?
if (_parsingContext.expectComma()) {
if (i != INT_COMMA) {
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
i = _skipWS();
// Was that a trailing comma?
if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
if ((i == INT_RBRACKET) || (i == INT_RCURLY)) {
_closeScope(i);
return null;
}
}
}
if (!_parsingContext.inObject()) {
_updateLocation();
_nextTokenNotInObject(i);
return null;
}
_updateNameLocation();
final String nameStr = _parseName(i);
_parsingContext.setCurrentName(nameStr);
_currToken = JsonToken.FIELD_NAME;
i = _skipColon();
_updateLocation();
if (i == INT_QUOTE) {
_tokenIncomplete = true;
_nextToken = JsonToken.VALUE_STRING;
return nameStr;
}
JsonToken t;
switch (i) {
case '-':
t = _parseNegNumber();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
t = _parsePosNumber(i);
break;
case 'f':
_matchFalse();
t = JsonToken.VALUE_FALSE;
break;
case 'n':
_matchNull();
t = JsonToken.VALUE_NULL;
break;
case 't':
_matchTrue();
t = JsonToken.VALUE_TRUE;
break;
case '[':
t = JsonToken.START_ARRAY;
break;
case '{':
t = JsonToken.START_OBJECT;
break;
default:
t = _handleUnexpectedValue(i);
}
_nextToken = t;
return nameStr;
}
// Variant called when we know there's at least 4 more bytes available
private final int _skipColonFast(int ptr) throws IOException
{
int i = _inputBuffer[ptr++];
if (i == INT_COLON) { // common case, no leading space
i = _inputBuffer[ptr++];
if (i > INT_SPACE) { // nor trailing
if (i != INT_SLASH && i != INT_HASH) {
_inputPtr = ptr;
return i;
}
} else if (i == INT_SPACE || i == INT_TAB) {
i = (int) _inputBuffer[ptr++];
if (i > INT_SPACE) {
if (i != INT_SLASH && i != INT_HASH) {
_inputPtr = ptr;
return i;
}
}
}
_inputPtr = ptr-1;
return _skipColon2(true); // true -> skipped colon
}
if (i == INT_SPACE || i == INT_TAB) {
i = _inputBuffer[ptr++];
}
if (i == INT_COLON) {
i = _inputBuffer[ptr++];
if (i > INT_SPACE) {
if (i != INT_SLASH && i != INT_HASH) {
_inputPtr = ptr;
return i;
}
} else if (i == INT_SPACE || i == INT_TAB) {
i = (int) _inputBuffer[ptr++];
if (i > INT_SPACE) {
if (i != INT_SLASH && i != INT_HASH) {
_inputPtr = ptr;
return i;
}
}
}
_inputPtr = ptr-1;
return _skipColon2(true);
}
_inputPtr = ptr-1;
return _skipColon2(false);
}
private final void _isNextTokenNameYes(int i) throws IOException
{
_currToken = JsonToken.FIELD_NAME;
_updateLocation();
switch (i) {
case '"':
_tokenIncomplete = true;
_nextToken = JsonToken.VALUE_STRING;
return;
case '[':
_nextToken = JsonToken.START_ARRAY;
return;
case '{':
_nextToken = JsonToken.START_OBJECT;
return;
case 't':
_matchTrue();
_nextToken = JsonToken.VALUE_TRUE;
return;
case 'f':
_matchFalse();
_nextToken = JsonToken.VALUE_FALSE;
return;
case 'n':
_matchNull();
_nextToken = JsonToken.VALUE_NULL;
return;
case '-':
_nextToken = _parseNegNumber();
return;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
_nextToken = _parsePosNumber(i);
return;
}
_nextToken = _handleUnexpectedValue(i);
}
private final boolean _isNextTokenNameMaybe(int i, SerializableString str) throws IOException
{
// // // and this is back to standard nextToken()
String n = _parseName(i);
_parsingContext.setCurrentName(n);
final boolean match = n.equals(str.getValue());
_currToken = JsonToken.FIELD_NAME;
i = _skipColon();
_updateLocation();
// Ok: we must have a value... what is it? Strings are very common, check first:
if (i == INT_QUOTE) {
_tokenIncomplete = true;
_nextToken = JsonToken.VALUE_STRING;
return match;
}
JsonToken t;
switch (i) {
case '[':
t = JsonToken.START_ARRAY;
break;
case '{':
t = JsonToken.START_OBJECT;
break;
case 't':
_matchTrue();
t = JsonToken.VALUE_TRUE;
break;
case 'f':
_matchFalse();
t = JsonToken.VALUE_FALSE;
break;
case 'n':
_matchNull();
t = JsonToken.VALUE_NULL;
break;
case '-':
t = _parseNegNumber();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
t = _parsePosNumber(i);
break;
default:
t = _handleUnexpectedValue(i);
}
_nextToken = t;
return match;
}
@Override
public String nextTextValue() throws IOException
{
// two distinct cases; either got name and we know next type, or 'other'
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
return _finishAndReturnString();
}
return _textBuffer.contentsAsString();
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return null;
}
// !!! TODO: optimize this case as well
return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
}
@Override
public int nextIntValue(int defaultValue) throws IOException
{
// two distinct cases; either got name and we know next type, or 'other'
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_NUMBER_INT) {
return getIntValue();
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return defaultValue;
}
// !!! TODO: optimize this case as well
return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
}
@Override
public long nextLongValue(long defaultValue) throws IOException
{
// two distinct cases; either got name and we know next type, or 'other'
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_NUMBER_INT) {
return getLongValue();
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return defaultValue;
}
// !!! TODO: optimize this case as well
return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
}
@Override
public Boolean nextBooleanValue() throws IOException
{
// two distinct cases; either got name and we know next type, or 'other'
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_TRUE) {
return Boolean.TRUE;
}
if (t == JsonToken.VALUE_FALSE) {
return Boolean.FALSE;
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return null;
}
JsonToken t = nextToken();
if (t == JsonToken.VALUE_TRUE) {
return Boolean.TRUE;
}
if (t == JsonToken.VALUE_FALSE) {
return Boolean.FALSE;
}
return null;
}
/*
/**********************************************************
/* Internal methods, number parsing
/**********************************************************
*/
/**
* Initial parsing method for number values. It needs to be able
* to parse enough input to be able to determine whether the
* value is to be considered a simple integer value, or a more
* generic decimal value: latter of which needs to be expressed
* as a floating point number. The basic rule is that if the number
* has no fractional or exponential part, it is an integer; otherwise
* a floating point number.
*
* Because much of input has to be processed in any case, no partial
* parsing is done: all input text will be stored for further
* processing. However, actual numeric value conversion will be
* deferred, since it is usually the most complicated and costliest
* part of processing.
*/
protected JsonToken _parsePosNumber(int c) throws IOException
{
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
// One special case: if first char is 0, must not be followed by a digit
if (c == INT_0) {
c = _verifyNoLeadingZeroes();
}
// Ok: we can first just add digit we saw first:
outBuf[0] = (char) c;
int intLen = 1;
int outPtr = 1;
// And then figure out how far we can read without further checks
// for either input or output
final int end = Math.min(_inputEnd, _inputPtr + outBuf.length - 1); // 1 == outPtr
// With this, we have a nice and tight loop:
while (true) {
if (_inputPtr >= end) { // split across boundary, offline
return _parseNumber2(outBuf, outPtr, false, intLen);
}
c = (int) _inputBuffer[_inputPtr++] & 0xFF;
if (c < INT_0 || c > INT_9) {
break;
}
++intLen;
outBuf[outPtr++] = (char) c;
}
if (c == INT_PERIOD || c == INT_e || c == INT_E) {
return _parseFloat(outBuf, outPtr, c, false, intLen);
}
--_inputPtr; // to push back trailing char (comma etc)
_textBuffer.setCurrentLength(outPtr);
// As per #105, need separating space between root values; check here
if (_parsingContext.inRoot()) {
_verifyRootSpace(c);
}
// And there we have it!
return resetInt(false, intLen);
}
protected JsonToken _parseNegNumber() throws IOException
{
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
int outPtr = 0;
// Need to prepend sign?
outBuf[outPtr++] = '-';
// Must have something after sign too
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
int c = (int) _inputBuffer[_inputPtr++] & 0xFF;
// Note: must be followed by a digit
if (c <= INT_0) {
// One special case: if first char is 0, must not be followed by a digit
if (c != INT_0) {
return _handleInvalidNumberStart(c, true);
}
c = _verifyNoLeadingZeroes();
} else if (c > INT_9) {
return _handleInvalidNumberStart(c, true);
}
// Ok: we can first just add digit we saw first:
outBuf[outPtr++] = (char) c;
int intLen = 1;
// And then figure out how far we can read without further checks
// for either input or output
final int end = Math.min(_inputEnd, _inputPtr + outBuf.length - outPtr);
// With this, we have a nice and tight loop:
while (true) {
if (_inputPtr >= end) {
// Long enough to be split across boundary, so:
return _parseNumber2(outBuf, outPtr, true, intLen);
}
c = (int) _inputBuffer[_inputPtr++] & 0xFF;
if (c < INT_0 || c > INT_9) {
break;
}
++intLen;
outBuf[outPtr++] = (char) c;
}
if (c == INT_PERIOD || c == INT_e || c == INT_E) {
return _parseFloat(outBuf, outPtr, c, true, intLen);
}
--_inputPtr; // to push back trailing char (comma etc)
_textBuffer.setCurrentLength(outPtr);
// As per #105, need separating space between root values; check here
if (_parsingContext.inRoot()) {
_verifyRootSpace(c);
}
// And there we have it!
return resetInt(true, intLen);
}
/**
* Method called to handle parsing when input is split across buffer boundary
* (or output is longer than segment used to store it)
*/
private final JsonToken _parseNumber2(char[] outBuf, int outPtr, boolean negative,
int intPartLength) throws IOException
{
// Ok, parse the rest
while (true) {
if (_inputPtr >= _inputEnd && !_loadMore()) {
_textBuffer.setCurrentLength(outPtr);
return resetInt(negative, intPartLength);
}
int c = (int) _inputBuffer[_inputPtr++] & 0xFF;
if (c > INT_9 || c < INT_0) {
if (c == INT_PERIOD || c == INT_e || c == INT_E) {
return _parseFloat(outBuf, outPtr, c, negative, intPartLength);
}
break;
}
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
++intPartLength;
}
--_inputPtr; // to push back trailing char (comma etc)
_textBuffer.setCurrentLength(outPtr);
// As per #105, need separating space between root values; check here
if (_parsingContext.inRoot()) {
_verifyRootSpace(_inputBuffer[_inputPtr] & 0xFF);
}
// And there we have it!
return resetInt(negative, intPartLength);
}
/**
* Method called when we have seen one zero, and want to ensure
* it is not followed by another
*/
private final int _verifyNoLeadingZeroes() throws IOException
{
// Ok to have plain "0"
if (_inputPtr >= _inputEnd && !_loadMore()) {
return INT_0;
}
int ch = _inputBuffer[_inputPtr] & 0xFF;
// if not followed by a number (probably '.'); return zero as is, to be included
if (ch < INT_0 || ch > INT_9) {
return INT_0;
}
// [JACKSON-358]: we may want to allow them, after all...
if ((_features & FEAT_MASK_LEADING_ZEROS) == 0) {
reportInvalidNumber("Leading zeroes not allowed");
}
// if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number)
++_inputPtr; // Leading zero to be skipped
if (ch == INT_0) {
while (_inputPtr < _inputEnd || _loadMore()) {
ch = _inputBuffer[_inputPtr] & 0xFF;
if (ch < INT_0 || ch > INT_9) { // followed by non-number; retain one zero
return INT_0;
}
++_inputPtr; // skip previous zeroes
if (ch != INT_0) { // followed by other number; return
break;
}
}
}
return ch;
}
private final JsonToken _parseFloat(char[] outBuf, int outPtr, int c,
boolean negative, int integerPartLength) throws IOException
{
int fractLen = 0;
boolean eof = false;
// And then see if we get other parts
if (c == INT_PERIOD) { // yes, fraction
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
fract_loop:
while (true) {
if (_inputPtr >= _inputEnd && !_loadMore()) {
eof = true;
break fract_loop;
}
c = (int) _inputBuffer[_inputPtr++] & 0xFF;
if (c < INT_0 || c > INT_9) {
break fract_loop;
}
++fractLen;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
}
// must be followed by sequence of ints, one minimum
if (fractLen == 0) {
reportUnexpectedNumberChar(c, "Decimal point not followed by a digit");
}
}
int expLen = 0;
if (c == INT_e || c == INT_E) { // exponent?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
// Not optional, can require that we get one more char
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
c = (int) _inputBuffer[_inputPtr++] & 0xFF;
// Sign indicator?
if (c == '-' || c == '+') {
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
// Likewise, non optional:
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
c = (int) _inputBuffer[_inputPtr++] & 0xFF;
}
exp_loop:
while (c >= INT_0 && c <= INT_9) {
++expLen;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
outBuf[outPtr++] = (char) c;
if (_inputPtr >= _inputEnd && !_loadMore()) {
eof = true;
break exp_loop;
}
c = (int) _inputBuffer[_inputPtr++] & 0xFF;
}
// must be followed by sequence of ints, one minimum
if (expLen == 0) {
reportUnexpectedNumberChar(c, "Exponent indicator not followed by a digit");
}
}
// Ok; unless we hit end-of-input, need to push last char read back
if (!eof) {
--_inputPtr;
// As per [core#105], need separating space between root values; check here
if (_parsingContext.inRoot()) {
_verifyRootSpace(c);
}
}
_textBuffer.setCurrentLength(outPtr);
// And there we have it!
return resetFloat(negative, integerPartLength, fractLen, expLen);
}
/**
* Method called to ensure that a root-value is followed by a space
* token.
*
* NOTE: caller MUST ensure there is at least one character available;
* and that input pointer is AT given char (not past)
*/
private final void _verifyRootSpace(int ch) throws IOException
{
// caller had pushed it back, before calling; reset
++_inputPtr;
// TODO? Handle UTF-8 char decoding for error reporting
switch (ch) {
case ' ':
case '\t':
return;
case '\r':
_skipCR();
return;
case '\n':
++_currInputRow;
_currInputRowStart = _inputPtr;
return;
}
_reportMissingRootWS(ch);
}
/*
/**********************************************************
/* Internal methods, secondary parsing
/**********************************************************
*/
protected final String _parseName(int i) throws IOException
{
if (i != INT_QUOTE) {
return _handleOddName(i);
}
// First: can we optimize out bounds checks?
if ((_inputPtr + 13) > _inputEnd) { // Need up to 12 chars, plus one trailing (quote)
return slowParseName();
}
// If so, can also unroll loops nicely
/* 25-Nov-2008, tatu: This may seem weird, but here we do
* NOT want to worry about UTF-8 decoding. Rather, we'll
* assume that part is ok (if not it will get caught
* later on), and just handle quotes and backslashes here.
*/
final byte[] input = _inputBuffer;
final int[] codes = _icLatin1;
int q = input[_inputPtr++] & 0xFF;
if (codes[q] == 0) {
i = input[_inputPtr++] & 0xFF;
if (codes[i] == 0) {
q = (q << 8) | i;
i = input[_inputPtr++] & 0xFF;
if (codes[i] == 0) {
q = (q << 8) | i;
i = input[_inputPtr++] & 0xFF;
if (codes[i] == 0) {
q = (q << 8) | i;
i = input[_inputPtr++] & 0xFF;
if (codes[i] == 0) {
_quad1 = q;
return parseMediumName(i);
}
if (i == INT_QUOTE) { // 4 byte/char case or broken
return findName(q, 4);
}
return parseName(q, i, 4);
}
if (i == INT_QUOTE) { // 3 byte/char case or broken
return findName(q, 3);
}
return parseName(q, i, 3);
}
if (i == INT_QUOTE) { // 2 byte/char case or broken
return findName(q, 2);
}
return parseName(q, i, 2);
}
if (i == INT_QUOTE) { // one byte/char case or broken
return findName(q, 1);
}
return parseName(q, i, 1);
}
if (q == INT_QUOTE) { // special case, ""
return "";
}
return parseName(0, q, 0); // quoting or invalid char
}
protected final String parseMediumName(int q2) throws IOException
{
final byte[] input = _inputBuffer;
final int[] codes = _icLatin1;
// Ok, got 5 name bytes so far
int i = input[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 5 bytes
return findName(_quad1, q2, 1);
}
return parseName(_quad1, q2, i, 1); // quoting or invalid char
}
q2 = (q2 << 8) | i;
i = input[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 6 bytes
return findName(_quad1, q2, 2);
}
return parseName(_quad1, q2, i, 2);
}
q2 = (q2 << 8) | i;
i = input[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 7 bytes
return findName(_quad1, q2, 3);
}
return parseName(_quad1, q2, i, 3);
}
q2 = (q2 << 8) | i;
i = input[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 8 bytes
return findName(_quad1, q2, 4);
}
return parseName(_quad1, q2, i, 4);
}
return parseMediumName2(i, q2);
}
/**
* @since 2.6
*/
protected final String parseMediumName2(int q3, final int q2) throws IOException
{
final byte[] input = _inputBuffer;
final int[] codes = _icLatin1;
// Got 9 name bytes so far
int i = input[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 9 bytes
return findName(_quad1, q2, q3, 1);
}
return parseName(_quad1, q2, q3, i, 1);
}
q3 = (q3 << 8) | i;
i = input[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 10 bytes
return findName(_quad1, q2, q3, 2);
}
return parseName(_quad1, q2, q3, i, 2);
}
q3 = (q3 << 8) | i;
i = input[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 11 bytes
return findName(_quad1, q2, q3, 3);
}
return parseName(_quad1, q2, q3, i, 3);
}
q3 = (q3 << 8) | i;
i = input[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 12 bytes
return findName(_quad1, q2, q3, 4);
}
return parseName(_quad1, q2, q3, i, 4);
}
return parseLongName(i, q2, q3);
}
protected final String parseLongName(int q, final int q2, int q3) throws IOException
{
_quadBuffer[0] = _quad1;
_quadBuffer[1] = q2;
_quadBuffer[2] = q3;
// As explained above, will ignore UTF-8 encoding at this point
final byte[] input = _inputBuffer;
final int[] codes = _icLatin1;
int qlen = 3;
while ((_inputPtr + 4) <= _inputEnd) {
int i = input[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) {
return findName(_quadBuffer, qlen, q, 1);
}
return parseEscapedName(_quadBuffer, qlen, q, i, 1);
}
q = (q << 8) | i;
i = input[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) {
return findName(_quadBuffer, qlen, q, 2);
}
return parseEscapedName(_quadBuffer, qlen, q, i, 2);
}
q = (q << 8) | i;
i = input[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) {
return findName(_quadBuffer, qlen, q, 3);
}
return parseEscapedName(_quadBuffer, qlen, q, i, 3);
}
q = (q << 8) | i;
i = input[_inputPtr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) {
return findName(_quadBuffer, qlen, q, 4);
}
return parseEscapedName(_quadBuffer, qlen, q, i, 4);
}
// Nope, no end in sight. Need to grow quad array etc
if (qlen >= _quadBuffer.length) {
_quadBuffer = growArrayBy(_quadBuffer, qlen);
}
_quadBuffer[qlen++] = q;
q = i;
}
/* Let's offline if we hit buffer boundary (otherwise would
* need to [try to] align input, which is bit complicated
* and may not always be possible)
*/
return parseEscapedName(_quadBuffer, qlen, 0, q, 0);
}
/**
* Method called when not even first 8 bytes are guaranteed
* to come consecutively. Happens rarely, so this is offlined;
* plus we'll also do full checks for escaping etc.
*/
protected String slowParseName() throws IOException
{
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOF(": was expecting closing '\"' for name", JsonToken.FIELD_NAME);
}
}
int i = _inputBuffer[_inputPtr++] & 0xFF;
if (i == INT_QUOTE) { // special case, ""
return "";
}
return parseEscapedName(_quadBuffer, 0, 0, i, 0);
}
private final String parseName(int q1, int ch, int lastQuadBytes) throws IOException {
return parseEscapedName(_quadBuffer, 0, q1, ch, lastQuadBytes);
}
private final String parseName(int q1, int q2, int ch, int lastQuadBytes) throws IOException {
_quadBuffer[0] = q1;
return parseEscapedName(_quadBuffer, 1, q2, ch, lastQuadBytes);
}
private final String parseName(int q1, int q2, int q3, int ch, int lastQuadBytes) throws IOException {
_quadBuffer[0] = q1;
_quadBuffer[1] = q2;
return parseEscapedName(_quadBuffer, 2, q3, ch, lastQuadBytes);
}
/**
* Slower parsing method which is generally branched to when an escape
* sequence is detected (or alternatively for long names, one crossing
* input buffer boundary). Needs to be able to handle more exceptional
* cases, gets slower, and hence is offlined to a separate method.
*/
protected final String parseEscapedName(int[] quads, int qlen, int currQuad, int ch,
int currQuadBytes) throws IOException
{
// This may seem weird, but here we do not want to worry about
// UTF-8 decoding yet. Rather, we'll assume that part is ok (if not it will get
// caught later on), and just handle quotes and backslashes here.
final int[] codes = _icLatin1;
while (true) {
if (codes[ch] != 0) {
if (ch == INT_QUOTE) { // we are done
break;
}
// Unquoted white space?
if (ch != INT_BACKSLASH) {
// As per [JACKSON-208], call can now return:
_throwUnquotedSpace(ch, "name");
} else {
// Nope, escape sequence
ch = _decodeEscaped();
}
// Oh crap. May need to UTF-8 (re-)encode it, if it's beyond
// 7-bit ASCII. Gets pretty messy. If this happens often, may
// want to use different name canonicalization to avoid these hits.
if (ch > 127) {
// Ok, we'll need room for first byte right away
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
if (ch < 0x800) { // 2-byte
currQuad = (currQuad << 8) | (0xc0 | (ch >> 6));
++currQuadBytes;
// Second byte gets output below:
} else { // 3 bytes; no need to worry about surrogates here
currQuad = (currQuad << 8) | (0xe0 | (ch >> 12));
++currQuadBytes;
// need room for middle byte?
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
currQuad = (currQuad << 8) | (0x80 | ((ch >> 6) & 0x3f));
++currQuadBytes;
}
// And same last byte in both cases, gets output below:
ch = 0x80 | (ch & 0x3f);
}
}
// Ok, we have one more byte to add at any rate:
if (currQuadBytes < 4) {
++currQuadBytes;
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
currQuadBytes = 1;
}
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOF(" in field name", JsonToken.FIELD_NAME);
}
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
}
if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = _padLastQuad(currQuad, currQuadBytes);
}
String name = _symbols.findName(quads, qlen);
if (name == null) {
name = addName(quads, qlen, currQuadBytes);
}
return name;
}
/**
* Method called when we see non-white space character other
* than double quote, when expecting a field name.
* In standard mode will just throw an exception; but
* in non-standard modes may be able to parse name.
*/
protected String _handleOddName(int ch) throws IOException
{
// First: may allow single quotes
if (ch == INT_APOS && (_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) {
return _parseAposName();
}
// Allow unquoted names if feature enabled:
if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) {
char c = (char) _decodeCharForError(ch);
_reportUnexpectedChar(c, "was expecting double-quote to start field name");
}
/* Also: note that although we use a different table here,
* it does NOT handle UTF-8 decoding. It'll just pass those
* high-bit codes as acceptable for later decoding.
*/
final int[] codes = CharTypes.getInputCodeUtf8JsNames();
// Also: must start with a valid character...
if (codes[ch] != 0) {
_reportUnexpectedChar(ch, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name");
}
// Ok, now; instead of ultra-optimizing parsing here (as with regular
// JSON names), let's just use the generic "slow" variant.
// Can measure its impact later on if need be.
int[] quads = _quadBuffer;
int qlen = 0;
int currQuad = 0;
int currQuadBytes = 0;
while (true) {
// Ok, we have one more byte to add at any rate:
if (currQuadBytes < 4) {
++currQuadBytes;
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
currQuadBytes = 1;
}
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOF(" in field name", JsonToken.FIELD_NAME);
}
}
ch = _inputBuffer[_inputPtr] & 0xFF;
if (codes[ch] != 0) {
break;
}
++_inputPtr;
}
if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
}
String name = _symbols.findName(quads, qlen);
if (name == null) {
name = addName(quads, qlen, currQuadBytes);
}
return name;
}
/* Parsing to support [JACKSON-173]. Plenty of duplicated code;
* main reason being to try to avoid slowing down fast path
* for valid JSON -- more alternatives, more code, generally
* bit slower execution.
*/
protected String _parseAposName() throws IOException
{
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOF(": was expecting closing '\'' for field name", JsonToken.FIELD_NAME);
}
}
int ch = _inputBuffer[_inputPtr++] & 0xFF;
if (ch == INT_APOS) { // special case, ''
return "";
}
int[] quads = _quadBuffer;
int qlen = 0;
int currQuad = 0;
int currQuadBytes = 0;
// Copied from parseEscapedFieldName, with minor mods:
final int[] codes = _icLatin1;
while (true) {
if (ch == INT_APOS) {
break;
}
// additional check to skip handling of double-quotes
if ((codes[ch] != 0) && (ch != '"')) {
if (ch != '\\') {
// Unquoted white space?
// As per [JACKSON-208], call can now return:
_throwUnquotedSpace(ch, "name");
} else {
// Nope, escape sequence
ch = _decodeEscaped();
}
// as per main code, inefficient but will have to do
if (ch > 127) {
// Ok, we'll need room for first byte right away
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
if (ch < 0x800) { // 2-byte
currQuad = (currQuad << 8) | (0xc0 | (ch >> 6));
++currQuadBytes;
// Second byte gets output below:
} else { // 3 bytes; no need to worry about surrogates here
currQuad = (currQuad << 8) | (0xe0 | (ch >> 12));
++currQuadBytes;
// need room for middle byte?
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
currQuad = (currQuad << 8) | (0x80 | ((ch >> 6) & 0x3f));
++currQuadBytes;
}
// And same last byte in both cases, gets output below:
ch = 0x80 | (ch & 0x3f);
}
}
// Ok, we have one more byte to add at any rate:
if (currQuadBytes < 4) {
++currQuadBytes;
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
currQuadBytes = 1;
}
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOF(" in field name", JsonToken.FIELD_NAME);
}
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
}
if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = _padLastQuad(currQuad, currQuadBytes);
}
String name = _symbols.findName(quads, qlen);
if (name == null) {
name = addName(quads, qlen, currQuadBytes);
}
return name;
}
/*
/**********************************************************
/* Internal methods, symbol (name) handling
/**********************************************************
*/
private final String findName(int q1, int lastQuadBytes) throws JsonParseException
{
q1 = _padLastQuad(q1, lastQuadBytes);
// Usually we'll find it from the canonical symbol table already
String name = _symbols.findName(q1);
if (name != null) {
return name;
}
// If not, more work. We'll need add stuff to buffer
_quadBuffer[0] = q1;
return addName(_quadBuffer, 1, lastQuadBytes);
}
private final String findName(int q1, int q2, int lastQuadBytes) throws JsonParseException
{
q2 = _padLastQuad(q2, lastQuadBytes);
// Usually we'll find it from the canonical symbol table already
String name = _symbols.findName(q1, q2);
if (name != null) {
return name;
}
// If not, more work. We'll need add stuff to buffer
_quadBuffer[0] = q1;
_quadBuffer[1] = q2;
return addName(_quadBuffer, 2, lastQuadBytes);
}
private final String findName(int q1, int q2, int q3, int lastQuadBytes) throws JsonParseException
{
q3 = _padLastQuad(q3, lastQuadBytes);
String name = _symbols.findName(q1, q2, q3);
if (name != null) {
return name;
}
int[] quads = _quadBuffer;
quads[0] = q1;
quads[1] = q2;
quads[2] = _padLastQuad(q3, lastQuadBytes);
return addName(quads, 3, lastQuadBytes);
}
private final String findName(int[] quads, int qlen, int lastQuad, int lastQuadBytes) throws JsonParseException
{
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = _padLastQuad(lastQuad, lastQuadBytes);
String name = _symbols.findName(quads, qlen);
if (name == null) {
return addName(quads, qlen, lastQuadBytes);
}
return name;
}
/**
* This is the main workhorse method used when we take a symbol
* table miss. It needs to demultiplex individual bytes, decode
* multi-byte chars (if any), and then construct Name instance
* and add it to the symbol table.
*/
private final String addName(int[] quads, int qlen, int lastQuadBytes) throws JsonParseException
{
/* Ok: must decode UTF-8 chars. No other validation is
* needed, since unescaping has been done earlier as necessary
* (as well as error reporting for unescaped control chars)
*/
// 4 bytes per quad, except last one maybe less
int byteLen = (qlen << 2) - 4 + lastQuadBytes;
/* And last one is not correctly aligned (leading zero bytes instead
* need to shift a bit, instead of trailing). Only need to shift it
* for UTF-8 decoding; need revert for storage (since key will not
* be aligned, to optimize lookup speed)
*/
int lastQuad;
if (lastQuadBytes < 4) {
lastQuad = quads[qlen-1];
// 8/16/24 bit left shift
quads[qlen-1] = (lastQuad << ((4 - lastQuadBytes) << 3));
} else {
lastQuad = 0;
}
// Need some working space, TextBuffer works well:
char[] cbuf = _textBuffer.emptyAndGetCurrentSegment();
int cix = 0;
for (int ix = 0; ix < byteLen; ) {
int ch = quads[ix >> 2]; // current quad, need to shift+mask
int byteIx = (ix & 3);
ch = (ch >> ((3 - byteIx) << 3)) & 0xFF;
++ix;
if (ch > 127) { // multi-byte
int needed;
if ((ch & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
ch &= 0x1F;
needed = 1;
} else if ((ch & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
ch &= 0x0F;
needed = 2;
} else if ((ch & 0xF8) == 0xF0) { // 4 bytes; double-char with surrogates and all...
ch &= 0x07;
needed = 3;
} else { // 5- and 6-byte chars not valid xml chars
_reportInvalidInitial(ch);
needed = ch = 1; // never really gets this far
}
if ((ix + needed) > byteLen) {
_reportInvalidEOF(" in field name", JsonToken.FIELD_NAME);
}
// Ok, always need at least one more:
int ch2 = quads[ix >> 2]; // current quad, need to shift+mask
byteIx = (ix & 3);
ch2 = (ch2 >> ((3 - byteIx) << 3));
++ix;
if ((ch2 & 0xC0) != 0x080) {
_reportInvalidOther(ch2);
}
ch = (ch << 6) | (ch2 & 0x3F);
if (needed > 1) {
ch2 = quads[ix >> 2];
byteIx = (ix & 3);
ch2 = (ch2 >> ((3 - byteIx) << 3));
++ix;
if ((ch2 & 0xC0) != 0x080) {
_reportInvalidOther(ch2);
}
ch = (ch << 6) | (ch2 & 0x3F);
if (needed > 2) { // 4 bytes? (need surrogates on output)
ch2 = quads[ix >> 2];
byteIx = (ix & 3);
ch2 = (ch2 >> ((3 - byteIx) << 3));
++ix;
if ((ch2 & 0xC0) != 0x080) {
_reportInvalidOther(ch2 & 0xFF);
}
ch = (ch << 6) | (ch2 & 0x3F);
}
}
if (needed > 2) { // surrogate pair? once again, let's output one here, one later on
ch -= 0x10000; // to normalize it starting with 0x0
if (cix >= cbuf.length) {
cbuf = _textBuffer.expandCurrentSegment();
}
cbuf[cix++] = (char) (0xD800 + (ch >> 10));
ch = 0xDC00 | (ch & 0x03FF);
}
}
if (cix >= cbuf.length) {
cbuf = _textBuffer.expandCurrentSegment();
}
cbuf[cix++] = (char) ch;
}
// Ok. Now we have the character array, and can construct the String
String baseName = new String(cbuf, 0, cix);
// And finally, un-align if necessary
if (lastQuadBytes < 4) {
quads[qlen-1] = lastQuad;
}
return _symbols.addName(baseName, quads, qlen);
}
/**
* Helper method needed to fix [jackson-core#148], masking of 0x00 character
*/
private final static int _padLastQuad(int q, int bytes) {
return (bytes == 4) ? q : (q | (-1 << (bytes << 3)));
}
/*
/**********************************************************
/* Internal methods, String value parsing
/**********************************************************
*/
protected void _loadMoreGuaranteed() throws IOException {
if (!_loadMore()) { _reportInvalidEOF(); }
}
@Override
protected void _finishString() throws IOException
{
// First, single tight loop for ASCII content, not split across input buffer boundary:
int ptr = _inputPtr;
if (ptr >= _inputEnd) {
_loadMoreGuaranteed();
ptr = _inputPtr;
}
int outPtr = 0;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
final int[] codes = _icUTF8;
final int max = Math.min(_inputEnd, (ptr + outBuf.length));
final byte[] inputBuffer = _inputBuffer;
while (ptr < max) {
int c = (int) inputBuffer[ptr] & 0xFF;
if (codes[c] != 0) {
if (c == INT_QUOTE) {
_inputPtr = ptr+1;
_textBuffer.setCurrentLength(outPtr);
return;
}
break;
}
++ptr;
outBuf[outPtr++] = (char) c;
}
_inputPtr = ptr;
_finishString2(outBuf, outPtr);
}
/**
* @since 2.6
*/
protected String _finishAndReturnString() throws IOException
{
// First, single tight loop for ASCII content, not split across input buffer boundary:
int ptr = _inputPtr;
if (ptr >= _inputEnd) {
_loadMoreGuaranteed();
ptr = _inputPtr;
}
int outPtr = 0;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
final int[] codes = _icUTF8;
final int max = Math.min(_inputEnd, (ptr + outBuf.length));
final byte[] inputBuffer = _inputBuffer;
while (ptr < max) {
int c = (int) inputBuffer[ptr] & 0xFF;
if (codes[c] != 0) {
if (c == INT_QUOTE) {
_inputPtr = ptr+1;
return _textBuffer.setCurrentAndReturn(outPtr);
}
break;
}
++ptr;
outBuf[outPtr++] = (char) c;
}
_inputPtr = ptr;
_finishString2(outBuf, outPtr);
return _textBuffer.contentsAsString();
}
private final void _finishString2(char[] outBuf, int outPtr)
throws IOException
{
int c;
// Here we do want to do full decoding, hence:
final int[] codes = _icUTF8;
final byte[] inputBuffer = _inputBuffer;
main_loop:
while (true) {
// Then the tight ASCII non-funny-char loop:
ascii_loop:
while (true) {
int ptr = _inputPtr;
if (ptr >= _inputEnd) {
_loadMoreGuaranteed();
ptr = _inputPtr;
}
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
final int max = Math.min(_inputEnd, (ptr + (outBuf.length - outPtr)));
while (ptr < max) {
c = (int) inputBuffer[ptr++] & 0xFF;
if (codes[c] != 0) {
_inputPtr = ptr;
break ascii_loop;
}
outBuf[outPtr++] = (char) c;
}
_inputPtr = ptr;
}
// Ok: end marker, escape or multi-byte?
if (c == INT_QUOTE) {
break main_loop;
}
switch (codes[c]) {
case 1: // backslash
c = _decodeEscaped();
break;
case 2: // 2-byte UTF
c = _decodeUtf8_2(c);
break;
case 3: // 3-byte UTF
if ((_inputEnd - _inputPtr) >= 2) {
c = _decodeUtf8_3fast(c);
} else {
c = _decodeUtf8_3(c);
}
break;
case 4: // 4-byte UTF
c = _decodeUtf8_4(c);
// Let's add first part right away:
outBuf[outPtr++] = (char) (0xD800 | (c >> 10));
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
c = 0xDC00 | (c & 0x3FF);
// And let the other char output down below
break;
default:
if (c < INT_SPACE) {
// As per [JACKSON-208], call can now return:
_throwUnquotedSpace(c, "string value");
} else {
// Is this good enough error message?
_reportInvalidChar(c);
}
}
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
// Ok, let's add char to output:
outBuf[outPtr++] = (char) c;
}
_textBuffer.setCurrentLength(outPtr);
}
/**
* Method called to skim through rest of unparsed String value,
* if it is not needed. This can be done bit faster if contents
* need not be stored for future access.
*/
protected void _skipString() throws IOException
{
_tokenIncomplete = false;
// Need to be fully UTF-8 aware here:
final int[] codes = _icUTF8;
final byte[] inputBuffer = _inputBuffer;
main_loop:
while (true) {
int c;
ascii_loop:
while (true) {
int ptr = _inputPtr;
int max = _inputEnd;
if (ptr >= max) {
_loadMoreGuaranteed();
ptr = _inputPtr;
max = _inputEnd;
}
while (ptr < max) {
c = (int) inputBuffer[ptr++] & 0xFF;
if (codes[c] != 0) {
_inputPtr = ptr;
break ascii_loop;
}
}
_inputPtr = ptr;
}
// Ok: end marker, escape or multi-byte?
if (c == INT_QUOTE) {
break main_loop;
}
switch (codes[c]) {
case 1: // backslash
_decodeEscaped();
break;
case 2: // 2-byte UTF
_skipUtf8_2();
break;
case 3: // 3-byte UTF
_skipUtf8_3();
break;
case 4: // 4-byte UTF
_skipUtf8_4(c);
break;
default:
if (c < INT_SPACE) {
_throwUnquotedSpace(c, "string value");
} else {
// Is this good enough error message?
_reportInvalidChar(c);
}
}
}
}
/**
* Method for handling cases where first non-space character
* of an expected value token is not legal for standard JSON content.
*/
protected JsonToken _handleUnexpectedValue(int c) throws IOException
{
// Most likely an error, unless we are to allow single-quote-strings
switch (c) {
/* This check proceeds only if `Feature.ALLOW_MISSING_VALUES` is enabled;
* it is for missing values. In case of missing values in an array the next token
* will be either ',' or ']'. This case, decrements the already incremented _inputPtr
* in the buffer in case of comma (`,`) so that the existing flow goes back to checking
* the next token which will be comma again and it parsing continues.
* Also the case returns NULL as current token in case of ',' or ']'.
*/
case ']':
if (!_parsingContext.inArray()) {
break;
}
// fall through
case ',':
// 28-Mar-2016: [core#116]: If Feature.ALLOW_MISSING_VALUES is enabled
// we may allow "missing values", that is, encountering a trailing
// comma or closing marker where value would be expected
if ((_features & FEAT_MASK_ALLOW_MISSING) != 0) {
--_inputPtr;
return JsonToken.VALUE_NULL;
}
// fall through
case '}':
// Error: neither is valid at this point; valid closers have
// been handled earlier
_reportUnexpectedChar(c, "expected a value");
case '\'':
if ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) {
return _handleApos();
}
break;
case 'N':
_matchToken("NaN", 1);
if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) {
return resetAsNaN("NaN", Double.NaN);
}
_reportError("Non-standard token 'NaN': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
break;
case 'I':
_matchToken("Infinity", 1);
if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) {
return resetAsNaN("Infinity", Double.POSITIVE_INFINITY);
}
_reportError("Non-standard token 'Infinity': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
break;
case '+': // note: '-' is taken as number
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOFInValue(JsonToken.VALUE_NUMBER_INT);
}
}
return _handleInvalidNumberStart(_inputBuffer[_inputPtr++] & 0xFF, false);
}
// [core#77] Try to decode most likely token
if (Character.isJavaIdentifierStart(c)) {
_reportInvalidToken(""+((char) c), _validJsonTokenList());
}
// but if it doesn't look like a token:
_reportUnexpectedChar(c, "expected a valid value "+_validJsonValueList());
return null;
}
protected JsonToken _handleApos() throws IOException
{
int c = 0;
// Otherwise almost verbatim copy of _finishString()
int outPtr = 0;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
// Here we do want to do full decoding, hence:
final int[] codes = _icUTF8;
final byte[] inputBuffer = _inputBuffer;
main_loop:
while (true) {
// Then the tight ascii non-funny-char loop:
ascii_loop:
while (true) {
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
int max = _inputEnd;
{
int max2 = _inputPtr + (outBuf.length - outPtr);
if (max2 < max) {
max = max2;
}
}
while (_inputPtr < max) {
c = (int) inputBuffer[_inputPtr++] & 0xFF;
if (c == INT_APOS || codes[c] != 0) {
break ascii_loop;
}
outBuf[outPtr++] = (char) c;
}
}
// Ok: end marker, escape or multi-byte?
if (c == INT_APOS) {
break main_loop;
}
switch (codes[c]) {
case 1: // backslash
c = _decodeEscaped();
break;
case 2: // 2-byte UTF
c = _decodeUtf8_2(c);
break;
case 3: // 3-byte UTF
if ((_inputEnd - _inputPtr) >= 2) {
c = _decodeUtf8_3fast(c);
} else {
c = _decodeUtf8_3(c);
}
break;
case 4: // 4-byte UTF
c = _decodeUtf8_4(c);
// Let's add first part right away:
outBuf[outPtr++] = (char) (0xD800 | (c >> 10));
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
c = 0xDC00 | (c & 0x3FF);
// And let the other char output down below
break;
default:
if (c < INT_SPACE) {
_throwUnquotedSpace(c, "string value");
}
// Is this good enough error message?
_reportInvalidChar(c);
}
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
// Ok, let's add char to output:
outBuf[outPtr++] = (char) c;
}
_textBuffer.setCurrentLength(outPtr);
return JsonToken.VALUE_STRING;
}
/*
/**********************************************************
/* Internal methods, well-known token decoding
/**********************************************************
*/
/**
* Method called if expected numeric value (due to leading sign) does not
* look like a number
*/
protected JsonToken _handleInvalidNumberStart(int ch, boolean neg) throws IOException
{
while (ch == 'I') {
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOFInValue(JsonToken.VALUE_NUMBER_FLOAT); // possibly?
}
}
ch = _inputBuffer[_inputPtr++];
String match;
if (ch == 'N') {
match = neg ? "-INF" :"+INF";
} else if (ch == 'n') {
match = neg ? "-Infinity" :"+Infinity";
} else {
break;
}
_matchToken(match, 3);
if ((_features & FEAT_MASK_NON_NUM_NUMBERS) != 0) {
return resetAsNaN(match, neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
}
_reportError("Non-standard token '%s': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow",
match);
}
reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value");
return null;
}
// NOTE: first character already decoded
protected final void _matchTrue() throws IOException
{
int ptr = _inputPtr;
if ((ptr + 3) < _inputEnd) {
byte[] buf = _inputBuffer;
if ((buf[ptr++] == 'r')
&& (buf[ptr++] == 'u')
&& (buf[ptr++] == 'e')) {
int ch = buf[ptr] & 0xFF;
if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
_inputPtr = ptr;
return;
}
}
}
_matchToken2("true", 1);
}
protected final void _matchFalse() throws IOException
{
int ptr = _inputPtr;
if ((ptr + 4) < _inputEnd) {
byte[] buf = _inputBuffer;
if ((buf[ptr++] == 'a')
&& (buf[ptr++] == 'l')
&& (buf[ptr++] == 's')
&& (buf[ptr++] == 'e')) {
int ch = buf[ptr] & 0xFF;
if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
_inputPtr = ptr;
return;
}
}
}
_matchToken2("false", 1);
}
protected final void _matchNull() throws IOException
{
int ptr = _inputPtr;
if ((ptr + 3) < _inputEnd) {
byte[] buf = _inputBuffer;
if ((buf[ptr++] == 'u')
&& (buf[ptr++] == 'l')
&& (buf[ptr++] == 'l')) {
int ch = buf[ptr] & 0xFF;
if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
_inputPtr = ptr;
return;
}
}
}
_matchToken2("null", 1);
}
protected final void _matchToken(String matchStr, int i) throws IOException
{
final int len = matchStr.length();
if ((_inputPtr + len) >= _inputEnd) {
_matchToken2(matchStr, i);
return;
}
do {
if (_inputBuffer[_inputPtr] != matchStr.charAt(i)) {
_reportInvalidToken(matchStr.substring(0, i));
}
++_inputPtr;
} while (++i < len);
int ch = _inputBuffer[_inputPtr] & 0xFF;
if (ch >= '0' && ch != ']' && ch != '}') { // expected/allowed chars
_checkMatchEnd(matchStr, i, ch);
}
}
private final void _matchToken2(String matchStr, int i) throws IOException
{
final int len = matchStr.length();
do {
if (((_inputPtr >= _inputEnd) && !_loadMore())
|| (_inputBuffer[_inputPtr] != matchStr.charAt(i))) {
_reportInvalidToken(matchStr.substring(0, i));
}
++_inputPtr;
} while (++i < len);
// but let's also ensure we either get EOF, or non-alphanum char...
if (_inputPtr >= _inputEnd && !_loadMore()) {
return;
}
int ch = _inputBuffer[_inputPtr] & 0xFF;
if (ch >= '0' && ch != ']' && ch != '}') { // expected/allowed chars
_checkMatchEnd(matchStr, i, ch);
}
}
private final void _checkMatchEnd(String matchStr, int i, int ch) throws IOException {
// but actually only alphanums are problematic
char c = (char) _decodeCharForError(ch);
if (Character.isJavaIdentifierPart(c)) {
_reportInvalidToken(matchStr.substring(0, i));
}
}
/*
/**********************************************************
/* Internal methods, ws skipping, escape/unescape
/**********************************************************
*/
private final int _skipWS() throws IOException
{
while (_inputPtr < _inputEnd) {
int i = _inputBuffer[_inputPtr++] & 0xFF;
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
--_inputPtr;
return _skipWS2();
}
return i;
}
if (i != INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
return _skipWS2();
}
private final int _skipWS2() throws IOException
{
while (_inputPtr < _inputEnd || _loadMore()) {
int i = _inputBuffer[_inputPtr++] & 0xFF;
if (i > INT_SPACE) {
if (i == INT_SLASH) {
_skipComment();
continue;
}
if (i == INT_HASH) {
if (_skipYAMLComment()) {
continue;
}
}
return i;
}
if (i != INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
throw _constructError("Unexpected end-of-input within/between "+_parsingContext.typeDesc()+" entries");
}
private final int _skipWSOrEnd() throws IOException
{
// Let's handle first character separately since it is likely that
// it is either non-whitespace; or we have longer run of white space
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
return _eofAsNextChar();
}
}
int i = _inputBuffer[_inputPtr++] & 0xFF;
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
--_inputPtr;
return _skipWSOrEnd2();
}
return i;
}
if (i != INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
while (_inputPtr < _inputEnd) {
i = _inputBuffer[_inputPtr++] & 0xFF;
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
--_inputPtr;
return _skipWSOrEnd2();
}
return i;
}
if (i != INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
return _skipWSOrEnd2();
}
private final int _skipWSOrEnd2() throws IOException
{
while ((_inputPtr < _inputEnd) || _loadMore()) {
int i = _inputBuffer[_inputPtr++] & 0xFF;
if (i > INT_SPACE) {
if (i == INT_SLASH) {
_skipComment();
continue;
}
if (i == INT_HASH) {
if (_skipYAMLComment()) {
continue;
}
}
return i;
} else if (i != INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
// We ran out of input...
return _eofAsNextChar();
}
private final int _skipColon() throws IOException
{
if ((_inputPtr + 4) >= _inputEnd) {
return _skipColon2(false);
}
// Fast path: colon with optional single-space/tab before and/or after:
int i = _inputBuffer[_inputPtr];
if (i == INT_COLON) { // common case, no leading space
i = _inputBuffer[++_inputPtr];
if (i > INT_SPACE) { // nor trailing
if (i == INT_SLASH || i == INT_HASH) {
return _skipColon2(true);
}
++_inputPtr;
return i;
}
if (i == INT_SPACE || i == INT_TAB) {
i = (int) _inputBuffer[++_inputPtr];
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
return _skipColon2(true);
}
++_inputPtr;
return i;
}
}
return _skipColon2(true); // true -> skipped colon
}
if (i == INT_SPACE || i == INT_TAB) {
i = _inputBuffer[++_inputPtr];
}
if (i == INT_COLON) {
i = _inputBuffer[++_inputPtr];
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
return _skipColon2(true);
}
++_inputPtr;
return i;
}
if (i == INT_SPACE || i == INT_TAB) {
i = (int) _inputBuffer[++_inputPtr];
if (i > INT_SPACE) {
if (i == INT_SLASH || i == INT_HASH) {
return _skipColon2(true);
}
++_inputPtr;
return i;
}
}
return _skipColon2(true);
}
return _skipColon2(false);
}
private final int _skipColon2(boolean gotColon) throws IOException
{
while (_inputPtr < _inputEnd || _loadMore()) {
int i = _inputBuffer[_inputPtr++] & 0xFF;
if (i > INT_SPACE) {
if (i == INT_SLASH) {
_skipComment();
continue;
}
if (i == INT_HASH) {
if (_skipYAMLComment()) {
continue;
}
}
if (gotColon) {
return i;
}
if (i != INT_COLON) {
_reportUnexpectedChar(i, "was expecting a colon to separate field name and value");
}
gotColon = true;
} else if (i != INT_SPACE) {
if (i == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (i == INT_CR) {
_skipCR();
} else if (i != INT_TAB) {
_throwInvalidSpace(i);
}
}
}
_reportInvalidEOF(" within/between "+_parsingContext.typeDesc()+" entries",
null);
return -1;
}
private final void _skipComment() throws IOException
{
if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) {
_reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)");
}
// First: check which comment (if either) it is:
if (_inputPtr >= _inputEnd && !_loadMore()) {
_reportInvalidEOF(" in a comment", null);
}
int c = _inputBuffer[_inputPtr++] & 0xFF;
if (c == INT_SLASH) {
_skipLine();
} else if (c == INT_ASTERISK) {
_skipCComment();
} else {
_reportUnexpectedChar(c, "was expecting either '*' or '/' for a comment");
}
}
private final void _skipCComment() throws IOException
{
// Need to be UTF-8 aware here to decode content (for skipping)
final int[] codes = CharTypes.getInputCodeComment();
// Ok: need the matching '*/'
main_loop:
while ((_inputPtr < _inputEnd) || _loadMore()) {
int i = (int) _inputBuffer[_inputPtr++] & 0xFF;
int code = codes[i];
if (code != 0) {
switch (code) {
case '*':
if (_inputPtr >= _inputEnd && !_loadMore()) {
break main_loop;
}
if (_inputBuffer[_inputPtr] == INT_SLASH) {
++_inputPtr;
return;
}
break;
case INT_LF:
++_currInputRow;
_currInputRowStart = _inputPtr;
break;
case INT_CR:
_skipCR();
break;
case 2: // 2-byte UTF
_skipUtf8_2();
break;
case 3: // 3-byte UTF
_skipUtf8_3();
break;
case 4: // 4-byte UTF
_skipUtf8_4(i);
break;
default: // e.g. -1
// Is this good enough error message?
_reportInvalidChar(i);
}
}
}
_reportInvalidEOF(" in a comment", null);
}
private final boolean _skipYAMLComment() throws IOException
{
if ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) == 0) {
return false;
}
_skipLine();
return true;
}
/**
* Method for skipping contents of an input line; usually for CPP
* and YAML style comments.
*/
private final void _skipLine() throws IOException
{
// Ok: need to find EOF or linefeed
final int[] codes = CharTypes.getInputCodeComment();
while ((_inputPtr < _inputEnd) || _loadMore()) {
int i = (int) _inputBuffer[_inputPtr++] & 0xFF;
int code = codes[i];
if (code != 0) {
switch (code) {
case INT_LF:
++_currInputRow;
_currInputRowStart = _inputPtr;
return;
case INT_CR:
_skipCR();
return;
case '*': // nop for these comments
break;
case 2: // 2-byte UTF
_skipUtf8_2();
break;
case 3: // 3-byte UTF
_skipUtf8_3();
break;
case 4: // 4-byte UTF
_skipUtf8_4(i);
break;
default: // e.g. -1
if (code < 0) {
// Is this good enough error message?
_reportInvalidChar(i);
}
}
}
}
}
@Override
protected char _decodeEscaped() throws IOException
{
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOF(" in character escape sequence", JsonToken.VALUE_STRING);
}
}
int c = (int) _inputBuffer[_inputPtr++];
switch (c) {
// First, ones that are mapped
case 'b':
return '\b';
case 't':
return '\t';
case 'n':
return '\n';
case 'f':
return '\f';
case 'r':
return '\r';
// And these are to be returned as they are
case '"':
case '/':
case '\\':
return (char) c;
case 'u': // and finally hex-escaped
break;
default:
return _handleUnrecognizedCharacterEscape((char) _decodeCharForError(c));
}
// Ok, a hex escape. Need 4 characters
int value = 0;
for (int i = 0; i < 4; ++i) {
if (_inputPtr >= _inputEnd) {
if (!_loadMore()) {
_reportInvalidEOF(" in character escape sequence", JsonToken.VALUE_STRING);
}
}
int ch = _inputBuffer[_inputPtr++];
int digit = CharTypes.charToHex(ch);
if (digit < 0) {
_reportUnexpectedChar(ch & 0xFF, "expected a hex-digit for character escape sequence");
}
value = (value << 4) | digit;
}
return (char) value;
}
protected int _decodeCharForError(int firstByte) throws IOException
{
int c = firstByte & 0xFF;
if (c > 0x7F) { // if >= 0, is ascii and fine as is
int needed;
// Ok; if we end here, we got multi-byte combination
if ((c & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
c &= 0x1F;
needed = 1;
} else if ((c & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
c &= 0x0F;
needed = 2;
} else if ((c & 0xF8) == 0xF0) {
// 4 bytes; double-char with surrogates and all...
c &= 0x07;
needed = 3;
} else {
_reportInvalidInitial(c & 0xFF);
needed = 1; // never gets here
}
int d = nextByte();
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
c = (c << 6) | (d & 0x3F);
if (needed > 1) { // needed == 1 means 2 bytes total
d = nextByte(); // 3rd byte
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
c = (c << 6) | (d & 0x3F);
if (needed > 2) { // 4 bytes? (need surrogates)
d = nextByte();
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF);
}
c = (c << 6) | (d & 0x3F);
}
}
}
return c;
}
/*
/**********************************************************
/* Internal methods,UTF8 decoding
/**********************************************************
*/
private final int _decodeUtf8_2(int c) throws IOException
{
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
return ((c & 0x1F) << 6) | (d & 0x3F);
}
private final int _decodeUtf8_3(int c1) throws IOException
{
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
c1 &= 0x0F;
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
int c = (c1 << 6) | (d & 0x3F);
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
c = (c << 6) | (d & 0x3F);
return c;
}
private final int _decodeUtf8_3fast(int c1) throws IOException
{
c1 &= 0x0F;
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
int c = (c1 << 6) | (d & 0x3F);
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
c = (c << 6) | (d & 0x3F);
return c;
}
/**
* @return Character value minus 0x10000; this so that caller
* can readily expand it to actual surrogates
*/
private final int _decodeUtf8_4(int c) throws IOException
{
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
c = ((c & 0x07) << 6) | (d & 0x3F);
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
c = (c << 6) | (d & 0x3F);
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
/* note: won't change it to negative here, since caller
* already knows it'll need a surrogate
*/
return ((c << 6) | (d & 0x3F)) - 0x10000;
}
private final void _skipUtf8_2() throws IOException
{
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
int c = (int) _inputBuffer[_inputPtr++];
if ((c & 0xC0) != 0x080) {
_reportInvalidOther(c & 0xFF, _inputPtr);
}
}
/* Alas, can't heavily optimize skipping, since we still have to
* do validity checks...
*/
private final void _skipUtf8_3() throws IOException
{
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
//c &= 0x0F;
int c = (int) _inputBuffer[_inputPtr++];
if ((c & 0xC0) != 0x080) {
_reportInvalidOther(c & 0xFF, _inputPtr);
}
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
c = (int) _inputBuffer[_inputPtr++];
if ((c & 0xC0) != 0x080) {
_reportInvalidOther(c & 0xFF, _inputPtr);
}
}
private final void _skipUtf8_4(int c) throws IOException
{
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
int d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
d = (int) _inputBuffer[_inputPtr++];
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
}
/*
/**********************************************************
/* Internal methods, input loading
/**********************************************************
*/
/**
* We actually need to check the character value here
* (to see if we have \n following \r).
*/
protected final void _skipCR() throws IOException
{
if (_inputPtr < _inputEnd || _loadMore()) {
if (_inputBuffer[_inputPtr] == BYTE_LF) {
++_inputPtr;
}
}
++_currInputRow;
_currInputRowStart = _inputPtr;
}
private int nextByte() throws IOException
{
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
return _inputBuffer[_inputPtr++] & 0xFF;
}
/*
/**********************************************************
/* Internal methods, error reporting
/**********************************************************
*/
protected void _reportInvalidToken(String matchedPart, int ptr) throws IOException {
_inputPtr = ptr;
_reportInvalidToken(matchedPart, _validJsonTokenList());
}
protected void _reportInvalidToken(String matchedPart) throws IOException {
_reportInvalidToken(matchedPart, _validJsonTokenList());
}
protected void _reportInvalidToken(String matchedPart, String msg) throws IOException
{
/* Let's just try to find what appears to be the token, using
* regular Java identifier character rules. It's just a heuristic,
* nothing fancy here (nor fast).
*/
StringBuilder sb = new StringBuilder(matchedPart);
while ((_inputPtr < _inputEnd) || _loadMore()) {
int i = (int) _inputBuffer[_inputPtr++];
char c = (char) _decodeCharForError(i);
if (!Character.isJavaIdentifierPart(c)) {
// 11-Jan-2016, tatu: note: we will fully consume the character,
// included or not, so if recovery was possible, it'd be off-by-one...
break;
}
sb.append(c);
if (sb.length() >= MAX_ERROR_TOKEN_LENGTH) {
sb.append("...");
break;
}
}
_reportError("Unrecognized token '%s': was expecting %s", sb, msg);
}
protected void _reportInvalidChar(int c) throws JsonParseException
{
// Either invalid WS or illegal UTF-8 start char
if (c < INT_SPACE) {
_throwInvalidSpace(c);
}
_reportInvalidInitial(c);
}
protected void _reportInvalidInitial(int mask) throws JsonParseException {
_reportError("Invalid UTF-8 start byte 0x"+Integer.toHexString(mask));
}
protected void _reportInvalidOther(int mask) throws JsonParseException {
_reportError("Invalid UTF-8 middle byte 0x"+Integer.toHexString(mask));
}
protected void _reportInvalidOther(int mask, int ptr)
throws JsonParseException
{
_inputPtr = ptr;
_reportInvalidOther(mask);
}
/*
/**********************************************************
/* Internal methods, binary access
/**********************************************************
*/
/**
* Efficient handling for incremental parsing of base64-encoded
* textual content.
*/
@SuppressWarnings("resource")
protected final byte[] _decodeBase64(Base64Variant b64variant) throws IOException
{
ByteArrayBuilder builder = _getByteArrayBuilder();
while (true) {
// first, we'll skip preceding white space, if any
int ch;
do {
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = (int) _inputBuffer[_inputPtr++] & 0xFF;
} while (ch <= INT_SPACE);
int bits = b64variant.decodeBase64Char(ch);
if (bits < 0) { // reached the end, fair and square?
if (ch == INT_QUOTE) {
return builder.toByteArray();
}
bits = _decodeBase64Escape(b64variant, ch, 0);
if (bits < 0) { // white space to skip
continue;
}
}
int decodedData = bits;
// then second base64 char; can't get padding yet, nor ws
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
bits = _decodeBase64Escape(b64variant, ch, 1);
}
decodedData = (decodedData << 6) | bits;
// third base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
bits = b64variant.decodeBase64Char(ch);
// First branch: can get padding (-> 1 byte)
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// could also just be 'missing' padding
if (ch == INT_QUOTE) {
decodedData >>= 4;
builder.append(decodedData);
if (b64variant.usesPadding()) {
--_inputPtr; // to keep parser state bit more consistent
_handleBase64MissingPadding(b64variant);
}
return builder.toByteArray();
}
bits = _decodeBase64Escape(b64variant, ch, 2);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
// Ok, must get padding
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
if (!b64variant.usesPaddingChar(ch)) {
if (_decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) {
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
}
}
// Got 12 bits, only need 8, need to shift
decodedData >>= 4;
builder.append(decodedData);
continue;
}
}
// Nope, 2 or 3 bytes
decodedData = (decodedData << 6) | bits;
// fourth and last base64 char; can be padding, but not ws
if (_inputPtr >= _inputEnd) {
_loadMoreGuaranteed();
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// could also just be 'missing' padding
if (ch == INT_QUOTE) {
decodedData >>= 2;
builder.appendTwoBytes(decodedData);
if (b64variant.usesPadding()) {
--_inputPtr; // to keep parser state bit more consistent
_handleBase64MissingPadding(b64variant);
}
return builder.toByteArray();
}
bits = _decodeBase64Escape(b64variant, ch, 3);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
// With padding we only get 2 bytes; but we have to shift it
// a bit so it is identical to triplet case with partial output.
// 3 chars gives 3x6 == 18 bits, of which 2 are dummies, need to discard:
decodedData >>= 2;
builder.appendTwoBytes(decodedData);
continue;
}
}
// otherwise, our triplet is now complete
decodedData = (decodedData << 6) | bits;
builder.appendThreeBytes(decodedData);
}
}
/*
/**********************************************************
/* Improved location updating (refactored in 2.7)
/**********************************************************
*/
// As per [core#108], must ensure we call the right method
@Override
public JsonLocation getTokenLocation()
{
if (_currToken == JsonToken.FIELD_NAME) {
long total = _currInputProcessed + (_nameStartOffset-1);
return new JsonLocation(_getSourceReference(),
total, -1L, _nameStartRow, _nameStartCol);
}
return new JsonLocation(_getSourceReference(),
_tokenInputTotal-1, -1L, _tokenInputRow, _tokenInputCol);
}
// As per [core#108], must ensure we call the right method
@Override
public JsonLocation getCurrentLocation()
{
int col = _inputPtr - _currInputRowStart + 1; // 1-based
return new JsonLocation(_getSourceReference(),
_currInputProcessed + _inputPtr, -1L, // bytes, chars
_currInputRow, col);
}
// @since 2.7
private final void _updateLocation()
{
_tokenInputRow = _currInputRow;
final int ptr = _inputPtr;
_tokenInputTotal = _currInputProcessed + ptr;
_tokenInputCol = ptr - _currInputRowStart;
}
// @since 2.7
private final void _updateNameLocation()
{
_nameStartRow = _currInputRow;
final int ptr = _inputPtr;
_nameStartOffset = ptr;
_nameStartCol = ptr - _currInputRowStart;
}
/*
/**********************************************************
/* Internal methods, other
/**********************************************************
*/
private final JsonToken _closeScope(int i) throws JsonParseException {
if (i == INT_RCURLY) {
_closeObjectScope();
return (_currToken = JsonToken.END_OBJECT);
}
_closeArrayScope();
return (_currToken = JsonToken.END_ARRAY);
}
private final void _closeArrayScope() throws JsonParseException {
_updateLocation();
if (!_parsingContext.inArray()) {
_reportMismatchedEndMarker(']', '}');
}
_parsingContext = _parsingContext.clearAndGetParent();
}
private final void _closeObjectScope() throws JsonParseException {
_updateLocation();
if (!_parsingContext.inObject()) {
_reportMismatchedEndMarker('}', ']');
}
_parsingContext = _parsingContext.clearAndGetParent();
}
}
WriterBasedJsonGenerator.java 0000664 0000000 0000000 00000203405 13561642473 0035014 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json package com.fasterxml.jackson.core.json;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.io.CharTypes;
import com.fasterxml.jackson.core.io.CharacterEscapes;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.io.NumberOutput;
/**
* {@link JsonGenerator} that outputs JSON content using a {@link java.io.Writer}
* which handles character encoding.
*/
public class WriterBasedJsonGenerator
extends JsonGeneratorImpl
{
final protected static int SHORT_WRITE = 32;
final protected static char[] HEX_CHARS = CharTypes.copyHexChars();
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
final protected Writer _writer;
/**
* Character used for quoting JSON Object property names
* and String values.
*/
protected char _quoteChar;
/*
/**********************************************************
/* Output buffering
/**********************************************************
*/
/**
* Intermediate buffer in which contents are buffered before
* being written using {@link #_writer}.
*/
protected char[] _outputBuffer;
/**
* Pointer to the first buffered character to output
*/
protected int _outputHead;
/**
* Pointer to the position right beyond the last character to output
* (end marker; may point to position right beyond the end of the buffer)
*/
protected int _outputTail;
/**
* End marker of the output buffer; one past the last valid position
* within the buffer.
*/
protected int _outputEnd;
/**
* Short (14 char) temporary buffer allocated if needed, for constructing
* escape sequences
*/
protected char[] _entityBuffer;
/**
* When custom escapes are used, this member variable is used
* internally to hold a reference to currently used escape
*/
protected SerializableString _currentEscape;
/**
* Intermediate buffer in which characters of a String are copied
* before being encoded.
*
* @since 2.10
*/
protected char[] _copyBuffer;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
@Deprecated // since 2.10
public WriterBasedJsonGenerator(IOContext ctxt, int features,
ObjectCodec codec, Writer w) {
this(ctxt, features, codec, w, JsonFactory.DEFAULT_QUOTE_CHAR);
}
/**
* @since 2.10
*/
public WriterBasedJsonGenerator(IOContext ctxt, int features,
ObjectCodec codec, Writer w,
char quoteChar)
{
super(ctxt, features, codec);
_writer = w;
_outputBuffer = ctxt.allocConcatBuffer();
_outputEnd = _outputBuffer.length;
_quoteChar = quoteChar;
if (quoteChar != '"') { // since 2.10
_outputEscapes = CharTypes.get7BitOutputEscapes(quoteChar);
}
}
/*
/**********************************************************
/* Overridden configuration, introspection methods
/**********************************************************
*/
@Override
public Object getOutputTarget() {
return _writer;
}
@Override
public int getOutputBuffered() {
// Assuming tail and head are kept but... trust and verify:
int len = _outputTail - _outputHead;
return Math.max(0, len);
}
// json does allow this so
@Override
public boolean canWriteFormattedNumbers() { return true; }
/*
/**********************************************************
/* Overridden methods
/**********************************************************
*/
@Override
public void writeFieldName(String name) throws IOException
{
int status = _writeContext.writeFieldName(name);
if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
_writeFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
}
@Override
public void writeFieldName(SerializableString name) throws IOException
{
// Object is a value, need to verify it's allowed
int status = _writeContext.writeFieldName(name.getValue());
if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
_writeFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
}
protected final void _writeFieldName(String name, boolean commaBefore) throws IOException
{
if (_cfgPrettyPrinter != null) {
_writePPFieldName(name, commaBefore);
return;
}
// for fast+std case, need to output up to 2 chars, comma, dquote
if ((_outputTail + 1) >= _outputEnd) {
_flushBuffer();
}
if (commaBefore) {
_outputBuffer[_outputTail++] = ',';
}
// Alternate mode, in which quoting of field names disabled?
if (_cfgUnqNames) {
_writeString(name);
return;
}
// we know there's room for at least one more char
_outputBuffer[_outputTail++] = _quoteChar;
// The beef:
_writeString(name);
// and closing quotes; need room for one more char:
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
protected final void _writeFieldName(SerializableString name, boolean commaBefore) throws IOException
{
if (_cfgPrettyPrinter != null) {
_writePPFieldName(name, commaBefore);
return;
}
// for fast+std case, need to output up to 2 chars, comma, dquote
if ((_outputTail + 1) >= _outputEnd) {
_flushBuffer();
}
if (commaBefore) {
_outputBuffer[_outputTail++] = ',';
}
// Alternate mode, in which quoting of field names disabled?
if (_cfgUnqNames) {
final char[] ch = name.asQuotedChars();
writeRaw(ch, 0, ch.length);
return;
}
// we know there's room for at least one more char
_outputBuffer[_outputTail++] = _quoteChar;
// The beef:
int len = name.appendQuoted(_outputBuffer, _outputTail);
if (len < 0) {
_writeFieldNameTail(name);
return;
}
_outputTail += len;
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
private final void _writeFieldNameTail(SerializableString name) throws IOException
{
final char[] quoted = name.asQuotedChars();
writeRaw(quoted, 0, quoted.length);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
/*
/**********************************************************
/* Output method implementations, structural
/**********************************************************
*/
@Override
public void writeStartArray() throws IOException
{
_verifyValueWrite("start an array");
_writeContext = _writeContext.createChildArrayContext();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeStartArray(this);
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '[';
}
}
@Override // since 2.10
public void writeStartArray(int size) throws IOException
{
_verifyValueWrite("start an array");
_writeContext = _writeContext.createChildArrayContext();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeStartArray(this);
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '[';
}
}
@Override
public void writeEndArray() throws IOException
{
if (!_writeContext.inArray()) {
_reportError("Current context not Array but "+_writeContext.typeDesc());
}
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount());
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = ']';
}
_writeContext = _writeContext.clearAndGetParent();
}
@Override
public void writeStartObject() throws IOException
{
_verifyValueWrite("start an object");
_writeContext = _writeContext.createChildObjectContext();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeStartObject(this);
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '{';
}
}
@Override // since 2.8
public void writeStartObject(Object forValue) throws IOException
{
_verifyValueWrite("start an object");
JsonWriteContext ctxt = _writeContext.createChildObjectContext(forValue);
_writeContext = ctxt;
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeStartObject(this);
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '{';
}
}
@Override
public void writeEndObject() throws IOException
{
if (!_writeContext.inObject()) {
_reportError("Current context not Object but "+_writeContext.typeDesc());
}
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount());
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '}';
}
_writeContext = _writeContext.clearAndGetParent();
}
/**
* Specialized version of
* Note: when called, textual content to write is within output
* buffer, right after buffered content (if any). That's why only
* length of that text is passed, as buffer and offset are implied.
*/
private void _writeSegment(int end) throws IOException
{
final int[] escCodes = _outputEscapes;
final int escLen = escCodes.length;
int ptr = 0;
int start = ptr;
output_loop:
while (ptr < end) {
// Fast loop for chars not needing escaping
char c;
while (true) {
c = _outputBuffer[ptr];
if (c < escLen && escCodes[c] != 0) {
break;
}
if (++ptr >= end) {
break;
}
}
// Ok, bumped into something that needs escaping.
/* First things first: need to flush the buffer.
* Inlined, as we don't want to lose tail pointer
*/
int flushLen = (ptr - start);
if (flushLen > 0) {
_writer.write(_outputBuffer, start, flushLen);
if (ptr >= end) {
break output_loop;
}
}
++ptr;
// So; either try to prepend (most likely), or write directly:
start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCodes[c]);
}
}
/**
* This method called when the string content is already in
* a char buffer, and need not be copied for processing.
*/
private void _writeString(char[] text, int offset, int len) throws IOException
{
if (_characterEscapes != null) {
_writeStringCustom(text, offset, len);
return;
}
if (_maximumNonEscapedChar != 0) {
_writeStringASCII(text, offset, len, _maximumNonEscapedChar);
return;
}
/* Let's just find longest spans of non-escapable
* content, and for each see if it makes sense
* to copy them, or write through
*/
len += offset; // -> len marks the end from now on
final int[] escCodes = _outputEscapes;
final int escLen = escCodes.length;
while (offset < len) {
int start = offset;
while (true) {
char c = text[offset];
if (c < escLen && escCodes[c] != 0) {
break;
}
if (++offset >= len) {
break;
}
}
// Short span? Better just copy it to buffer first:
int newAmount = offset - start;
if (newAmount < SHORT_WRITE) {
// Note: let's reserve room for escaped char (up to 6 chars)
if ((_outputTail + newAmount) > _outputEnd) {
_flushBuffer();
}
if (newAmount > 0) {
System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount);
_outputTail += newAmount;
}
} else { // Nope: better just write through
_flushBuffer();
_writer.write(text, start, newAmount);
}
// Was this the end?
if (offset >= len) { // yup
break;
}
// Nope, need to escape the char.
char c = text[offset++];
_appendCharacterEscape(c, escCodes[c]);
}
}
/*
/**********************************************************
/* Internal methods, low-level writing, text segment
/* with additional escaping (ASCII or such)
/**********************************************************
*/
/* Same as "_writeString2()", except needs additional escaping
* for subset of characters
*/
private void _writeStringASCII(final int len, final int maxNonEscaped)
throws IOException, JsonGenerationException
{
// And then we'll need to verify need for escaping etc:
int end = _outputTail + len;
final int[] escCodes = _outputEscapes;
final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
int escCode = 0;
output_loop:
while (_outputTail < end) {
char c;
// Fast loop for chars not needing escaping
escape_loop:
while (true) {
c = _outputBuffer[_outputTail];
if (c < escLimit) {
escCode = escCodes[c];
if (escCode != 0) {
break escape_loop;
}
} else if (c > maxNonEscaped) {
escCode = CharacterEscapes.ESCAPE_STANDARD;
break escape_loop;
}
if (++_outputTail >= end) {
break output_loop;
}
}
int flushLen = (_outputTail - _outputHead);
if (flushLen > 0) {
_writer.write(_outputBuffer, _outputHead, flushLen);
}
++_outputTail;
_prependOrWriteCharacterEscape(c, escCode);
}
}
private void _writeSegmentASCII(int end, final int maxNonEscaped)
throws IOException, JsonGenerationException
{
final int[] escCodes = _outputEscapes;
final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
int ptr = 0;
int escCode = 0;
int start = ptr;
output_loop:
while (ptr < end) {
// Fast loop for chars not needing escaping
char c;
while (true) {
c = _outputBuffer[ptr];
if (c < escLimit) {
escCode = escCodes[c];
if (escCode != 0) {
break;
}
} else if (c > maxNonEscaped) {
escCode = CharacterEscapes.ESCAPE_STANDARD;
break;
}
if (++ptr >= end) {
break;
}
}
int flushLen = (ptr - start);
if (flushLen > 0) {
_writer.write(_outputBuffer, start, flushLen);
if (ptr >= end) {
break output_loop;
}
}
++ptr;
start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCode);
}
}
private void _writeStringASCII(char[] text, int offset, int len,
final int maxNonEscaped)
throws IOException, JsonGenerationException
{
len += offset; // -> len marks the end from now on
final int[] escCodes = _outputEscapes;
final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
int escCode = 0;
while (offset < len) {
int start = offset;
char c;
while (true) {
c = text[offset];
if (c < escLimit) {
escCode = escCodes[c];
if (escCode != 0) {
break;
}
} else if (c > maxNonEscaped) {
escCode = CharacterEscapes.ESCAPE_STANDARD;
break;
}
if (++offset >= len) {
break;
}
}
// Short span? Better just copy it to buffer first:
int newAmount = offset - start;
if (newAmount < SHORT_WRITE) {
// Note: let's reserve room for escaped char (up to 6 chars)
if ((_outputTail + newAmount) > _outputEnd) {
_flushBuffer();
}
if (newAmount > 0) {
System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount);
_outputTail += newAmount;
}
} else { // Nope: better just write through
_flushBuffer();
_writer.write(text, start, newAmount);
}
// Was this the end?
if (offset >= len) { // yup
break;
}
// Nope, need to escape the char.
++offset;
_appendCharacterEscape(c, escCode);
}
}
/*
/**********************************************************
/* Internal methods, low-level writing, text segment
/* with custom escaping (possibly coupling with ASCII limits)
/**********************************************************
*/
/* Same as "_writeString2()", except needs additional escaping
* for subset of characters
*/
private void _writeStringCustom(final int len)
throws IOException, JsonGenerationException
{
// And then we'll need to verify need for escaping etc:
int end = _outputTail + len;
final int[] escCodes = _outputEscapes;
final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar;
final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
int escCode = 0;
final CharacterEscapes customEscapes = _characterEscapes;
output_loop:
while (_outputTail < end) {
char c;
// Fast loop for chars not needing escaping
escape_loop:
while (true) {
c = _outputBuffer[_outputTail];
if (c < escLimit) {
escCode = escCodes[c];
if (escCode != 0) {
break escape_loop;
}
} else if (c > maxNonEscaped) {
escCode = CharacterEscapes.ESCAPE_STANDARD;
break escape_loop;
} else {
if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) {
escCode = CharacterEscapes.ESCAPE_CUSTOM;
break escape_loop;
}
}
if (++_outputTail >= end) {
break output_loop;
}
}
int flushLen = (_outputTail - _outputHead);
if (flushLen > 0) {
_writer.write(_outputBuffer, _outputHead, flushLen);
}
++_outputTail;
_prependOrWriteCharacterEscape(c, escCode);
}
}
private void _writeSegmentCustom(int end)
throws IOException, JsonGenerationException
{
final int[] escCodes = _outputEscapes;
final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar;
final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
final CharacterEscapes customEscapes = _characterEscapes;
int ptr = 0;
int escCode = 0;
int start = ptr;
output_loop:
while (ptr < end) {
// Fast loop for chars not needing escaping
char c;
while (true) {
c = _outputBuffer[ptr];
if (c < escLimit) {
escCode = escCodes[c];
if (escCode != 0) {
break;
}
} else if (c > maxNonEscaped) {
escCode = CharacterEscapes.ESCAPE_STANDARD;
break;
} else {
if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) {
escCode = CharacterEscapes.ESCAPE_CUSTOM;
break;
}
}
if (++ptr >= end) {
break;
}
}
int flushLen = (ptr - start);
if (flushLen > 0) {
_writer.write(_outputBuffer, start, flushLen);
if (ptr >= end) {
break output_loop;
}
}
++ptr;
start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCode);
}
}
private void _writeStringCustom(char[] text, int offset, int len)
throws IOException, JsonGenerationException
{
len += offset; // -> len marks the end from now on
final int[] escCodes = _outputEscapes;
final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar;
final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
final CharacterEscapes customEscapes = _characterEscapes;
int escCode = 0;
while (offset < len) {
int start = offset;
char c;
while (true) {
c = text[offset];
if (c < escLimit) {
escCode = escCodes[c];
if (escCode != 0) {
break;
}
} else if (c > maxNonEscaped) {
escCode = CharacterEscapes.ESCAPE_STANDARD;
break;
} else {
if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) {
escCode = CharacterEscapes.ESCAPE_CUSTOM;
break;
}
}
if (++offset >= len) {
break;
}
}
// Short span? Better just copy it to buffer first:
int newAmount = offset - start;
if (newAmount < SHORT_WRITE) {
// Note: let's reserve room for escaped char (up to 6 chars)
if ((_outputTail + newAmount) > _outputEnd) {
_flushBuffer();
}
if (newAmount > 0) {
System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount);
_outputTail += newAmount;
}
} else { // Nope: better just write through
_flushBuffer();
_writer.write(text, start, newAmount);
}
// Was this the end?
if (offset >= len) { // yup
break;
}
// Nope, need to escape the char.
++offset;
_appendCharacterEscape(c, escCode);
}
}
/*
/**********************************************************
/* Internal methods, low-level writing; binary
/**********************************************************
*/
protected final void _writeBinary(Base64Variant b64variant, byte[] input, int inputPtr, final int inputEnd)
throws IOException, JsonGenerationException
{
// Encoding is by chunks of 3 input, 4 output chars, so:
int safeInputEnd = inputEnd - 3;
// Let's also reserve room for possible (and quoted) lf char each round
int safeOutputEnd = _outputEnd - 6;
int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
// Ok, first we loop through all full triplets of data:
while (inputPtr <= safeInputEnd) {
if (_outputTail > safeOutputEnd) { // need to flush
_flushBuffer();
}
// First, mash 3 bytes into lsb of 32-bit int
int b24 = ((int) input[inputPtr++]) << 8;
b24 |= ((int) input[inputPtr++]) & 0xFF;
b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF);
_outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
if (--chunksBeforeLF <= 0) {
// note: must quote in JSON value
_outputBuffer[_outputTail++] = '\\';
_outputBuffer[_outputTail++] = 'n';
chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
}
}
// And then we may have 1 or 2 leftover bytes to encode
int inputLeft = inputEnd - inputPtr; // 0, 1 or 2
if (inputLeft > 0) { // yes, but do we have room for output?
if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
_flushBuffer();
}
int b24 = ((int) input[inputPtr++]) << 16;
if (inputLeft == 2) {
b24 |= (((int) input[inputPtr++]) & 0xFF) << 8;
}
_outputTail = b64variant.encodeBase64Partial(b24, inputLeft, _outputBuffer, _outputTail);
}
}
// write-method called when length is definitely known
protected final int _writeBinary(Base64Variant b64variant,
InputStream data, byte[] readBuffer, int bytesLeft)
throws IOException, JsonGenerationException
{
int inputPtr = 0;
int inputEnd = 0;
int lastFullOffset = -3;
// Let's also reserve room for possible (and quoted) lf char each round
int safeOutputEnd = _outputEnd - 6;
int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
while (bytesLeft > 2) { // main loop for full triplets
if (inputPtr > lastFullOffset) {
inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft);
inputPtr = 0;
if (inputEnd < 3) { // required to try to read to have at least 3 bytes
break;
}
lastFullOffset = inputEnd-3;
}
if (_outputTail > safeOutputEnd) { // need to flush
_flushBuffer();
}
int b24 = ((int) readBuffer[inputPtr++]) << 8;
b24 |= ((int) readBuffer[inputPtr++]) & 0xFF;
b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF);
bytesLeft -= 3;
_outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
if (--chunksBeforeLF <= 0) {
_outputBuffer[_outputTail++] = '\\';
_outputBuffer[_outputTail++] = 'n';
chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
}
}
// And then we may have 1 or 2 leftover bytes to encode
if (bytesLeft > 0) {
inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft);
inputPtr = 0;
if (inputEnd > 0) { // yes, but do we have room for output?
if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
_flushBuffer();
}
int b24 = ((int) readBuffer[inputPtr++]) << 16;
int amount;
if (inputPtr < inputEnd) {
b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8;
amount = 2;
} else {
amount = 1;
}
_outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail);
bytesLeft -= amount;
}
}
return bytesLeft;
}
// write method when length is unknown
protected final int _writeBinary(Base64Variant b64variant,
InputStream data, byte[] readBuffer)
throws IOException, JsonGenerationException
{
int inputPtr = 0;
int inputEnd = 0;
int lastFullOffset = -3;
int bytesDone = 0;
// Let's also reserve room for possible (and quoted) LF char each round
int safeOutputEnd = _outputEnd - 6;
int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
// Ok, first we loop through all full triplets of data:
while (true) {
if (inputPtr > lastFullOffset) { // need to load more
inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, readBuffer.length);
inputPtr = 0;
if (inputEnd < 3) { // required to try to read to have at least 3 bytes
break;
}
lastFullOffset = inputEnd-3;
}
if (_outputTail > safeOutputEnd) { // need to flush
_flushBuffer();
}
// First, mash 3 bytes into lsb of 32-bit int
int b24 = ((int) readBuffer[inputPtr++]) << 8;
b24 |= ((int) readBuffer[inputPtr++]) & 0xFF;
b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF);
bytesDone += 3;
_outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
if (--chunksBeforeLF <= 0) {
_outputBuffer[_outputTail++] = '\\';
_outputBuffer[_outputTail++] = 'n';
chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
}
}
// And then we may have 1 or 2 leftover bytes to encode
if (inputPtr < inputEnd) { // yes, but do we have room for output?
if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
_flushBuffer();
}
int b24 = ((int) readBuffer[inputPtr++]) << 16;
int amount = 1;
if (inputPtr < inputEnd) {
b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8;
amount = 2;
}
bytesDone += amount;
_outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail);
}
return bytesDone;
}
private int _readMore(InputStream in,
byte[] readBuffer, int inputPtr, int inputEnd,
int maxRead) throws IOException
{
// anything to shift to front?
int i = 0;
while (inputPtr < inputEnd) {
readBuffer[i++] = readBuffer[inputPtr++];
}
inputPtr = 0;
inputEnd = i;
maxRead = Math.min(maxRead, readBuffer.length);
do {
int length = maxRead - inputEnd;
if (length == 0) {
break;
}
int count = in.read(readBuffer, inputEnd, length);
if (count < 0) {
return inputEnd;
}
inputEnd += count;
} while (inputEnd < 3);
return inputEnd;
}
/*
/**********************************************************
/* Internal methods, low-level writing, other
/**********************************************************
*/
private final void _writeNull() throws IOException
{
if ((_outputTail + 4) >= _outputEnd) {
_flushBuffer();
}
int ptr = _outputTail;
char[] buf = _outputBuffer;
buf[ptr] = 'n';
buf[++ptr] = 'u';
buf[++ptr] = 'l';
buf[++ptr] = 'l';
_outputTail = ptr+1;
}
/*
/**********************************************************
/* Internal methods, low-level writing, escapes
/**********************************************************
*/
/**
* Method called to try to either prepend character escape at front of
* given buffer; or if not possible, to write it out directly.
* Uses head and tail pointers (and updates as necessary)
*/
private void _prependOrWriteCharacterEscape(char ch, int escCode)
throws IOException, JsonGenerationException
{
if (escCode >= 0) { // \\N (2 char)
if (_outputTail >= 2) { // fits, just prepend
int ptr = _outputTail - 2;
_outputHead = ptr;
_outputBuffer[ptr++] = '\\';
_outputBuffer[ptr] = (char) escCode;
return;
}
// won't fit, write
char[] buf = _entityBuffer;
if (buf == null) {
buf = _allocateEntityBuffer();
}
_outputHead = _outputTail;
buf[1] = (char) escCode;
_writer.write(buf, 0, 2);
return;
}
if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX
if (_outputTail >= 6) { // fits, prepend to buffer
char[] buf = _outputBuffer;
int ptr = _outputTail - 6;
_outputHead = ptr;
buf[ptr] = '\\';
buf[++ptr] = 'u';
// We know it's a control char, so only the last 2 chars are non-0
if (ch > 0xFF) { // beyond 8 bytes
int hi = (ch >> 8) & 0xFF;
buf[++ptr] = HEX_CHARS[hi >> 4];
buf[++ptr] = HEX_CHARS[hi & 0xF];
ch &= 0xFF;
} else {
buf[++ptr] = '0';
buf[++ptr] = '0';
}
buf[++ptr] = HEX_CHARS[ch >> 4];
buf[++ptr] = HEX_CHARS[ch & 0xF];
return;
}
// won't fit, flush and write
char[] buf = _entityBuffer;
if (buf == null) {
buf = _allocateEntityBuffer();
}
_outputHead = _outputTail;
if (ch > 0xFF) { // beyond 8 bytes
int hi = (ch >> 8) & 0xFF;
int lo = ch & 0xFF;
buf[10] = HEX_CHARS[hi >> 4];
buf[11] = HEX_CHARS[hi & 0xF];
buf[12] = HEX_CHARS[lo >> 4];
buf[13] = HEX_CHARS[lo & 0xF];
_writer.write(buf, 8, 6);
} else { // We know it's a control char, so only the last 2 chars are non-0
buf[6] = HEX_CHARS[ch >> 4];
buf[7] = HEX_CHARS[ch & 0xF];
_writer.write(buf, 2, 6);
}
return;
}
String escape;
if (_currentEscape == null) {
escape = _characterEscapes.getEscapeSequence(ch).getValue();
} else {
escape = _currentEscape.getValue();
_currentEscape = null;
}
int len = escape.length();
if (_outputTail >= len) { // fits in, prepend
int ptr = _outputTail - len;
_outputHead = ptr;
escape.getChars(0, len, _outputBuffer, ptr);
return;
}
// won't fit, write separately
_outputHead = _outputTail;
_writer.write(escape);
}
/**
* Method called to try to either prepend character escape at front of
* given buffer; or if not possible, to write it out directly.
*
* @return Pointer to start of prepended entity (if prepended); or 'ptr'
* if not.
*/
private int _prependOrWriteCharacterEscape(char[] buffer, int ptr, int end,
char ch, int escCode)
throws IOException, JsonGenerationException
{
if (escCode >= 0) { // \\N (2 char)
if (ptr > 1 && ptr < end) { // fits, just prepend
ptr -= 2;
buffer[ptr] = '\\';
buffer[ptr+1] = (char) escCode;
} else { // won't fit, write
char[] ent = _entityBuffer;
if (ent == null) {
ent = _allocateEntityBuffer();
}
ent[1] = (char) escCode;
_writer.write(ent, 0, 2);
}
return ptr;
}
if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX
if (ptr > 5 && ptr < end) { // fits, prepend to buffer
ptr -= 6;
buffer[ptr++] = '\\';
buffer[ptr++] = 'u';
// We know it's a control char, so only the last 2 chars are non-0
if (ch > 0xFF) { // beyond 8 bytes
int hi = (ch >> 8) & 0xFF;
buffer[ptr++] = HEX_CHARS[hi >> 4];
buffer[ptr++] = HEX_CHARS[hi & 0xF];
ch &= 0xFF;
} else {
buffer[ptr++] = '0';
buffer[ptr++] = '0';
}
buffer[ptr++] = HEX_CHARS[ch >> 4];
buffer[ptr] = HEX_CHARS[ch & 0xF];
ptr -= 5;
} else {
// won't fit, flush and write
char[] ent = _entityBuffer;
if (ent == null) {
ent = _allocateEntityBuffer();
}
_outputHead = _outputTail;
if (ch > 0xFF) { // beyond 8 bytes
int hi = (ch >> 8) & 0xFF;
int lo = ch & 0xFF;
ent[10] = HEX_CHARS[hi >> 4];
ent[11] = HEX_CHARS[hi & 0xF];
ent[12] = HEX_CHARS[lo >> 4];
ent[13] = HEX_CHARS[lo & 0xF];
_writer.write(ent, 8, 6);
} else { // We know it's a control char, so only the last 2 chars are non-0
ent[6] = HEX_CHARS[ch >> 4];
ent[7] = HEX_CHARS[ch & 0xF];
_writer.write(ent, 2, 6);
}
}
return ptr;
}
String escape;
if (_currentEscape == null) {
escape = _characterEscapes.getEscapeSequence(ch).getValue();
} else {
escape = _currentEscape.getValue();
_currentEscape = null;
}
int len = escape.length();
if (ptr >= len && ptr < end) { // fits in, prepend
ptr -= len;
escape.getChars(0, len, buffer, ptr);
} else { // won't fit, write separately
_writer.write(escape);
}
return ptr;
}
/**
* Method called to append escape sequence for given character, at the
* end of standard output buffer; or if not possible, write out directly.
*/
private void _appendCharacterEscape(char ch, int escCode)
throws IOException, JsonGenerationException
{
if (escCode >= 0) { // \\N (2 char)
if ((_outputTail + 2) > _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '\\';
_outputBuffer[_outputTail++] = (char) escCode;
return;
}
if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX
if ((_outputTail + 5) >= _outputEnd) {
_flushBuffer();
}
int ptr = _outputTail;
char[] buf = _outputBuffer;
buf[ptr++] = '\\';
buf[ptr++] = 'u';
// We know it's a control char, so only the last 2 chars are non-0
if (ch > 0xFF) { // beyond 8 bytes
int hi = (ch >> 8) & 0xFF;
buf[ptr++] = HEX_CHARS[hi >> 4];
buf[ptr++] = HEX_CHARS[hi & 0xF];
ch &= 0xFF;
} else {
buf[ptr++] = '0';
buf[ptr++] = '0';
}
buf[ptr++] = HEX_CHARS[ch >> 4];
buf[ptr++] = HEX_CHARS[ch & 0xF];
_outputTail = ptr;
return;
}
String escape;
if (_currentEscape == null) {
escape = _characterEscapes.getEscapeSequence(ch).getValue();
} else {
escape = _currentEscape.getValue();
_currentEscape = null;
}
int len = escape.length();
if ((_outputTail + len) > _outputEnd) {
_flushBuffer();
if (len > _outputEnd) { // very very long escape; unlikely but theoretically possible
_writer.write(escape);
return;
}
}
escape.getChars(0, len, _outputBuffer, _outputTail);
_outputTail += len;
}
private char[] _allocateEntityBuffer()
{
char[] buf = new char[14];
// first 2 chars, non-numeric escapes (like \n)
buf[0] = '\\';
// next 6; 8-bit escapes (control chars mostly)
buf[2] = '\\';
buf[3] = 'u';
buf[4] = '0';
buf[5] = '0';
// last 6, beyond 8 bits
buf[8] = '\\';
buf[9] = 'u';
_entityBuffer = buf;
return buf;
}
/**
* @since 2.9
*/
private char[] _allocateCopyBuffer() {
if (_copyBuffer == null) {
_copyBuffer = _ioContext.allocNameCopyBuffer(2000);
}
return _copyBuffer;
}
protected void _flushBuffer() throws IOException
{
int len = _outputTail - _outputHead;
if (len > 0) {
int offset = _outputHead;
_outputTail = _outputHead = 0;
_writer.write(_outputBuffer, offset, len);
}
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/async/ 0000775 0000000 0000000 00000000000 13561642473 0030425 5 ustar 00root root 0000000 0000000 NonBlockingJsonParser.java 0000664 0000000 0000000 00000322271 13561642473 0035432 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/async package com.fasterxml.jackson.core.json.async;
import java.io.IOException;
import java.io.OutputStream;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.async.ByteArrayFeeder;
import com.fasterxml.jackson.core.async.NonBlockingInputFeeder;
import com.fasterxml.jackson.core.io.CharTypes;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
import com.fasterxml.jackson.core.util.VersionUtil;
public class NonBlockingJsonParser
extends NonBlockingJsonParserBase
implements ByteArrayFeeder
{
@SuppressWarnings("deprecation")
private final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask();
@SuppressWarnings("deprecation")
private final static int FEAT_MASK_LEADING_ZEROS = Feature.ALLOW_NUMERIC_LEADING_ZEROS.getMask();
@SuppressWarnings("deprecation")
private final static int FEAT_MASK_ALLOW_MISSING = Feature.ALLOW_MISSING_VALUES.getMask();
private final static int FEAT_MASK_ALLOW_SINGLE_QUOTES = Feature.ALLOW_SINGLE_QUOTES.getMask();
private final static int FEAT_MASK_ALLOW_UNQUOTED_NAMES = Feature.ALLOW_UNQUOTED_FIELD_NAMES.getMask();
private final static int FEAT_MASK_ALLOW_JAVA_COMMENTS = Feature.ALLOW_COMMENTS.getMask();
private final static int FEAT_MASK_ALLOW_YAML_COMMENTS = Feature.ALLOW_YAML_COMMENTS.getMask();
// This is the main input-code lookup table, fetched eagerly
private final static int[] _icUTF8 = CharTypes.getInputCodeUtf8();
// Latin1 encoding is not supported, but we do use 8-bit subset for
// pre-processing task, to simplify first pass, keep it fast.
protected final static int[] _icLatin1 = CharTypes.getInputCodeLatin1();
/*
/**********************************************************************
/* Input source config
/**********************************************************************
*/
/**
* This buffer is actually provided via {@link NonBlockingInputFeeder}
*/
protected byte[] _inputBuffer = NO_BYTES;
/**
* In addition to current buffer pointer, and end pointer,
* we will also need to know number of bytes originally
* contained. This is needed to correctly update location
* information when the block has been completed.
*/
protected int _origBufferLen;
// And from ParserBase:
// protected int _inputPtr;
// protected int _inputEnd;
/*
/**********************************************************************
/* Life-cycle
/**********************************************************************
*/
public NonBlockingJsonParser(IOContext ctxt, int parserFeatures,
ByteQuadsCanonicalizer sym)
{
super(ctxt, parserFeatures, sym);
}
/*
/**********************************************************************
/* AsyncInputFeeder impl
/**********************************************************************
*/
@Override
public ByteArrayFeeder getNonBlockingInputFeeder() {
return this;
}
@Override
public final boolean needMoreInput() {
return (_inputPtr >=_inputEnd) && !_endOfInput;
}
@Override
public void feedInput(byte[] buf, int start, int end) throws IOException
{
// Must not have remaining input
if (_inputPtr < _inputEnd) {
_reportError("Still have %d undecoded bytes, should not call 'feedInput'", _inputEnd - _inputPtr);
}
if (end < start) {
_reportError("Input end (%d) may not be before start (%d)", end, start);
}
// and shouldn't have been marked as end-of-input
if (_endOfInput) {
_reportError("Already closed, can not feed more input");
}
// Time to update pointers first
_currInputProcessed += _origBufferLen;
// Also need to adjust row start, to work as if it extended into the past wrt new buffer
_currInputRowStart = start - (_inputEnd - _currInputRowStart);
// And then update buffer settings
_currBufferStart = start;
_inputBuffer = buf;
_inputPtr = start;
_inputEnd = end;
_origBufferLen = end - start;
}
@Override
public void endOfInput() {
_endOfInput = true;
}
/*
/**********************************************************************
/* Abstract methods/overrides from JsonParser
/**********************************************************************
*/
/* Implementing these methods efficiently for non-blocking cases would
* be complicated; so for now let's just use the default non-optimized
* implementation
*/
// public boolean nextFieldName(SerializableString str) throws IOException
// public String nextTextValue() throws IOException
// public int nextIntValue(int defaultValue) throws IOException
// public long nextLongValue(long defaultValue) throws IOException
// public Boolean nextBooleanValue() throws IOException
@Override
public int releaseBuffered(OutputStream out) throws IOException {
int avail = _inputEnd - _inputPtr;
if (avail > 0) {
out.write(_inputBuffer, _inputPtr, avail);
}
return avail;
}
// Should never be called: can not be implemented quite as expected
// due to non-blocking behavior
@Override
protected char _decodeEscaped() throws IOException {
VersionUtil.throwInternal();
return ' ';
}
/*
/**********************************************************************
/* Main-level decoding
/**********************************************************************
*/
@Override
public JsonToken nextToken() throws IOException
{
// First: regardless of where we really are, need at least one more byte;
// can simplify some of the checks by short-circuiting right away
if (_inputPtr >= _inputEnd) {
if (_closed) {
return null;
}
// note: if so, do not even bother changing state
if (_endOfInput) { // except for this special case
// End-of-input within (possibly...) started token is bit complicated,
// so offline
if (_currToken == JsonToken.NOT_AVAILABLE) {
return _finishTokenWithEOF();
}
return _eofAsNextToken();
}
return JsonToken.NOT_AVAILABLE;
}
// in the middle of tokenization?
if (_currToken == JsonToken.NOT_AVAILABLE) {
return _finishToken();
}
// No: fresh new token; may or may not have existing one
_numTypesValid = NR_UNKNOWN;
_tokenInputTotal = _currInputProcessed + _inputPtr;
// also: clear any data retained so far
_binaryValue = null;
int ch = _inputBuffer[_inputPtr++] & 0xFF;
switch (_majorState) {
case MAJOR_INITIAL:
return _startDocument(ch);
case MAJOR_ROOT:
return _startValue(ch);
case MAJOR_OBJECT_FIELD_FIRST: // expect field-name or end-object
return _startFieldName(ch);
case MAJOR_OBJECT_FIELD_NEXT: // expect comma + field-name or end-object
return _startFieldNameAfterComma(ch);
case MAJOR_OBJECT_VALUE: // expect colon, followed by value
return _startValueExpectColon(ch);
case MAJOR_ARRAY_ELEMENT_FIRST: // expect value or end-array
return _startValue(ch);
case MAJOR_ARRAY_ELEMENT_NEXT: // expect leading comma + value or end-array
return _startValueExpectComma(ch);
default:
}
VersionUtil.throwInternal();
return null;
}
/**
* Method called when decoding of a token has been started, but not yet completed due
* to missing input; method is to continue decoding due to at least one more byte
* being made available to decode.
*/
protected final JsonToken _finishToken() throws IOException
{
// NOTE: caller ensures there's input available...
switch (_minorState) {
case MINOR_ROOT_BOM:
return _finishBOM(_pending32);
case MINOR_FIELD_LEADING_WS:
return _startFieldName(_inputBuffer[_inputPtr++] & 0xFF);
case MINOR_FIELD_LEADING_COMMA:
return _startFieldNameAfterComma(_inputBuffer[_inputPtr++] & 0xFF);
// Field name states
case MINOR_FIELD_NAME:
return _parseEscapedName(_quadLength, _pending32, _pendingBytes);
case MINOR_FIELD_NAME_ESCAPE:
return _finishFieldWithEscape();
case MINOR_FIELD_APOS_NAME:
return _finishAposName(_quadLength, _pending32, _pendingBytes);
case MINOR_FIELD_UNQUOTED_NAME:
return _finishUnquotedName(_quadLength, _pending32, _pendingBytes);
// Value states
case MINOR_VALUE_LEADING_WS:
return _startValue(_inputBuffer[_inputPtr++] & 0xFF);
case MINOR_VALUE_WS_AFTER_COMMA:
return _startValueAfterComma(_inputBuffer[_inputPtr++] & 0xFF);
case MINOR_VALUE_EXPECTING_COMMA:
return _startValueExpectComma(_inputBuffer[_inputPtr++] & 0xFF);
case MINOR_VALUE_EXPECTING_COLON:
return _startValueExpectColon(_inputBuffer[_inputPtr++] & 0xFF);
case MINOR_VALUE_TOKEN_NULL:
return _finishKeywordToken("null", _pending32, JsonToken.VALUE_NULL);
case MINOR_VALUE_TOKEN_TRUE:
return _finishKeywordToken("true", _pending32, JsonToken.VALUE_TRUE);
case MINOR_VALUE_TOKEN_FALSE:
return _finishKeywordToken("false", _pending32, JsonToken.VALUE_FALSE);
case MINOR_VALUE_TOKEN_NON_STD:
return _finishNonStdToken(_nonStdTokenType, _pending32);
case MINOR_NUMBER_MINUS:
return _finishNumberMinus(_inputBuffer[_inputPtr++] & 0xFF);
case MINOR_NUMBER_ZERO:
return _finishNumberLeadingZeroes();
case MINOR_NUMBER_MINUSZERO:
return _finishNumberLeadingNegZeroes();
case MINOR_NUMBER_INTEGER_DIGITS:
return _finishNumberIntegralPart(_textBuffer.getBufferWithoutReset(),
_textBuffer.getCurrentSegmentSize());
case MINOR_NUMBER_FRACTION_DIGITS:
return _finishFloatFraction();
case MINOR_NUMBER_EXPONENT_MARKER:
return _finishFloatExponent(true, _inputBuffer[_inputPtr++] & 0xFF);
case MINOR_NUMBER_EXPONENT_DIGITS:
return _finishFloatExponent(false, _inputBuffer[_inputPtr++] & 0xFF);
case MINOR_VALUE_STRING:
return _finishRegularString();
case MINOR_VALUE_STRING_UTF8_2:
_textBuffer.append((char) _decodeUTF8_2(_pending32, _inputBuffer[_inputPtr++]));
if (_minorStateAfterSplit == MINOR_VALUE_APOS_STRING) {
return _finishAposString();
}
return _finishRegularString();
case MINOR_VALUE_STRING_UTF8_3:
if (!_decodeSplitUTF8_3(_pending32, _pendingBytes, _inputBuffer[_inputPtr++])) {
return JsonToken.NOT_AVAILABLE;
}
if (_minorStateAfterSplit == MINOR_VALUE_APOS_STRING) {
return _finishAposString();
}
return _finishRegularString();
case MINOR_VALUE_STRING_UTF8_4:
if (!_decodeSplitUTF8_4(_pending32, _pendingBytes, _inputBuffer[_inputPtr++])) {
return JsonToken.NOT_AVAILABLE;
}
if (_minorStateAfterSplit == MINOR_VALUE_APOS_STRING) {
return _finishAposString();
}
return _finishRegularString();
case MINOR_VALUE_STRING_ESCAPE:
{
int c = _decodeSplitEscaped(_quoted32, _quotedDigits);
if (c < 0) {
return JsonToken.NOT_AVAILABLE;
}
_textBuffer.append((char) c);
}
if (_minorStateAfterSplit == MINOR_VALUE_APOS_STRING) {
return _finishAposString();
}
return _finishRegularString();
case MINOR_VALUE_APOS_STRING:
return _finishAposString();
case MINOR_VALUE_TOKEN_ERROR: // case of "almost token", just need tokenize for error
return _finishErrorToken();
// Comments
case MINOR_COMMENT_LEADING_SLASH:
return _startSlashComment(_pending32);
case MINOR_COMMENT_CLOSING_ASTERISK:
return _finishCComment(_pending32, true);
case MINOR_COMMENT_C:
return _finishCComment(_pending32, false);
case MINOR_COMMENT_CPP:
return _finishCppComment(_pending32);
case MINOR_COMMENT_YAML:
return _finishHashComment(_pending32);
}
VersionUtil.throwInternal();
return null;
}
/**
* Method similar to {@link #_finishToken}, but called when no more input is
* available, and end-of-input has been detected. This is usually problem
* case, but not always: root-level values may be properly terminated by
* this, and similarly trailing white-space may have been skipped.
*/
protected final JsonToken _finishTokenWithEOF() throws IOException
{
// NOTE: caller ensures there's input available...
JsonToken t = _currToken;
switch (_minorState) {
case MINOR_ROOT_GOT_SEPARATOR: // fine, just skip some trailing space
return _eofAsNextToken();
case MINOR_VALUE_LEADING_WS: // finished at token boundary; probably fine
return _eofAsNextToken();
// case MINOR_VALUE_EXPECTING_COMMA: // not fine
// case MINOR_VALUE_EXPECTING_COLON: // not fine
case MINOR_VALUE_TOKEN_NULL:
return _finishKeywordTokenWithEOF("null", _pending32, JsonToken.VALUE_NULL);
case MINOR_VALUE_TOKEN_TRUE:
return _finishKeywordTokenWithEOF("true", _pending32, JsonToken.VALUE_TRUE);
case MINOR_VALUE_TOKEN_FALSE:
return _finishKeywordTokenWithEOF("false", _pending32, JsonToken.VALUE_FALSE);
case MINOR_VALUE_TOKEN_NON_STD:
return _finishNonStdTokenWithEOF(_nonStdTokenType, _pending32);
case MINOR_VALUE_TOKEN_ERROR: // case of "almost token", just need tokenize for error
return _finishErrorTokenWithEOF();
// Number-parsing states; valid stopping points, more explicit errors
case MINOR_NUMBER_ZERO:
case MINOR_NUMBER_MINUSZERO:
// NOTE: does NOT retain possible leading minus-sign (can change if
// absolutely needs be)
return _valueCompleteInt(0, "0");
case MINOR_NUMBER_INTEGER_DIGITS:
// Fine: just need to ensure we have value fully defined
{
int len = _textBuffer.getCurrentSegmentSize();
if (_numberNegative) {
--len;
}
_intLength = len;
}
return _valueComplete(JsonToken.VALUE_NUMBER_INT);
case MINOR_NUMBER_FRACTION_DIGITS:
_expLength = 0;
// fall through
case MINOR_NUMBER_EXPONENT_DIGITS:
return _valueComplete(JsonToken.VALUE_NUMBER_FLOAT);
case MINOR_NUMBER_EXPONENT_MARKER:
_reportInvalidEOF(": was expecting fraction after exponent marker", JsonToken.VALUE_NUMBER_FLOAT);
// How about comments?
// Inside C-comments; not legal
// case MINOR_COMMENT_LEADING_SLASH: // not legal, but use default error
case MINOR_COMMENT_CLOSING_ASTERISK:
case MINOR_COMMENT_C:
_reportInvalidEOF(": was expecting closing '*/' for comment", JsonToken.NOT_AVAILABLE);
case MINOR_COMMENT_CPP:
case MINOR_COMMENT_YAML:
// within C++/YAML comments, ok, as long as major state agrees...
return _eofAsNextToken();
default:
}
_reportInvalidEOF(": was expecting rest of token (internal state: "+_minorState+")", _currToken);
return t; // never gets here
}
/*
/**********************************************************************
/* Second-level decoding, root level
/**********************************************************************
*/
private final JsonToken _startDocument(int ch) throws IOException
{
ch &= 0xFF;
// Very first byte: could be BOM
if ((ch == 0xEF) && (_minorState != MINOR_ROOT_BOM)) {
return _finishBOM(1);
}
// If not BOM (or we got past it), could be whitespace or comment to skip
while (ch <= 0x020) {
if (ch != INT_SPACE) {
if (ch == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (ch == INT_CR) {
++_currInputRowAlt;
_currInputRowStart = _inputPtr;
} else if (ch != INT_TAB) {
_throwInvalidSpace(ch);
}
}
if (_inputPtr >= _inputEnd) {
_minorState = MINOR_ROOT_GOT_SEPARATOR;
if (_closed) {
return null;
}
// note: if so, do not even bother changing state
if (_endOfInput) { // except for this special case
return _eofAsNextToken();
}
return JsonToken.NOT_AVAILABLE;
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
}
return _startValue(ch);
}
private final JsonToken _finishBOM(int bytesHandled) throws IOException
{
// public final static byte UTF8_BOM_1 = (byte) 0xEF;
// public final static byte UTF8_BOM_2 = (byte) 0xBB;
// public final static byte UTF8_BOM_3 = (byte) 0xBF;
while (_inputPtr < _inputEnd) {
int ch = _inputBuffer[_inputPtr++] & 0xFF;
switch (bytesHandled) {
case 3:
// got it all; go back to "start document" handling, without changing
// minor state (to let it know we've done BOM)
_currInputProcessed -= 3;
return _startDocument(ch);
case 2:
if (ch != 0xBF) {
_reportError("Unexpected byte 0x%02x following 0xEF 0xBB; should get 0xBF as third byte of UTF-8 BOM", ch);
}
break;
case 1:
if (ch != 0xBB) {
_reportError("Unexpected byte 0x%02x following 0xEF; should get 0xBB as second byte UTF-8 BOM", ch);
}
break;
}
++bytesHandled;
}
_pending32 = bytesHandled;
_minorState = MINOR_ROOT_BOM;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
/*
/**********************************************************************
/* Second-level decoding, primary field name decoding
/**********************************************************************
*/
/**
* Method that handles initial token type recognition for token
* that has to be either FIELD_NAME or END_OBJECT.
*/
private final JsonToken _startFieldName(int ch) throws IOException
{
// First: any leading white space?
if (ch <= 0x0020) {
ch = _skipWS(ch);
if (ch <= 0) {
_minorState = MINOR_FIELD_LEADING_WS;
return _currToken;
}
}
_updateTokenLocation();
if (ch != INT_QUOTE) {
if (ch == INT_RCURLY) {
return _closeObjectScope();
}
return _handleOddName(ch);
}
// First: can we optimize out bounds checks?
if ((_inputPtr + 13) <= _inputEnd) { // Need up to 12 chars, plus one trailing (quote)
String n = _fastParseName();
if (n != null) {
return _fieldComplete(n);
}
}
return _parseEscapedName(0, 0, 0);
}
private final JsonToken _startFieldNameAfterComma(int ch) throws IOException
{
// First: any leading white space?
if (ch <= 0x0020) {
ch = _skipWS(ch); // will skip through all available ws (and comments)
if (ch <= 0) {
_minorState = MINOR_FIELD_LEADING_COMMA;
return _currToken;
}
}
if (ch != INT_COMMA) { // either comma, separating entries, or closing right curly
if (ch == INT_RCURLY) {
return _closeObjectScope();
}
if (ch == INT_HASH) {
return _finishHashComment(MINOR_FIELD_LEADING_COMMA);
}
if (ch == INT_SLASH) {
return _startSlashComment(MINOR_FIELD_LEADING_COMMA);
}
_reportUnexpectedChar(ch, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
int ptr = _inputPtr;
if (ptr >= _inputEnd) {
_minorState = MINOR_FIELD_LEADING_WS;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
ch = _inputBuffer[ptr];
_inputPtr = ptr+1;
if (ch <= 0x0020) {
ch = _skipWS(ch);
if (ch <= 0) {
_minorState = MINOR_FIELD_LEADING_WS;
return _currToken;
}
}
_updateTokenLocation();
if (ch != INT_QUOTE) {
if (ch == INT_RCURLY) {
if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
return _closeObjectScope();
}
}
return _handleOddName(ch);
}
// First: can we optimize out bounds checks?
if ((_inputPtr + 13) <= _inputEnd) { // Need up to 12 chars, plus one trailing (quote)
String n = _fastParseName();
if (n != null) {
return _fieldComplete(n);
}
}
return _parseEscapedName(0, 0, 0);
}
/*
/**********************************************************************
/* Second-level decoding, value decoding
/**********************************************************************
*/
/**
* Helper method called to detect type of a value token (at any level), and possibly
* decode it if contained in input buffer.
* Value may be preceded by leading white-space, but no separator (comma).
*/
private final JsonToken _startValue(int ch) throws IOException
{
// First: any leading white space?
if (ch <= 0x0020) {
ch = _skipWS(ch);
if (ch <= 0) {
_minorState = MINOR_VALUE_LEADING_WS;
return _currToken;
}
}
_updateTokenLocation();
// 17-Sep-2019, tatu: [core#563] Need to call this to update index within array
_parsingContext.expectComma();
if (ch == INT_QUOTE) {
return _startString();
}
switch (ch) {
case '#':
return _finishHashComment(MINOR_VALUE_LEADING_WS);
case '-':
return _startNegativeNumber();
case '/': // c/c++ comments
return _startSlashComment(MINOR_VALUE_LEADING_WS);
// Should we have separate handling for plus? Although
// it is not allowed per se, it may be erroneously used,
// and could be indicate by a more specific error message.
case '0':
return _startNumberLeadingZero();
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return _startPositiveNumber(ch);
case 'f':
return _startFalseToken();
case 'n':
return _startNullToken();
case 't':
return _startTrueToken();
case '[':
return _startArrayScope();
case INT_RBRACKET:
return _closeArrayScope();
case '{':
return _startObjectScope();
case INT_RCURLY:
return _closeObjectScope();
default:
}
return _startUnexpectedValue(false, ch);
}
/**
* Helper method called to parse token that is either a value token in array
* or end-array marker
*/
private final JsonToken _startValueExpectComma(int ch) throws IOException
{
// First: any leading white space?
if (ch <= 0x0020) {
ch = _skipWS(ch); // will skip through all available ws (and comments)
if (ch <= 0) {
_minorState = MINOR_VALUE_EXPECTING_COMMA;
return _currToken;
}
}
if (ch != INT_COMMA) {
if (ch == INT_RBRACKET) {
return _closeArrayScope();
}
if (ch == INT_RCURLY){
return _closeObjectScope();
}
if (ch == INT_SLASH) {
return _startSlashComment(MINOR_VALUE_EXPECTING_COMMA);
}
if (ch == INT_HASH) {
return _finishHashComment(MINOR_VALUE_EXPECTING_COMMA);
}
_reportUnexpectedChar(ch, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
// 17-Sep-2019, tatu: [core#563] Need to call this to update index within array
_parsingContext.expectComma();
int ptr = _inputPtr;
if (ptr >= _inputEnd) {
_minorState = MINOR_VALUE_WS_AFTER_COMMA;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
ch = _inputBuffer[ptr];
_inputPtr = ptr+1;
if (ch <= 0x0020) {
ch = _skipWS(ch);
if (ch <= 0) {
_minorState = MINOR_VALUE_WS_AFTER_COMMA;
return _currToken;
}
}
_updateTokenLocation();
if (ch == INT_QUOTE) {
return _startString();
}
switch (ch) {
case '#':
return _finishHashComment(MINOR_VALUE_WS_AFTER_COMMA);
case '-':
return _startNegativeNumber();
case '/':
return _startSlashComment(MINOR_VALUE_WS_AFTER_COMMA);
// Should we have separate handling for plus? Although
// it is not allowed per se, it may be erroneously used,
// and could be indicate by a more specific error message.
case '0':
return _startNumberLeadingZero();
case '1':
case '2': case '3':
case '4': case '5':
case '6': case '7':
case '8': case '9':
return _startPositiveNumber(ch);
case 'f':
return _startFalseToken();
case 'n':
return _startNullToken();
case 't':
return _startTrueToken();
case '[':
return _startArrayScope();
case INT_RBRACKET:
// Was that a trailing comma?
if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
return _closeArrayScope();
}
break;
case '{':
return _startObjectScope();
case INT_RCURLY:
// Was that a trailing comma?
if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
return _closeObjectScope();
}
break;
default:
}
return _startUnexpectedValue(true, ch);
}
/**
* Helper method called to detect type of a value token (at any level), and possibly
* decode it if contained in input buffer.
* Value MUST be preceded by a semi-colon (which may be surrounded by white-space)
*/
private final JsonToken _startValueExpectColon(int ch) throws IOException
{
// First: any leading white space?
if (ch <= 0x0020) {
ch = _skipWS(ch); // will skip through all available ws (and comments)
if (ch <= 0) {
_minorState = MINOR_VALUE_EXPECTING_COLON;
return _currToken;
}
}
if (ch != INT_COLON) {
if (ch == INT_SLASH) {
return _startSlashComment(MINOR_VALUE_EXPECTING_COLON);
}
if (ch == INT_HASH) {
return _finishHashComment(MINOR_VALUE_EXPECTING_COLON);
}
// can not omit colon here
_reportUnexpectedChar(ch, "was expecting a colon to separate field name and value");
}
int ptr = _inputPtr;
if (ptr >= _inputEnd) {
_minorState = MINOR_VALUE_LEADING_WS;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
ch = _inputBuffer[ptr];
_inputPtr = ptr+1;
if (ch <= 0x0020) {
ch = _skipWS(ch); // will skip through all available ws (and comments)
if (ch <= 0) {
_minorState = MINOR_VALUE_LEADING_WS;
return _currToken;
}
}
_updateTokenLocation();
if (ch == INT_QUOTE) {
return _startString();
}
switch (ch) {
case '#':
return _finishHashComment(MINOR_VALUE_LEADING_WS);
case '-':
return _startNegativeNumber();
case '/':
return _startSlashComment(MINOR_VALUE_LEADING_WS);
// Should we have separate handling for plus? Although
// it is not allowed per se, it may be erroneously used,
// and could be indicate by a more specific error message.
case '0':
return _startNumberLeadingZero();
case '1':
case '2': case '3':
case '4': case '5':
case '6': case '7':
case '8': case '9':
return _startPositiveNumber(ch);
case 'f':
return _startFalseToken();
case 'n':
return _startNullToken();
case 't':
return _startTrueToken();
case '[':
return _startArrayScope();
case '{':
return _startObjectScope();
default:
}
return _startUnexpectedValue(false, ch);
}
/* Method called when we have already gotten a comma (i.e. not the first value)
*/
private final JsonToken _startValueAfterComma(int ch) throws IOException
{
// First: any leading white space?
if (ch <= 0x0020) {
ch = _skipWS(ch);
if (ch <= 0) {
_minorState = MINOR_VALUE_WS_AFTER_COMMA;
return _currToken;
}
}
_updateTokenLocation();
if (ch == INT_QUOTE) {
return _startString();
}
switch (ch) {
case '#':
return _finishHashComment(MINOR_VALUE_WS_AFTER_COMMA);
case '-':
return _startNegativeNumber();
case '/':
return _startSlashComment(MINOR_VALUE_WS_AFTER_COMMA);
// Should we have separate handling for plus? Although
// it is not allowed per se, it may be erroneously used,
// and could be indicate by a more specific error message.
case '0':
return _startNumberLeadingZero();
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return _startPositiveNumber(ch);
case 'f':
return _startFalseToken();
case 'n':
return _startNullToken();
case 't':
return _startTrueToken();
case '[':
return _startArrayScope();
case INT_RBRACKET:
// Was that a trailing comma?
if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
return _closeArrayScope();
}
break;
case '{':
return _startObjectScope();
case INT_RCURLY:
// Was that a trailing comma?
if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
return _closeObjectScope();
}
break;
default:
}
return _startUnexpectedValue(true, ch);
}
protected JsonToken _startUnexpectedValue(boolean leadingComma, int ch) throws IOException
{
switch (ch) {
case INT_RBRACKET:
if (!_parsingContext.inArray()) {
break;
}
// fall through
case ',':
// 28-Mar-2016: [core#116]: If Feature.ALLOW_MISSING_VALUES is enabled
// we may allow "missing values", that is, encountering a trailing
// comma or closing marker where value would be expected
if ((_features & FEAT_MASK_ALLOW_MISSING) != 0) {
--_inputPtr;
return _valueComplete(JsonToken.VALUE_NULL);
}
// fall through
case INT_RCURLY:
// Error: neither is valid at this point; valid closers have
// been handled earlier
break;
case '\'':
if ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) {
return _startAposString();
}
break;
case '+':
return _finishNonStdToken(NON_STD_TOKEN_PLUS_INFINITY, 1);
case 'N':
return _finishNonStdToken(NON_STD_TOKEN_NAN, 1);
case 'I':
return _finishNonStdToken(NON_STD_TOKEN_INFINITY, 1);
}
// !!! TODO: maybe try to collect more information for better diagnostics
_reportUnexpectedChar(ch, "expected a valid value "+_validJsonValueList());
return null;
}
/*
/**********************************************************************
/* Second-level decoding, skipping white-space, comments
/**********************************************************************
*/
private final int _skipWS(int ch) throws IOException
{
do {
if (ch != INT_SPACE) {
if (ch == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (ch == INT_CR) {
++_currInputRowAlt;
_currInputRowStart = _inputPtr;
} else if (ch != INT_TAB) {
_throwInvalidSpace(ch);
}
}
if (_inputPtr >= _inputEnd) {
_currToken = JsonToken.NOT_AVAILABLE;
return 0;
}
ch = _inputBuffer[_inputPtr++] & 0xFF;
} while (ch <= 0x0020);
return ch;
}
private final JsonToken _startSlashComment(int fromMinorState) throws IOException
{
if ((_features & FEAT_MASK_ALLOW_JAVA_COMMENTS) == 0) {
_reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)");
}
// After that, need to verify if we have c/c++ comment
if (_inputPtr >= _inputEnd) {
_pending32 = fromMinorState;
_minorState = MINOR_COMMENT_LEADING_SLASH;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
int ch = _inputBuffer[_inputPtr++];
if (ch == INT_ASTERISK) { // c-style
return _finishCComment(fromMinorState, false);
}
if (ch == INT_SLASH) { // c++-style
return _finishCppComment(fromMinorState);
}
_reportUnexpectedChar(ch & 0xFF, "was expecting either '*' or '/' for a comment");
return null;
}
private final JsonToken _finishHashComment(int fromMinorState) throws IOException
{
// Could by-pass this check by refactoring, but for now simplest way...
if ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) == 0) {
_reportUnexpectedChar('#', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_YAML_COMMENTS' not enabled for parser)");
}
while (true) {
if (_inputPtr >= _inputEnd) {
_minorState = MINOR_COMMENT_YAML;
_pending32 = fromMinorState;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
int ch = _inputBuffer[_inputPtr++] & 0xFF;
if (ch < 0x020) {
if (ch == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
break;
} else if (ch == INT_CR) {
++_currInputRowAlt;
_currInputRowStart = _inputPtr;
break;
} else if (ch != INT_TAB) {
_throwInvalidSpace(ch);
}
}
}
return _startAfterComment(fromMinorState);
}
private final JsonToken _finishCppComment(int fromMinorState) throws IOException
{
while (true) {
if (_inputPtr >= _inputEnd) {
_minorState = MINOR_COMMENT_CPP;
_pending32 = fromMinorState;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
int ch = _inputBuffer[_inputPtr++] & 0xFF;
if (ch < 0x020) {
if (ch == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
break;
} else if (ch == INT_CR) {
++_currInputRowAlt;
_currInputRowStart = _inputPtr;
break;
} else if (ch != INT_TAB) {
_throwInvalidSpace(ch);
}
}
}
return _startAfterComment(fromMinorState);
}
private final JsonToken _finishCComment(int fromMinorState, boolean gotStar) throws IOException
{
while (true) {
if (_inputPtr >= _inputEnd) {
_minorState = gotStar ? MINOR_COMMENT_CLOSING_ASTERISK : MINOR_COMMENT_C;
_pending32 = fromMinorState;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
int ch = _inputBuffer[_inputPtr++] & 0xFF;
if (ch < 0x020) {
if (ch == INT_LF) {
++_currInputRow;
_currInputRowStart = _inputPtr;
} else if (ch == INT_CR) {
++_currInputRowAlt;
_currInputRowStart = _inputPtr;
} else if (ch != INT_TAB) {
_throwInvalidSpace(ch);
}
} else if (ch == INT_ASTERISK) {
gotStar = true;
continue;
} else if (ch == INT_SLASH) {
if (gotStar) {
break;
}
}
gotStar = false;
}
return _startAfterComment(fromMinorState);
}
private final JsonToken _startAfterComment(int fromMinorState) throws IOException
{
// Ok, then, need one more character...
if (_inputPtr >= _inputEnd) {
_minorState = fromMinorState;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
int ch = _inputBuffer[_inputPtr++] & 0xFF;
switch (fromMinorState) {
case MINOR_FIELD_LEADING_WS:
return _startFieldName(ch);
case MINOR_FIELD_LEADING_COMMA:
return _startFieldNameAfterComma(ch);
case MINOR_VALUE_LEADING_WS:
return _startValue(ch);
case MINOR_VALUE_EXPECTING_COMMA:
return _startValueExpectComma(ch);
case MINOR_VALUE_EXPECTING_COLON:
return _startValueExpectColon(ch);
case MINOR_VALUE_WS_AFTER_COMMA:
return _startValueAfterComma(ch);
default:
}
VersionUtil.throwInternal();
return null;
}
/*
/**********************************************************************
/* Tertiary decoding, simple tokens
/**********************************************************************
*/
protected JsonToken _startFalseToken() throws IOException
{
int ptr = _inputPtr;
if ((ptr + 4) < _inputEnd) { // yes, can determine efficiently
byte[] buf = _inputBuffer;
if ((buf[ptr++] == 'a')
&& (buf[ptr++] == 'l')
&& (buf[ptr++] == 's')
&& (buf[ptr++] == 'e')) {
int ch = buf[ptr] & 0xFF;
if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
_inputPtr = ptr;
return _valueComplete(JsonToken.VALUE_FALSE);
}
}
}
_minorState = MINOR_VALUE_TOKEN_FALSE;
return _finishKeywordToken("false", 1, JsonToken.VALUE_FALSE);
}
protected JsonToken _startTrueToken() throws IOException
{
int ptr = _inputPtr;
if ((ptr + 3) < _inputEnd) { // yes, can determine efficiently
byte[] buf = _inputBuffer;
if ((buf[ptr++] == 'r')
&& (buf[ptr++] == 'u')
&& (buf[ptr++] == 'e')) {
int ch = buf[ptr] & 0xFF;
if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
_inputPtr = ptr;
return _valueComplete(JsonToken.VALUE_TRUE);
}
}
}
_minorState = MINOR_VALUE_TOKEN_TRUE;
return _finishKeywordToken("true", 1, JsonToken.VALUE_TRUE);
}
protected JsonToken _startNullToken() throws IOException
{
int ptr = _inputPtr;
if ((ptr + 3) < _inputEnd) { // yes, can determine efficiently
byte[] buf = _inputBuffer;
if ((buf[ptr++] == 'u')
&& (buf[ptr++] == 'l')
&& (buf[ptr++] == 'l')) {
int ch = buf[ptr] & 0xFF;
if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
_inputPtr = ptr;
return _valueComplete(JsonToken.VALUE_NULL);
}
}
}
_minorState = MINOR_VALUE_TOKEN_NULL;
return _finishKeywordToken("null", 1, JsonToken.VALUE_NULL);
}
protected JsonToken _finishKeywordToken(String expToken, int matched,
JsonToken result) throws IOException
{
final int end = expToken.length();
while (true) {
if (_inputPtr >= _inputEnd) {
_pending32 = matched;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
int ch = _inputBuffer[_inputPtr];
if (matched == end) { // need to verify trailing separator
if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
return _valueComplete(result);
}
break;
}
if (ch != expToken.charAt(matched)) {
break;
}
++matched;
++_inputPtr;
}
_minorState = MINOR_VALUE_TOKEN_ERROR;
_textBuffer.resetWithCopy(expToken, 0, matched);
return _finishErrorToken();
}
protected JsonToken _finishKeywordTokenWithEOF(String expToken, int matched,
JsonToken result) throws IOException
{
if (matched == expToken.length()) {
return (_currToken = result);
}
_textBuffer.resetWithCopy(expToken, 0, matched);
return _finishErrorTokenWithEOF();
}
protected JsonToken _finishNonStdToken(int type, int matched) throws IOException
{
final String expToken = _nonStdToken(type);
final int end = expToken.length();
while (true) {
if (_inputPtr >= _inputEnd) {
_nonStdTokenType = type;
_pending32 = matched;
_minorState = MINOR_VALUE_TOKEN_NON_STD;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
int ch = _inputBuffer[_inputPtr];
if (matched == end) { // need to verify trailing separator
if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
return _valueNonStdNumberComplete(type);
}
break;
}
if (ch != expToken.charAt(matched)) {
break;
}
++matched;
++_inputPtr;
}
_minorState = MINOR_VALUE_TOKEN_ERROR;
_textBuffer.resetWithCopy(expToken, 0, matched);
return _finishErrorToken();
}
protected JsonToken _finishNonStdTokenWithEOF(int type, int matched) throws IOException
{
final String expToken = _nonStdToken(type);
if (matched == expToken.length()) {
return _valueNonStdNumberComplete(type);
}
_textBuffer.resetWithCopy(expToken, 0, matched);
return _finishErrorTokenWithEOF();
}
protected JsonToken _finishErrorToken() throws IOException
{
while (_inputPtr < _inputEnd) {
int i = (int) _inputBuffer[_inputPtr++];
// !!! TODO: Decode UTF-8 characters properly...
// char c = (char) _decodeCharForError(i);
char ch = (char) i;
if (Character.isJavaIdentifierPart(ch)) {
// 11-Jan-2016, tatu: note: we will fully consume the character,
// included or not, so if recovery was possible, it'd be off-by-one...
_textBuffer.append(ch);
if (_textBuffer.size() < MAX_ERROR_TOKEN_LENGTH) {
continue;
}
}
return _reportErrorToken(_textBuffer.contentsAsString());
}
return (_currToken = JsonToken.NOT_AVAILABLE);
}
protected JsonToken _finishErrorTokenWithEOF() throws IOException
{
return _reportErrorToken(_textBuffer.contentsAsString());
}
protected JsonToken _reportErrorToken(String actualToken) throws IOException
{
// !!! TODO: Include non-standard ones if enabled
_reportError("Unrecognized token '%s': was expecting %s", _textBuffer.contentsAsString(),
_validJsonTokenList());
return JsonToken.NOT_AVAILABLE; // never gets here
}
/*
/**********************************************************************
/* Second-level decoding, Number decoding
/**********************************************************************
*/
protected JsonToken _startPositiveNumber(int ch) throws IOException
{
_numberNegative = false;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
outBuf[0] = (char) ch;
// in unlikely event of not having more input, denote location
if (_inputPtr >= _inputEnd) {
_minorState = MINOR_NUMBER_INTEGER_DIGITS;
_textBuffer.setCurrentLength(1);
return (_currToken = JsonToken.NOT_AVAILABLE);
}
int outPtr = 1;
ch = _inputBuffer[_inputPtr] & 0xFF;
while (true) {
if (ch < INT_0) {
if (ch == INT_PERIOD) {
_intLength = outPtr;
++_inputPtr;
return _startFloat(outBuf, outPtr, ch);
}
break;
}
if (ch > INT_9) {
if (ch == INT_e || ch == INT_E) {
_intLength = outPtr;
++_inputPtr;
return _startFloat(outBuf, outPtr, ch);
}
break;
}
if (outPtr >= outBuf.length) {
// NOTE: must expand to ensure contents all in a single buffer (to keep
// other parts of parsing simpler)
outBuf = _textBuffer.expandCurrentSegment();
}
outBuf[outPtr++] = (char) ch;
if (++_inputPtr >= _inputEnd) {
_minorState = MINOR_NUMBER_INTEGER_DIGITS;
_textBuffer.setCurrentLength(outPtr);
return (_currToken = JsonToken.NOT_AVAILABLE);
}
ch = _inputBuffer[_inputPtr] & 0xFF;
}
_intLength = outPtr;
_textBuffer.setCurrentLength(outPtr);
return _valueComplete(JsonToken.VALUE_NUMBER_INT);
}
protected JsonToken _startNegativeNumber() throws IOException
{
_numberNegative = true;
if (_inputPtr >= _inputEnd) {
_minorState = MINOR_NUMBER_MINUS;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
int ch = _inputBuffer[_inputPtr++] & 0xFF;
if (ch <= INT_0) {
if (ch == INT_0) {
return _finishNumberLeadingNegZeroes();
}
// One special case: if first char is 0, must not be followed by a digit
reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value");
} else if (ch > INT_9) {
if (ch == 'I') {
return _finishNonStdToken(NON_STD_TOKEN_MINUS_INFINITY, 2);
}
reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value");
}
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
outBuf[0] = '-';
outBuf[1] = (char) ch;
if (_inputPtr >= _inputEnd) {
_minorState = MINOR_NUMBER_INTEGER_DIGITS;
_textBuffer.setCurrentLength(2);
_intLength = 1;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
ch = _inputBuffer[_inputPtr];
int outPtr = 2;
while (true) {
if (ch < INT_0) {
if (ch == INT_PERIOD) {
_intLength = outPtr-1;
++_inputPtr;
return _startFloat(outBuf, outPtr, ch);
}
break;
}
if (ch > INT_9) {
if (ch == INT_e || ch == INT_E) {
_intLength = outPtr-1;
++_inputPtr;
return _startFloat(outBuf, outPtr, ch);
}
break;
}
if (outPtr >= outBuf.length) {
// NOTE: must expand, to ensure contiguous buffer, outPtr is the length
outBuf = _textBuffer.expandCurrentSegment();
}
outBuf[outPtr++] = (char) ch;
if (++_inputPtr >= _inputEnd) {
_minorState = MINOR_NUMBER_INTEGER_DIGITS;
_textBuffer.setCurrentLength(outPtr);
return (_currToken = JsonToken.NOT_AVAILABLE);
}
ch = _inputBuffer[_inputPtr] & 0xFF;
}
_intLength = outPtr-1;
_textBuffer.setCurrentLength(outPtr);
return _valueComplete(JsonToken.VALUE_NUMBER_INT);
}
protected JsonToken _startNumberLeadingZero() throws IOException
{
int ptr = _inputPtr;
if (ptr >= _inputEnd) {
_minorState = MINOR_NUMBER_ZERO;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
// While we could call `_finishNumberLeadingZeroes()`, let's try checking
// the very first char after first zero since the most common case is that
// there is a separator
int ch = _inputBuffer[ptr++] & 0xFF;
// one early check: leading zeroes may or may not be allowed
if (ch < INT_0) {
if (ch == INT_PERIOD) {
_inputPtr = ptr;
_intLength = 1;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
outBuf[0] = '0';
return _startFloat(outBuf, 1, ch);
}
} else if (ch > INT_9) {
if (ch == INT_e || ch == INT_E) {
_inputPtr = ptr;
_intLength = 1;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
outBuf[0] = '0';
return _startFloat(outBuf, 1, ch);
}
// Ok; unfortunately we have closing bracket/curly that are valid so need
// (colon not possible since this is within value, not after key)
//
if ((ch != INT_RBRACKET) && (ch != INT_RCURLY)) {
reportUnexpectedNumberChar(ch,
"expected digit (0-9), decimal point (.) or exponent indicator (e/E) to follow '0'");
}
} else { // leading zero case (zero followed by a digit)
// leave inputPtr as is (i.e. "push back" digit)
return _finishNumberLeadingZeroes();
}
// leave _inputPtr as-is, to push back byte we checked
return _valueCompleteInt(0, "0");
}
protected JsonToken _finishNumberMinus(int ch) throws IOException
{
if (ch <= INT_0) {
if (ch == INT_0) {
return _finishNumberLeadingNegZeroes();
}
reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value");
} else if (ch > INT_9) {
if (ch == 'I') {
return _finishNonStdToken(NON_STD_TOKEN_MINUS_INFINITY, 2);
}
reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value");
}
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
outBuf[0] = '-';
outBuf[1] = (char) ch;
_intLength = 1;
return _finishNumberIntegralPart(outBuf, 2);
}
protected JsonToken _finishNumberLeadingZeroes() throws IOException
{
// In general, skip further zeroes (if allowed), look for legal follow-up
// numeric characters; likely legal separators, or, known illegal (letters).
while (true) {
if (_inputPtr >= _inputEnd) {
_minorState = MINOR_NUMBER_ZERO;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
int ch = _inputBuffer[_inputPtr++] & 0xFF;
if (ch < INT_0) {
if (ch == INT_PERIOD) {
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
outBuf[0] = '0';
_intLength = 1;
return _startFloat(outBuf, 1, ch);
}
} else if (ch > INT_9) {
if (ch == INT_e || ch == INT_E) {
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
outBuf[0] = '0';
_intLength = 1;
return _startFloat(outBuf, 1, ch);
}
// Ok; unfortunately we have closing bracket/curly that are valid so need
// (colon not possible since this is within value, not after key)
//
if ((ch != INT_RBRACKET) && (ch != INT_RCURLY)) {
reportUnexpectedNumberChar(ch,
"expected digit (0-9), decimal point (.) or exponent indicator (e/E) to follow '0'");
}
} else { // Number between 0 and 9
// although not guaranteed, seems likely valid separator (white space,
// comma, end bracket/curly); next time token needed will verify
if ((_features & FEAT_MASK_LEADING_ZEROS) == 0) {
reportInvalidNumber("Leading zeroes not allowed");
}
if (ch == INT_0) { // coalesce multiple leading zeroes into just one
continue;
}
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
// trim out leading zero
outBuf[0] = (char) ch;
_intLength = 1;
return _finishNumberIntegralPart(outBuf, 1);
}
--_inputPtr;
return _valueCompleteInt(0, "0");
}
}
protected JsonToken _finishNumberLeadingNegZeroes() throws IOException
{
// In general, skip further zeroes (if allowed), look for legal follow-up
// numeric characters; likely legal separators, or, known illegal (letters).
while (true) {
if (_inputPtr >= _inputEnd) {
_minorState = MINOR_NUMBER_MINUSZERO;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
int ch = _inputBuffer[_inputPtr++] & 0xFF;
if (ch < INT_0) {
if (ch == INT_PERIOD) {
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
outBuf[0] = '-';
outBuf[1] = '0';
_intLength = 1;
return _startFloat(outBuf, 2, ch);
}
} else if (ch > INT_9) {
if (ch == INT_e || ch == INT_E) {
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
outBuf[0] = '-';
outBuf[1] = '0';
_intLength = 1;
return _startFloat(outBuf, 2, ch);
}
// Ok; unfortunately we have closing bracket/curly that are valid so need
// (colon not possible since this is within value, not after key)
//
if ((ch != INT_RBRACKET) && (ch != INT_RCURLY)) {
reportUnexpectedNumberChar(ch,
"expected digit (0-9), decimal point (.) or exponent indicator (e/E) to follow '0'");
}
} else { // Number between 1 and 9; go integral
// although not guaranteed, seems likely valid separator (white space,
// comma, end bracket/curly); next time token needed will verify
if ((_features & FEAT_MASK_LEADING_ZEROS) == 0) {
reportInvalidNumber("Leading zeroes not allowed");
}
if (ch == INT_0) { // coalesce multiple leading zeroes into just one
continue;
}
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
// trim out leading zero
outBuf[0] = '-';
outBuf[1] = (char) ch;
_intLength = 1;
return _finishNumberIntegralPart(outBuf, 2);
}
--_inputPtr;
return _valueCompleteInt(0, "0");
}
}
protected JsonToken _finishNumberIntegralPart(char[] outBuf, int outPtr) throws IOException
{
int negMod = _numberNegative ? -1 : 0;
while (true) {
if (_inputPtr >= _inputEnd) {
_minorState = MINOR_NUMBER_INTEGER_DIGITS;
_textBuffer.setCurrentLength(outPtr);
return (_currToken = JsonToken.NOT_AVAILABLE);
}
int ch = _inputBuffer[_inputPtr] & 0xFF;
if (ch < INT_0) {
if (ch == INT_PERIOD) {
_intLength = outPtr+negMod;
++_inputPtr;
return _startFloat(outBuf, outPtr, ch);
}
break;
}
if (ch > INT_9) {
if (ch == INT_e || ch == INT_E) {
_intLength = outPtr+negMod;
++_inputPtr;
return _startFloat(outBuf, outPtr, ch);
}
break;
}
++_inputPtr;
if (outPtr >= outBuf.length) {
// NOTE: must expand to ensure contents all in a single buffer (to keep
// other parts of parsing simpler)
outBuf = _textBuffer.expandCurrentSegment();
}
outBuf[outPtr++] = (char) ch;
}
_intLength = outPtr+negMod;
_textBuffer.setCurrentLength(outPtr);
return _valueComplete(JsonToken.VALUE_NUMBER_INT);
}
protected JsonToken _startFloat(char[] outBuf, int outPtr, int ch) throws IOException
{
int fractLen = 0;
if (ch == INT_PERIOD) {
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.expandCurrentSegment();
}
outBuf[outPtr++] = '.';
while (true) {
if (_inputPtr >= _inputEnd) {
_textBuffer.setCurrentLength(outPtr);
_minorState = MINOR_NUMBER_FRACTION_DIGITS;
_fractLength = fractLen;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
ch = _inputBuffer[_inputPtr++]; // ok to have sign extension for now
if (ch < INT_0 || ch > INT_9) {
ch &= 0xFF; // but here we'll want to mask it to unsigned 8-bit
// must be followed by sequence of ints, one minimum
if (fractLen == 0) {
reportUnexpectedNumberChar(ch, "Decimal point not followed by a digit");
}
break;
}
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.expandCurrentSegment();
}
outBuf[outPtr++] = (char) ch;
++fractLen;
}
}
_fractLength = fractLen;
int expLen = 0;
if (ch == INT_e || ch == INT_E) { // exponent?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.expandCurrentSegment();
}
outBuf[outPtr++] = (char) ch;
if (_inputPtr >= _inputEnd) {
_textBuffer.setCurrentLength(outPtr);
_minorState = MINOR_NUMBER_EXPONENT_MARKER;
_expLength = 0;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
ch = _inputBuffer[_inputPtr++]; // ok to have sign extension for now
if (ch == INT_MINUS || ch == INT_PLUS) {
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.expandCurrentSegment();
}
outBuf[outPtr++] = (char) ch;
if (_inputPtr >= _inputEnd) {
_textBuffer.setCurrentLength(outPtr);
_minorState = MINOR_NUMBER_EXPONENT_DIGITS;
_expLength = 0;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
ch = _inputBuffer[_inputPtr++];
}
while (ch >= INT_0 && ch <= INT_9) {
++expLen;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.expandCurrentSegment();
}
outBuf[outPtr++] = (char) ch;
if (_inputPtr >= _inputEnd) {
_textBuffer.setCurrentLength(outPtr);
_minorState = MINOR_NUMBER_EXPONENT_DIGITS;
_expLength = expLen;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
ch = _inputBuffer[_inputPtr++];
}
// must be followed by sequence of ints, one minimum
ch &= 0xFF;
if (expLen == 0) {
reportUnexpectedNumberChar(ch, "Exponent indicator not followed by a digit");
}
}
// push back the last char
--_inputPtr;
_textBuffer.setCurrentLength(outPtr);
// negative, int-length, fract-length already set, so...
_expLength = expLen;
return _valueComplete(JsonToken.VALUE_NUMBER_FLOAT);
}
protected JsonToken _finishFloatFraction() throws IOException
{
int fractLen = _fractLength;
char[] outBuf = _textBuffer.getBufferWithoutReset();
int outPtr = _textBuffer.getCurrentSegmentSize();
// caller guarantees at least one char; also, sign-extension not needed here
int ch;
while (((ch = _inputBuffer[_inputPtr++]) >= INT_0) && (ch <= INT_9)) {
++fractLen;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.expandCurrentSegment();
}
outBuf[outPtr++] = (char) ch;
if (_inputPtr >= _inputEnd) {
_textBuffer.setCurrentLength(outPtr);
_fractLength = fractLen;
return JsonToken.NOT_AVAILABLE;
}
}
// Ok, fraction done; what have we got next?
// must be followed by sequence of ints, one minimum
if (fractLen == 0) {
reportUnexpectedNumberChar(ch, "Decimal point not followed by a digit");
}
_fractLength = fractLen;
_textBuffer.setCurrentLength(outPtr);
// Ok: end of floating point number or exponent?
if (ch == INT_e || ch == INT_E) { // exponent?
_textBuffer.append((char) ch);
_expLength = 0;
if (_inputPtr >= _inputEnd) {
_minorState = MINOR_NUMBER_EXPONENT_MARKER;
return JsonToken.NOT_AVAILABLE;
}
_minorState = MINOR_NUMBER_EXPONENT_DIGITS;
return _finishFloatExponent(true, _inputBuffer[_inputPtr++] & 0xFF);
}
// push back the last char
--_inputPtr;
_textBuffer.setCurrentLength(outPtr);
// negative, int-length, fract-length already set, so...
_expLength = 0;
return _valueComplete(JsonToken.VALUE_NUMBER_FLOAT);
}
protected JsonToken _finishFloatExponent(boolean checkSign, int ch) throws IOException
{
if (checkSign) {
_minorState = MINOR_NUMBER_EXPONENT_DIGITS;
if (ch == INT_MINUS || ch == INT_PLUS) {
_textBuffer.append((char) ch);
if (_inputPtr >= _inputEnd) {
_minorState = MINOR_NUMBER_EXPONENT_DIGITS;
_expLength = 0;
return JsonToken.NOT_AVAILABLE;
}
ch = _inputBuffer[_inputPtr++];
}
}
char[] outBuf = _textBuffer.getBufferWithoutReset();
int outPtr = _textBuffer.getCurrentSegmentSize();
int expLen = _expLength;
while (ch >= INT_0 && ch <= INT_9) {
++expLen;
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.expandCurrentSegment();
}
outBuf[outPtr++] = (char) ch;
if (_inputPtr >= _inputEnd) {
_textBuffer.setCurrentLength(outPtr);
_expLength = expLen;
return JsonToken.NOT_AVAILABLE;
}
ch = _inputBuffer[_inputPtr++];
}
// must be followed by sequence of ints, one minimum
ch &= 0xFF;
if (expLen == 0) {
reportUnexpectedNumberChar(ch, "Exponent indicator not followed by a digit");
}
// push back the last char
--_inputPtr;
_textBuffer.setCurrentLength(outPtr);
// negative, int-length, fract-length already set, so...
_expLength = expLen;
return _valueComplete(JsonToken.VALUE_NUMBER_FLOAT);
}
/*
/**********************************************************************
/* Name-decoding, tertiary decoding
/**********************************************************************
*/
private final String _fastParseName() throws IOException
{
// If so, can also unroll loops nicely
// This may seem weird, but here we do NOT want to worry about UTF-8
// decoding. Rather, we'll assume that part is ok (if not it will be
// caught later on), and just handle quotes and backslashes here.
final byte[] input = _inputBuffer;
final int[] codes = _icLatin1;
int ptr = _inputPtr;
int q0 = input[ptr++] & 0xFF;
if (codes[q0] == 0) {
int i = input[ptr++] & 0xFF;
if (codes[i] == 0) {
int q = (q0 << 8) | i;
i = input[ptr++] & 0xFF;
if (codes[i] == 0) {
q = (q << 8) | i;
i = input[ptr++] & 0xFF;
if (codes[i] == 0) {
q = (q << 8) | i;
i = input[ptr++] & 0xFF;
if (codes[i] == 0) {
_quad1 = q;
return _parseMediumName(ptr, i);
}
if (i == INT_QUOTE) { // 4 byte/char case or broken
_inputPtr = ptr;
return _findName(q, 4);
}
return null;
}
if (i == INT_QUOTE) { // 3 byte/char case or broken
_inputPtr = ptr;
return _findName(q, 3);
}
return null;
}
if (i == INT_QUOTE) { // 2 byte/char case or broken
_inputPtr = ptr;
return _findName(q, 2);
}
return null;
}
if (i == INT_QUOTE) { // one byte/char case or broken
_inputPtr = ptr;
return _findName(q0, 1);
}
return null;
}
if (q0 == INT_QUOTE) {
_inputPtr = ptr;
return "";
}
return null;
}
private final String _parseMediumName(int ptr, int q2) throws IOException
{
final byte[] input = _inputBuffer;
final int[] codes = _icLatin1;
// Ok, got 5 name bytes so far
int i = input[ptr++] & 0xFF;
if (codes[i] == 0) {
q2 = (q2 << 8) | i;
i = input[ptr++] & 0xFF;
if (codes[i] == 0) {
q2 = (q2 << 8) | i;
i = input[ptr++] & 0xFF;
if (codes[i] == 0) {
q2 = (q2 << 8) | i;
i = input[ptr++] & 0xFF;
if (codes[i] == 0) {
return _parseMediumName2(ptr, i, q2);
}
if (i == INT_QUOTE) { // 8 bytes
_inputPtr = ptr;
return _findName(_quad1, q2, 4);
}
return null;
}
if (i == INT_QUOTE) { // 7 bytes
_inputPtr = ptr;
return _findName(_quad1, q2, 3);
}
return null;
}
if (i == INT_QUOTE) { // 6 bytes
_inputPtr = ptr;
return _findName(_quad1, q2, 2);
}
return null;
}
if (i == INT_QUOTE) { // 5 bytes
_inputPtr = ptr;
return _findName(_quad1, q2, 1);
}
return null;
}
private final String _parseMediumName2(int ptr, int q3, final int q2) throws IOException
{
final byte[] input = _inputBuffer;
final int[] codes = _icLatin1;
// Got 9 name bytes so far
int i = input[ptr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 9 bytes
_inputPtr = ptr;
return _findName(_quad1, q2, q3, 1);
}
return null;
}
q3 = (q3 << 8) | i;
i = input[ptr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 10 bytes
_inputPtr = ptr;
return _findName(_quad1, q2, q3, 2);
}
return null;
}
q3 = (q3 << 8) | i;
i = input[ptr++] & 0xFF;
if (codes[i] != 0) {
if (i == INT_QUOTE) { // 11 bytes
_inputPtr = ptr;
return _findName(_quad1, q2, q3, 3);
}
return null;
}
q3 = (q3 << 8) | i;
i = input[ptr++] & 0xFF;
if (i == INT_QUOTE) { // 12 bytes
_inputPtr = ptr;
return _findName(_quad1, q2, q3, 4);
}
// Could continue
return null;
}
/**
* Slower parsing method which is generally branched to when
* an escape sequence is detected (or alternatively for long
* names, one crossing input buffer boundary).
* Needs to be able to handle more exceptional cases, gets slower,
* and hence is offlined to a separate method.
*/
private final JsonToken _parseEscapedName(int qlen, int currQuad, int currQuadBytes)
throws IOException
{
// This may seem weird, but here we do not want to worry about
// UTF-8 decoding yet. Rather, we'll assume that part is ok (if not it will get
// caught later on), and just handle quotes and backslashes here.
int[] quads = _quadBuffer;
final int[] codes = _icLatin1;
while (true) {
if (_inputPtr >= _inputEnd) {
_quadLength = qlen;
_pending32 = currQuad;
_pendingBytes = currQuadBytes;
_minorState = MINOR_FIELD_NAME;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
int ch = _inputBuffer[_inputPtr++] & 0xFF;
if (codes[ch] == 0) {
if (currQuadBytes < 4) {
++currQuadBytes;
currQuad = (currQuad << 8) | ch;
continue;
}
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
currQuadBytes = 1;
continue;
}
// Otherwise bit longer handling
if (ch == INT_QUOTE) { // we are done
break;
}
// Unquoted white space?
if (ch != INT_BACKSLASH) {
// Call can actually now return (if unquoted linefeeds allowed)
_throwUnquotedSpace(ch, "name");
} else {
// Nope, escape sequence
ch = _decodeCharEscape();
if (ch < 0) { // method has set up state about escape sequence
_minorState = MINOR_FIELD_NAME_ESCAPE;
_minorStateAfterSplit = MINOR_FIELD_NAME;
_quadLength = qlen;
_pending32 = currQuad;
_pendingBytes = currQuadBytes;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
}
// May need to UTF-8 (re-)encode it, if it's beyond
// 7-bit ASCII. Gets pretty messy. If this happens often, may
// want to use different name canonicalization to avoid these hits.
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
if (ch > 127) {
// Ok, we'll need room for first byte right away
if (currQuadBytes >= 4) {
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
if (ch < 0x800) { // 2-byte
currQuad = (currQuad << 8) | (0xc0 | (ch >> 6));
++currQuadBytes;
// Second byte gets output below:
} else { // 3 bytes; no need to worry about surrogates here
currQuad = (currQuad << 8) | (0xe0 | (ch >> 12));
++currQuadBytes;
// need room for middle byte?
if (currQuadBytes >= 4) {
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
currQuad = (currQuad << 8) | (0x80 | ((ch >> 6) & 0x3f));
++currQuadBytes;
}
// And same last byte in both cases, gets output below:
ch = 0x80 | (ch & 0x3f);
}
if (currQuadBytes < 4) {
++currQuadBytes;
currQuad = (currQuad << 8) | ch;
continue;
}
quads[qlen++] = currQuad;
currQuad = ch;
currQuadBytes = 1;
}
if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = _padLastQuad(currQuad, currQuadBytes);
} else if (qlen == 0) { // rare, but may happen
return _fieldComplete("");
}
String name = _symbols.findName(quads, qlen);
if (name == null) {
name = _addName(quads, qlen, currQuadBytes);
}
return _fieldComplete(name);
}
/**
* Method called when we see non-white space character other
* than double quote, when expecting a field name.
* In standard mode will just throw an exception; but
* in non-standard modes may be able to parse name.
*/
private JsonToken _handleOddName(int ch) throws IOException
{
// First: may allow single quotes
switch (ch) {
case '#':
// Careful, since this may alternatively be leading char of
// unquoted name...
if ((_features & FEAT_MASK_ALLOW_YAML_COMMENTS) != 0) {
return _finishHashComment(MINOR_FIELD_LEADING_WS);
}
break;
case '/':
return _startSlashComment(MINOR_FIELD_LEADING_WS);
case '\'':
if ((_features & FEAT_MASK_ALLOW_SINGLE_QUOTES) != 0) {
return _finishAposName(0, 0, 0);
}
break;
case INT_RBRACKET: // for better error reporting...
return _closeArrayScope();
}
// allow unquoted names if feature enabled:
if ((_features & FEAT_MASK_ALLOW_UNQUOTED_NAMES) == 0) {
// !!! TODO: Decode UTF-8 characters properly...
// char c = (char) _decodeCharForError(ch);
char c = (char) ch;
_reportUnexpectedChar(c, "was expecting double-quote to start field name");
}
// Also: note that although we use a different table here, it does NOT handle UTF-8
// decoding. It'll just pass those high-bit codes as acceptable for later decoding.
final int[] codes = CharTypes.getInputCodeUtf8JsNames();
// Also: must start with a valid character...
if (codes[ch] != 0) {
_reportUnexpectedChar(ch, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name");
}
return _finishUnquotedName(0, ch, 1);
}
/**
* Parsing of optionally supported non-standard "unquoted" names: names without
* either double-quotes or apostrophes surrounding them.
* Unlike other
*/
private JsonToken _finishUnquotedName(int qlen, int currQuad, int currQuadBytes)
throws IOException
{
int[] quads = _quadBuffer;
final int[] codes = CharTypes.getInputCodeUtf8JsNames();
// Ok, now; instead of ultra-optimizing parsing here (as with regular JSON names),
// let's just use the generic "slow" variant. Can measure its impact later on if need be.
while (true) {
if (_inputPtr >= _inputEnd) {
_quadLength = qlen;
_pending32 = currQuad;
_pendingBytes = currQuadBytes;
_minorState = MINOR_FIELD_UNQUOTED_NAME;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
int ch = _inputBuffer[_inputPtr] & 0xFF;
if (codes[ch] != 0) {
break;
}
++_inputPtr;
// Ok, we have one more byte to add at any rate:
if (currQuadBytes < 4) {
++currQuadBytes;
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
currQuadBytes = 1;
}
}
if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
}
String name = _symbols.findName(quads, qlen);
if (name == null) {
name = _addName(quads, qlen, currQuadBytes);
}
return _fieldComplete(name);
}
private JsonToken _finishAposName(int qlen, int currQuad, int currQuadBytes)
throws IOException
{
int[] quads = _quadBuffer;
final int[] codes = _icLatin1;
while (true) {
if (_inputPtr >= _inputEnd) {
_quadLength = qlen;
_pending32 = currQuad;
_pendingBytes = currQuadBytes;
_minorState = MINOR_FIELD_APOS_NAME;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
int ch = _inputBuffer[_inputPtr++] & 0xFF;
if (ch == INT_APOS) {
break;
}
// additional check to skip handling of double-quotes
if (ch != '"' && codes[ch] != 0) {
if (ch != '\\') {
// Unquoted white space?
_throwUnquotedSpace(ch, "name");
} else {
// Nope, escape sequence
ch = _decodeCharEscape();
if (ch < 0) { // method has set up state about escape sequence
_minorState = MINOR_FIELD_NAME_ESCAPE;
_minorStateAfterSplit = MINOR_FIELD_APOS_NAME;
_quadLength = qlen;
_pending32 = currQuad;
_pendingBytes = currQuadBytes;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
}
if (ch > 127) {
// Ok, we'll need room for first byte right away
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
if (ch < 0x800) { // 2-byte
currQuad = (currQuad << 8) | (0xc0 | (ch >> 6));
++currQuadBytes;
// Second byte gets output below:
} else { // 3 bytes; no need to worry about surrogates here
currQuad = (currQuad << 8) | (0xe0 | (ch >> 12));
++currQuadBytes;
// need room for middle byte?
if (currQuadBytes >= 4) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
currQuad = (currQuad << 8) | (0x80 | ((ch >> 6) & 0x3f));
++currQuadBytes;
}
// And same last byte in both cases, gets output below:
ch = 0x80 | (ch & 0x3f);
}
}
// Ok, we have one more byte to add at any rate:
if (currQuadBytes < 4) {
++currQuadBytes;
currQuad = (currQuad << 8) | ch;
} else {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = currQuad;
currQuad = ch;
currQuadBytes = 1;
}
}
if (currQuadBytes > 0) {
if (qlen >= quads.length) {
_quadBuffer = quads = growArrayBy(quads, quads.length);
}
quads[qlen++] = _padLastQuad(currQuad, currQuadBytes);
} else if (qlen == 0) { // rare case but possible
return _fieldComplete("");
}
String name = _symbols.findName(quads, qlen);
if (name == null) {
name = _addName(quads, qlen, currQuadBytes);
}
return _fieldComplete(name);
}
protected final JsonToken _finishFieldWithEscape() throws IOException
{
// First: try finishing what wasn't yet:
int ch = _decodeSplitEscaped(_quoted32, _quotedDigits);
if (ch < 0) { // ... if possible
_minorState = MINOR_FIELD_NAME_ESCAPE;
return JsonToken.NOT_AVAILABLE;
}
if (_quadLength >= _quadBuffer.length) {
_quadBuffer = growArrayBy(_quadBuffer, 32);
}
int currQuad = _pending32;
int currQuadBytes = _pendingBytes;
if (ch > 127) {
// Ok, we'll need room for first byte right away
if (currQuadBytes >= 4) {
_quadBuffer[_quadLength++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
if (ch < 0x800) { // 2-byte
currQuad = (currQuad << 8) | (0xc0 | (ch >> 6));
++currQuadBytes;
// Second byte gets output below:
} else { // 3 bytes; no need to worry about surrogates here
currQuad = (currQuad << 8) | (0xe0 | (ch >> 12));
// need room for middle byte?
if (++currQuadBytes >= 4) {
_quadBuffer[_quadLength++] = currQuad;
currQuad = 0;
currQuadBytes = 0;
}
currQuad = (currQuad << 8) | (0x80 | ((ch >> 6) & 0x3f));
++currQuadBytes;
}
// And same last byte in both cases, gets output below:
ch = 0x80 | (ch & 0x3f);
}
if (currQuadBytes < 4) {
++currQuadBytes;
currQuad = (currQuad << 8) | ch;
} else {
_quadBuffer[_quadLength++] = currQuad;
currQuad = ch;
currQuadBytes = 1;
}
if (_minorStateAfterSplit == MINOR_FIELD_APOS_NAME) {
return _finishAposName(_quadLength, currQuad, currQuadBytes);
}
return _parseEscapedName(_quadLength, currQuad, currQuadBytes);
}
private int _decodeSplitEscaped(int value, int bytesRead) throws IOException
{
if (_inputPtr >= _inputEnd) {
_quoted32 = value;
_quotedDigits = bytesRead;
return -1;
}
int c = _inputBuffer[_inputPtr++];
if (bytesRead == -1) { // expecting first char after backslash
switch (c) {
// First, ones that are mapped
case 'b':
return '\b';
case 't':
return '\t';
case 'n':
return '\n';
case 'f':
return '\f';
case 'r':
return '\r';
// And these are to be returned as they are
case '"':
case '/':
case '\\':
return c;
case 'u': // and finally hex-escaped
break;
default:
{
// !!! TODO: Decode UTF-8 characters properly...
// char ch = (char) _decodeCharForError(c);
char ch = (char) c;
return _handleUnrecognizedCharacterEscape(ch);
}
}
if (_inputPtr >= _inputEnd) {
_quotedDigits = 0;
_quoted32 = 0;
return -1;
}
c = _inputBuffer[_inputPtr++];
bytesRead = 0;
}
c &= 0xFF;
while (true) {
int digit = CharTypes.charToHex(c);
if (digit < 0) {
_reportUnexpectedChar(c & 0xFF, "expected a hex-digit for character escape sequence");
}
value = (value << 4) | digit;
if (++bytesRead == 4) {
return value;
}
if (_inputPtr >= _inputEnd) {
_quotedDigits = bytesRead;
_quoted32 = value;
return -1;
}
c = _inputBuffer[_inputPtr++] & 0xFF;
}
}
/*
/**********************************************************************
/* Second-level decoding, String decoding
/**********************************************************************
*/
protected JsonToken _startString() throws IOException
{
int ptr = _inputPtr;
int outPtr = 0;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
final int[] codes = _icUTF8;
final int max = Math.min(_inputEnd, (ptr + outBuf.length));
final byte[] inputBuffer = _inputBuffer;
while (ptr < max) {
int c = (int) inputBuffer[ptr] & 0xFF;
if (codes[c] != 0) {
if (c == INT_QUOTE) {
_inputPtr = ptr+1;
_textBuffer.setCurrentLength(outPtr);
return _valueComplete(JsonToken.VALUE_STRING);
}
break;
}
++ptr;
outBuf[outPtr++] = (char) c;
}
_textBuffer.setCurrentLength(outPtr);
_inputPtr = ptr;
return _finishRegularString();
}
private final JsonToken _finishRegularString() throws IOException
{
int c;
// Here we do want to do full decoding, hence:
final int[] codes = _icUTF8;
final byte[] inputBuffer = _inputBuffer;
char[] outBuf = _textBuffer.getBufferWithoutReset();
int outPtr = _textBuffer.getCurrentSegmentSize();
int ptr = _inputPtr;
final int safeEnd = _inputEnd - 5; // longest escape is 6 chars
main_loop:
while (true) {
// Then the tight ASCII non-funny-char loop:
ascii_loop:
while (true) {
if (ptr >= _inputEnd) {
_inputPtr = ptr;
_minorState = MINOR_VALUE_STRING;
_textBuffer.setCurrentLength(outPtr);
return (_currToken = JsonToken.NOT_AVAILABLE);
}
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
final int max = Math.min(_inputEnd, (ptr + (outBuf.length - outPtr)));
while (ptr < max) {
c = inputBuffer[ptr++] & 0xFF;
if (codes[c] != 0) {
break ascii_loop;
}
outBuf[outPtr++] = (char) c;
}
}
// Ok: end marker, escape or multi-byte?
if (c == INT_QUOTE) {
_inputPtr = ptr;
_textBuffer.setCurrentLength(outPtr);
return _valueComplete(JsonToken.VALUE_STRING);
}
// If possibly split, use off-lined longer version
if (ptr >= safeEnd) {
_inputPtr = ptr;
_textBuffer.setCurrentLength(outPtr);
if (!_decodeSplitMultiByte(c, codes[c], ptr < _inputEnd)) {
_minorStateAfterSplit = MINOR_VALUE_STRING;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
outBuf = _textBuffer.getBufferWithoutReset();
outPtr = _textBuffer.getCurrentSegmentSize();
ptr = _inputPtr;
continue main_loop;
}
// otherwise use inlined
switch (codes[c]) {
case 1: // backslash
_inputPtr = ptr;
c = _decodeFastCharEscape(); // since we know it's not split
ptr = _inputPtr;
break;
case 2: // 2-byte UTF
c = _decodeUTF8_2(c, _inputBuffer[ptr++]);
break;
case 3: // 3-byte UTF
c = _decodeUTF8_3(c, _inputBuffer[ptr++], _inputBuffer[ptr++]);
break;
case 4: // 4-byte UTF
c = _decodeUTF8_4(c, _inputBuffer[ptr++], _inputBuffer[ptr++],
_inputBuffer[ptr++]);
// Let's add first part right away:
outBuf[outPtr++] = (char) (0xD800 | (c >> 10));
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
c = 0xDC00 | (c & 0x3FF);
// And let the other char output down below
break;
default:
if (c < INT_SPACE) {
// Note: call can now actually return (to allow unquoted linefeeds)
_throwUnquotedSpace(c, "string value");
} else {
// Is this good enough error message?
_reportInvalidChar(c);
}
}
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
// Ok, let's add char to output:
outBuf[outPtr++] = (char) c;
}
}
protected JsonToken _startAposString() throws IOException
{
int ptr = _inputPtr;
int outPtr = 0;
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
final int[] codes = _icUTF8;
final int max = Math.min(_inputEnd, (ptr + outBuf.length));
final byte[] inputBuffer = _inputBuffer;
while (ptr < max) {
int c = (int) inputBuffer[ptr] & 0xFF;
if (c == INT_APOS) {
_inputPtr = ptr+1;
_textBuffer.setCurrentLength(outPtr);
return _valueComplete(JsonToken.VALUE_STRING);
}
if (codes[c] != 0) {
break;
}
++ptr;
outBuf[outPtr++] = (char) c;
}
_textBuffer.setCurrentLength(outPtr);
_inputPtr = ptr;
return _finishAposString();
}
private final JsonToken _finishAposString() throws IOException
{
int c;
final int[] codes = _icUTF8;
final byte[] inputBuffer = _inputBuffer;
char[] outBuf = _textBuffer.getBufferWithoutReset();
int outPtr = _textBuffer.getCurrentSegmentSize();
int ptr = _inputPtr;
final int safeEnd = _inputEnd - 5; // longest escape is 6 chars
main_loop:
while (true) {
ascii_loop:
while (true) {
if (ptr >= _inputEnd) {
_inputPtr = ptr;
_minorState = MINOR_VALUE_APOS_STRING;
_textBuffer.setCurrentLength(outPtr);
return (_currToken = JsonToken.NOT_AVAILABLE);
}
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
final int max = Math.min(_inputEnd, (ptr + (outBuf.length - outPtr)));
while (ptr < max) {
c = inputBuffer[ptr++] & 0xFF;
if ((codes[c] != 0) && (c != INT_QUOTE)) {
break ascii_loop;
}
if (c == INT_APOS) {
_inputPtr = ptr;
_textBuffer.setCurrentLength(outPtr);
return _valueComplete(JsonToken.VALUE_STRING);
}
outBuf[outPtr++] = (char) c;
}
}
// Escape or multi-byte?
// If possibly split, use off-lined longer version
if (ptr >= safeEnd) {
_inputPtr = ptr;
_textBuffer.setCurrentLength(outPtr);
if (!_decodeSplitMultiByte(c, codes[c], ptr < _inputEnd)) {
_minorStateAfterSplit = MINOR_VALUE_APOS_STRING;
return (_currToken = JsonToken.NOT_AVAILABLE);
}
outBuf = _textBuffer.getBufferWithoutReset();
outPtr = _textBuffer.getCurrentSegmentSize();
ptr = _inputPtr;
continue main_loop;
}
// otherwise use inlined
switch (codes[c]) {
case 1: // backslash
_inputPtr = ptr;
c = _decodeFastCharEscape(); // since we know it's not split
ptr = _inputPtr;
break;
case 2: // 2-byte UTF
c = _decodeUTF8_2(c, _inputBuffer[ptr++]);
break;
case 3: // 3-byte UTF
c = _decodeUTF8_3(c, _inputBuffer[ptr++], _inputBuffer[ptr++]);
break;
case 4: // 4-byte UTF
c = _decodeUTF8_4(c, _inputBuffer[ptr++], _inputBuffer[ptr++],
_inputBuffer[ptr++]);
// Let's add first part right away:
outBuf[outPtr++] = (char) (0xD800 | (c >> 10));
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
c = 0xDC00 | (c & 0x3FF);
// And let the other char output down below
break;
default:
if (c < INT_SPACE) {
// Note: call can now actually return (to allow unquoted linefeeds)
_throwUnquotedSpace(c, "string value");
} else {
// Is this good enough error message?
_reportInvalidChar(c);
}
}
// Need more room?
if (outPtr >= outBuf.length) {
outBuf = _textBuffer.finishCurrentSegment();
outPtr = 0;
}
// Ok, let's add char to output:
outBuf[outPtr++] = (char) c;
}
}
private final boolean _decodeSplitMultiByte(int c, int type, boolean gotNext)
throws IOException
{
switch (type) {
case 1:
c = _decodeSplitEscaped(0, -1);
if (c < 0) {
_minorState = MINOR_VALUE_STRING_ESCAPE;
return false;
}
_textBuffer.append((char) c);
return true;
case 2: // 2-byte UTF; easy, either got both, or just miss one
if (gotNext) {
// NOTE: always succeeds, no need to check
c = _decodeUTF8_2(c, _inputBuffer[_inputPtr++]);
_textBuffer.append((char) c);
return true;
}
_minorState = MINOR_VALUE_STRING_UTF8_2;
_pending32 = c;
return false;
case 3: // 3-byte UTF
c &= 0x0F;
if (gotNext) {
return _decodeSplitUTF8_3(c, 1, _inputBuffer[_inputPtr++]);
}
_minorState = MINOR_VALUE_STRING_UTF8_3;
_pending32 = c;
_pendingBytes = 1;
return false;
case 4: // 4-byte UTF
c &= 0x07;
if (gotNext) {
return _decodeSplitUTF8_4(c, 1, _inputBuffer[_inputPtr++]);
}
_pending32 = c;
_pendingBytes = 1;
_minorState = MINOR_VALUE_STRING_UTF8_4;
return false;
default:
if (c < INT_SPACE) {
// Note: call can now actually return (to allow unquoted linefeeds)
_throwUnquotedSpace(c, "string value");
} else {
// Is this good enough error message?
_reportInvalidChar(c);
}
_textBuffer.append((char) c);
return true;
}
}
private final boolean _decodeSplitUTF8_3(int prev, int prevCount, int next)
throws IOException
{
if (prevCount == 1) {
if ((next & 0xC0) != 0x080) {
_reportInvalidOther(next & 0xFF, _inputPtr);
}
prev = (prev << 6) | (next & 0x3F);
if (_inputPtr >= _inputEnd) {
_minorState = MINOR_VALUE_STRING_UTF8_3;
_pending32 = prev;
_pendingBytes = 2;
return false;
}
next = _inputBuffer[_inputPtr++];
}
if ((next & 0xC0) != 0x080) {
_reportInvalidOther(next & 0xFF, _inputPtr);
}
_textBuffer.append((char) ((prev << 6) | (next & 0x3F)));
return true;
}
// @return Character value minus 0x10000; this so that caller
// can readily expand it to actual surrogates
private final boolean _decodeSplitUTF8_4(int prev, int prevCount, int next)
throws IOException
{
if (prevCount == 1) {
if ((next & 0xC0) != 0x080) {
_reportInvalidOther(next & 0xFF, _inputPtr);
}
prev = (prev << 6) | (next & 0x3F);
if (_inputPtr >= _inputEnd) {
_minorState = MINOR_VALUE_STRING_UTF8_4;
_pending32 = prev;
_pendingBytes = 2;
return false;
}
prevCount = 2;
next = _inputBuffer[_inputPtr++];
}
if (prevCount == 2) {
if ((next & 0xC0) != 0x080) {
_reportInvalidOther(next & 0xFF, _inputPtr);
}
prev = (prev << 6) | (next & 0x3F);
if (_inputPtr >= _inputEnd) {
_minorState = MINOR_VALUE_STRING_UTF8_4;
_pending32 = prev;
_pendingBytes = 3;
return false;
}
next = _inputBuffer[_inputPtr++];
}
if ((next & 0xC0) != 0x080) {
_reportInvalidOther(next & 0xFF, _inputPtr);
}
int c = ((prev << 6) | (next & 0x3F)) - 0x10000;
// Let's add first part right away:
_textBuffer.append((char) (0xD800 | (c >> 10)));
c = 0xDC00 | (c & 0x3FF);
// And let the other char output down below
_textBuffer.append((char) c);
return true;
}
/*
/**********************************************************************
/* Internal methods, UTF8 decoding
/**********************************************************************
*/
private final int _decodeCharEscape() throws IOException
{
int left = _inputEnd - _inputPtr;
if (left < 5) { // offline boundary-checking case:
return _decodeSplitEscaped(0, -1);
}
return _decodeFastCharEscape();
}
private final int _decodeFastCharEscape() throws IOException
{
int c = (int) _inputBuffer[_inputPtr++];
switch (c) {
// First, ones that are mapped
case 'b':
return '\b';
case 't':
return '\t';
case 'n':
return '\n';
case 'f':
return '\f';
case 'r':
return '\r';
// And these are to be returned as they are
case '"':
case '/':
case '\\':
return (char) c;
case 'u': // and finally hex-escaped
break;
default:
{
// !!! TODO: Decode UTF-8 characters properly...
// char ch = (char) _decodeCharForError(c);
char ch = (char) c;
return _handleUnrecognizedCharacterEscape(ch);
}
}
int ch = (int) _inputBuffer[_inputPtr++];
int digit = CharTypes.charToHex(ch);
int result = digit;
if (digit >= 0) {
ch = (int) _inputBuffer[_inputPtr++];
digit = CharTypes.charToHex(ch);
if (digit >= 0) {
result = (result << 4) | digit;
ch = (int) _inputBuffer[_inputPtr++];
digit = CharTypes.charToHex(ch);
if (digit >= 0) {
result = (result << 4) | digit;
ch = (int) _inputBuffer[_inputPtr++];
digit = CharTypes.charToHex(ch);
if (digit >= 0) {
return (result << 4) | digit;
}
}
}
}
_reportUnexpectedChar(ch & 0xFF, "expected a hex-digit for character escape sequence");
return -1;
}
/*
/**********************************************************************
/* Internal methods, UTF8 decoding
/**********************************************************************
*/
private final int _decodeUTF8_2(int c, int d) throws IOException
{
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
return ((c & 0x1F) << 6) | (d & 0x3F);
}
private final int _decodeUTF8_3(int c, int d, int e) throws IOException
{
c &= 0x0F;
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
c = (c << 6) | (d & 0x3F);
if ((e & 0xC0) != 0x080) {
_reportInvalidOther(e & 0xFF, _inputPtr);
}
return (c << 6) | (e & 0x3F);
}
// @return Character value minus 0x10000; this so that caller
// can readily expand it to actual surrogates
private final int _decodeUTF8_4(int c, int d, int e, int f) throws IOException
{
if ((d & 0xC0) != 0x080) {
_reportInvalidOther(d & 0xFF, _inputPtr);
}
c = ((c & 0x07) << 6) | (d & 0x3F);
if ((e & 0xC0) != 0x080) {
_reportInvalidOther(e & 0xFF, _inputPtr);
}
c = (c << 6) | (e & 0x3F);
if ((f & 0xC0) != 0x080) {
_reportInvalidOther(f & 0xFF, _inputPtr);
}
return ((c << 6) | (f & 0x3F)) - 0x10000;
}
/*
/**********************************************************************
/* Internal methods, other
/**********************************************************************
*/
}
NonBlockingJsonParserBase.java 0000664 0000000 0000000 00000073542 13561642473 0036231 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/async package com.fasterxml.jackson.core.json.async;
import java.io.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.base.ParserBase;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.json.JsonReadContext;
import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
import com.fasterxml.jackson.core.util.ByteArrayBuilder;
import static com.fasterxml.jackson.core.JsonTokenId.*;
/**
* Intermediate base class for non-blocking JSON parsers.
*/
public abstract class NonBlockingJsonParserBase
extends ParserBase
{
/*
/**********************************************************************
/* Major state constants
/**********************************************************************
*/
/**
* State right after parser has been constructed, before seeing the first byte
* to handle possible (but optional) BOM.
*/
protected final static int MAJOR_INITIAL = 0;
/**
* State right after parser a root value has been
* finished, but next token has not yet been recognized.
*/
protected final static int MAJOR_ROOT = 1;
protected final static int MAJOR_OBJECT_FIELD_FIRST = 2;
protected final static int MAJOR_OBJECT_FIELD_NEXT = 3;
protected final static int MAJOR_OBJECT_VALUE = 4;
protected final static int MAJOR_ARRAY_ELEMENT_FIRST = 5;
protected final static int MAJOR_ARRAY_ELEMENT_NEXT = 6;
/**
* State after non-blocking input source has indicated that no more input
* is forthcoming AND we have exhausted all the input
*/
protected final static int MAJOR_CLOSED = 7;
/*
/**********************************************************************
/* Minor state constants
/**********************************************************************
*/
/**
* State in which part of (UTF-8) BOM has been detected, but not yet completely.
*/
protected final static int MINOR_ROOT_BOM = 1;
/**
* State between root-level value, waiting for at least one white-space
* character as separator
*/
protected final static int MINOR_ROOT_NEED_SEPARATOR = 2;
/**
* State between root-level value, having processed at least one white-space
* character, and expecting either more, start of a value, or end of input
* stream.
*/
protected final static int MINOR_ROOT_GOT_SEPARATOR = 3;
// state before field name itself, waiting for quote (or unquoted name)
protected final static int MINOR_FIELD_LEADING_WS = 4;
// state before field name, expecting comma (or closing curly), then field name
protected final static int MINOR_FIELD_LEADING_COMMA = 5;
// State within regular (double-quoted) field name
protected final static int MINOR_FIELD_NAME = 7;
// State within regular (double-quoted) field name, within escape (having
// encountered either just backslash, or backslash and 'u' and 0 - 3 hex digits,
protected final static int MINOR_FIELD_NAME_ESCAPE = 8;
protected final static int MINOR_FIELD_APOS_NAME = 9;
protected final static int MINOR_FIELD_UNQUOTED_NAME = 10;
protected final static int MINOR_VALUE_LEADING_WS = 12;
protected final static int MINOR_VALUE_EXPECTING_COMMA = 13;
protected final static int MINOR_VALUE_EXPECTING_COLON = 14;
protected final static int MINOR_VALUE_WS_AFTER_COMMA = 15;
protected final static int MINOR_VALUE_TOKEN_NULL = 16;
protected final static int MINOR_VALUE_TOKEN_TRUE = 17;
protected final static int MINOR_VALUE_TOKEN_FALSE = 18;
protected final static int MINOR_VALUE_TOKEN_NON_STD = 19;
protected final static int MINOR_NUMBER_MINUS = 23;
protected final static int MINOR_NUMBER_ZERO = 24; // zero as first, possibly trimming multiple
protected final static int MINOR_NUMBER_MINUSZERO = 25; // "-0" (and possibly more zeroes) receive
protected final static int MINOR_NUMBER_INTEGER_DIGITS = 26;
protected final static int MINOR_NUMBER_FRACTION_DIGITS = 30;
protected final static int MINOR_NUMBER_EXPONENT_MARKER = 31;
protected final static int MINOR_NUMBER_EXPONENT_DIGITS = 32;
protected final static int MINOR_VALUE_STRING = 40;
protected final static int MINOR_VALUE_STRING_ESCAPE = 41;
protected final static int MINOR_VALUE_STRING_UTF8_2 = 42;
protected final static int MINOR_VALUE_STRING_UTF8_3 = 43;
protected final static int MINOR_VALUE_STRING_UTF8_4 = 44;
protected final static int MINOR_VALUE_APOS_STRING = 45;
/**
* Special state at which point decoding of a non-quoted token has encountered
* a problem; that is, either not matching fully (like "truf" instead of "true",
* at "tru"), or not having trailing separator (or end of input), like "trueful".
* Attempt is made, then, to decode likely full input token to report suitable
* error.
*/
protected final static int MINOR_VALUE_TOKEN_ERROR = 50;
protected final static int MINOR_COMMENT_LEADING_SLASH = 51;
protected final static int MINOR_COMMENT_CLOSING_ASTERISK = 52;
protected final static int MINOR_COMMENT_C = 53;
protected final static int MINOR_COMMENT_CPP = 54;
protected final static int MINOR_COMMENT_YAML = 55;
/*
/**********************************************************************
/* Helper objects, symbols (field names)
/**********************************************************************
*/
/**
* Symbol table that contains field names encountered so far
*/
final protected ByteQuadsCanonicalizer _symbols;
/**
* Temporary buffer used for name parsing.
*/
protected int[] _quadBuffer = new int[8];
protected int _quadLength;
protected int _quad1;
protected int _pending32;
protected int _pendingBytes;
protected int _quoted32;
protected int _quotedDigits;
/*
/**********************************************************************
/* Additional parsing state
/**********************************************************************
*/
/**
* Current main decoding state within logical tree
*/
protected int _majorState;
/**
* Value of {@link #_majorState} after completing a scalar value
*/
protected int _majorStateAfterValue;
/**
* Additional indicator within state; contextually relevant for just that state
*/
protected int _minorState;
/**
* Secondary minor state indicator used during decoding of escapes and/or
* multi-byte Unicode characters
*/
protected int _minorStateAfterSplit;
/**
* Flag that is sent when calling application indicates that there will
* be no more input to parse.
*/
protected boolean _endOfInput = false;
/*
/**********************************************************************
/* Additional parsing state: non-standard tokens
/**********************************************************************
*/
protected final static int NON_STD_TOKEN_NAN = 0;
protected final static int NON_STD_TOKEN_INFINITY = 1;
protected final static int NON_STD_TOKEN_PLUS_INFINITY = 2;
protected final static int NON_STD_TOKEN_MINUS_INFINITY = 3;
protected final static String[] NON_STD_TOKENS = new String[] {
"NaN",
"Infinity", "+Infinity", "-Infinity",
};
protected final static double[] NON_STD_TOKEN_VALUES = new double[] {
Double.NaN,
Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY,
};
/**
* When tokenizing non-standard ("odd") tokens, this is the type to consider;
* also works as index to actual textual representation.
*/
protected int _nonStdTokenType;
/*
/**********************************************************************
/* Location tracking, additional
/**********************************************************************
*/
/**
* Since we are fed content that may or may not start at zero offset, we need
* to keep track of the first byte within that buffer, to be able to calculate
* logical offset within input "stream"
*/
protected int _currBufferStart = 0;
/**
* Alternate row tracker, used to keep track of position by `\r` marker
* (whereas
* 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:
*
* Hash area is divided into 4 sections:
*
* NOTE: non-final to allow disabling intern()ing in case of excessive
* collisions.
*/
private boolean _intern;
/**
* Flag that indicates whether we should throw an exception if enough
* hash collisions are detected (true); or just worked around (false).
*
* @since 2.4
*/
private final boolean _failOnDoS;
/*
/**********************************************************
/* First, main hash area info
/**********************************************************
*/
/**
* Primary hash information area: consists of
* Default value is 2, for buckets of 4 slots; grows bigger with
* bigger table sizes.
*/
private int _tertiaryShift;
/**
* Total number of Strings in the symbol table; only used for child tables.
*/
private int _count;
/**
* Array that contains
* This flag needs to be checked both when adding new main entries,
* and when adding new collision list queues (i.e. creating a new
* collision list head entry)
*/
private boolean _hashShared;
/*
/**********************************************************
/* Life-cycle: constructors
/**********************************************************
*/
/**
* Constructor used for creating per-
* 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.
* Usual usage pattern is to create a single "master" instance, and either
* use that instance in sequential fashion, or to create derived "child"
* instances, which after use, are asked to return possible symbol additions
* to master instance. In either case benefit is that symbol table gets
* initialized so that further uses are more efficient, as eventually all
* symbols needed will already be in symbol table. At that point no more
* Symbol String allocations are needed, nor changes to symbol table itself.
*
* Note that while individual SymbolTable instances are NOT thread-safe
* (much like generic collection classes), concurrently used "child"
* instances can be freely used without synchronization. However, using
* master table concurrently with child instances can only be done if
* access to master instance is read-only (i.e. no modifications done).
*/
public final class CharsToNameCanonicalizer
{
/* If we use "multiply-add" based hash algorithm, this is the multiplier
* we use.
*
* Note that JDK uses 31; but it seems that 33 produces fewer collisions,
* at least with tests we have.
*/
public final static int HASH_MULT = 33;
/**
* Default initial table size. Shouldn't be miniscule (as there's
* cost to both array realloc and rehashing), but let's keep
* it reasonably small. For systems that properly
* reuse factories it doesn't matter either way; but when
* recreating factories often, initial overhead may dominate.
*/
private static final int DEFAULT_T_SIZE = 64;
/**
* Let's not expand symbol tables past some maximum size;
* this should protected against OOMEs caused by large documents
* with unique (~= random) names.
*/
private static final int MAX_T_SIZE = 0x10000; // 64k entries == 256k mem
/**
* Let's only share reasonably sized symbol tables. Max size set to 3/4 of 16k;
* this corresponds to 64k main hash index. This should allow for enough distinct
* names for almost any case.
*/
static final int MAX_ENTRIES_FOR_REUSE = 12000;
/**
* Also: to thwart attacks based on hash collisions (which may or may not
* be cheap to calculate), we will need to detect "too long"
* collision chains. Let's start with static value of 100 entries
* for the longest legal chain.
*
* Note: longest chain we have been able to produce without malicious
* intent has been 38 (with "com.fasterxml.jackson.core.main.TestWithTonsaSymbols");
* our setting should be reasonable here.
*
* @since 2.1
*/
static final int MAX_COLL_CHAIN_LENGTH = 100;
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Sharing of learnt symbols is done by optional linking of symbol
* table instances with their parents. When parent linkage is
* defined, and child instance is released (call to
* NOTE: non-final since we may need to disable this with overflow.
*/
private boolean _canonicalize;
/*
/**********************************************************
/* Actual symbol table data
/**********************************************************
*/
/**
* Primary matching symbols; it's expected most match occur from
* here.
*/
private String[] _symbols;
/**
* Overflow buckets; if primary doesn't match, lookup is done
* from here.
*
* Note: Number of buckets is half of number of symbol entries, on
* assumption there's less need for buckets.
*/
private Bucket[] _buckets;
/**
* Current size (number of entries); needed to know if and when
* rehash.
*/
private int _size;
/**
* Limit that indicates maximum size this instance can hold before
* it needs to be expanded and rehashed. Calculated using fill
* factor passed in to constructor.
*/
private int _sizeThreshold;
/**
* Mask used to get index from hash values; equal to
*
* This flag needs to be checked both when adding new main entries,
* and when adding new collision list queues (i.e. creating a new
* collision list head entry)
*/
private boolean _hashShared;
/*
/**********************************************************
/* Bit of DoS detection goodness
/**********************************************************
*/
/**
* Lazily constructed structure that is used to keep track of
* collision buckets that have overflowed once: this is used
* to detect likely attempts at denial-of-service attacks that
* uses hash collisions.
*
* @since 2.4
*/
private BitSet _overflows;
/*
/**********************************************************
/* Life-cycle: constructors
/**********************************************************
*/
/**
* Main method for constructing a root symbol table instance.
*/
private CharsToNameCanonicalizer(int seed)
{
_parent = null;
_seed = seed;
// these settings don't really matter for the bootstrap instance
_canonicalize = true;
_flags = -1;
// And we'll also set flags so no copying of buckets is needed:
_hashShared = false; // doesn't really matter for root instance
_longestCollisionList = 0;
_tableInfo = new AtomicReference
* Note: while this method is synchronized, it is generally not
* safe to both use makeChild/mergeChild, AND to use instance
* actively. Instead, a separate 'root' instance should be used
* on which only makeChild/mergeChild are called, but instance itself
* is not used as a symbol table.
*/
public CharsToNameCanonicalizer makeChild(int flags) {
return new CharsToNameCanonicalizer(this, flags, _seed, _tableInfo.get());
}
/**
* Method called by the using code to indicate it is done with this instance.
* This lets instance merge accumulated changes into parent (if need be),
* safely and efficiently, and without calling code having to know about parent
* information.
*/
public void release() {
// If nothing has been added, nothing to do
if (!maybeDirty()) { return; }
// we will try to merge if child table has new entries
if (_parent != null && _canonicalize) { // canonicalize set to false if max size was reached
_parent.mergeChild(new TableInfo(this));
// Let's also mark this instance as dirty, so that just in
// case release was too early, there's no corruption of possibly shared data.
_hashShared = true;
}
}
/**
* Method that allows contents of child table to potentially be
* "merged in" with contents of this symbol table.
*
* Note that caller has to make sure symbol table passed in is
* really a child or sibling of this symbol table.
*/
private void mergeChild(TableInfo childState)
{
final int childCount = childState.size;
TableInfo currState = _tableInfo.get();
// Should usually grow; but occasionally could also shrink if (but only if)
// collision list overflow ends up clearing some collision lists.
if (childCount == currState.size) {
return;
}
// One caveat: let's try to avoid problems with degenerate cases of documents with
// generated "random" names: for these, symbol tables would bloat indefinitely.
// One way to do this is to just purge tables if they grow
// too large, and that's what we'll do here.
if (childCount > MAX_ENTRIES_FOR_REUSE) {
// At any rate, need to clean up the tables
childState = TableInfo.createInitial(DEFAULT_T_SIZE);
}
_tableInfo.compareAndSet(currState, childState);
}
/*
/**********************************************************
/* Public API, generic accessors:
/**********************************************************
*/
public int size() {
if (_tableInfo != null) { // root table
return _tableInfo.get().size;
}
// nope, child table
return _size;
}
/**
* Method for checking number of primary hash buckets this symbol
* table uses.
*
* @since 2.1
*/
public int bucketCount() { return _symbols.length; }
public boolean maybeDirty() { return !_hashShared; }
public int hashSeed() { return _seed; }
/**
* Method mostly needed by unit tests; calculates number of
* entries that are in collision list. Value can be at most
* ({@link #size} - 1), but should usually be much lower, ideally 0.
*
* @since 2.1
*/
public int collisionCount() {
int count = 0;
for (Bucket bucket : _buckets) {
if (bucket != null) {
count += bucket.length;
}
}
return count;
}
/**
* Method mostly needed by unit tests; calculates length of the
* longest collision chain. This should typically be a low number,
* but may be up to {@link #size} - 1 in the pathological case
*
* @since 2.1
*/
public int maxCollisionLength() { return _longestCollisionList; }
/*
/**********************************************************
/* Public API, accessing symbols:
/**********************************************************
*/
public String findSymbol(char[] buffer, int start, int len, int h)
{
if (len < 1) { // empty Strings are simplest to handle up front
return "";
}
if (!_canonicalize) { // [JACKSON-259]
return new String(buffer, start, len);
}
/* Related to problems with sub-standard hashing (somewhat
* relevant for collision attacks too), let's try little
* bit of shuffling to improve hash codes.
* (note, however, that this can't help with full collisions)
*/
int index = _hashToIndex(h);
String sym = _symbols[index];
// Optimal case; checking existing primary symbol for hash index:
if (sym != null) {
// Let's inline primary String equality checking:
if (sym.length() == len) {
int i = 0;
while (sym.charAt(i) == buffer[start+i]) {
// Optimal case; primary match found
if (++i == len) {
return sym;
}
}
}
Bucket b = _buckets[index>>1];
if (b != null) {
sym = b.has(buffer, start, len);
if (sym != null) {
return sym;
}
sym = _findSymbol2(buffer, start, len, b.next);
if (sym != null) {
return sym;
}
}
}
return _addSymbol(buffer, start, len, h, index);
}
private String _findSymbol2(char[] buffer, int start, int len, Bucket b) {
while (b != null) {
String sym = b.has(buffer, start, len);
if (sym != null) {
return sym;
}
b = b.next;
}
return null;
}
private String _addSymbol(char[] buffer, int start, int len, int h, int index)
{
if (_hashShared) { //need to do copy-on-write?
copyArrays();
_hashShared = false;
} else if (_size >= _sizeThreshold) { // Need to expand?
rehash();
// Need to recalc hash; rare occurrence (index mask has been
// recalculated as part of rehash)
index = _hashToIndex(calcHash(buffer, start, len));
}
String newSymbol = new String(buffer, start, len);
if (JsonFactory.Feature.INTERN_FIELD_NAMES.enabledIn(_flags)) {
newSymbol = InternCache.instance.intern(newSymbol);
}
++_size;
// Ok; do we need to add primary entry, or a bucket?
if (_symbols[index] == null) {
_symbols[index] = newSymbol;
} else {
final int bix = (index >> 1);
Bucket newB = new Bucket(newSymbol, _buckets[bix]);
int collLen = newB.length;
if (collLen > MAX_COLL_CHAIN_LENGTH) {
// 23-May-2014, tatu: Instead of throwing an exception right away,
// let's handle in bit smarter way.
_handleSpillOverflow(bix, newB, index);
} else {
_buckets[bix] = newB;
_longestCollisionList = Math.max(collLen, _longestCollisionList);
}
}
return newSymbol;
}
/**
* Method called when an overflow bucket has hit the maximum expected length:
* this may be a case of DoS attack. Deal with it based on settings by either
* clearing up bucket (to avoid indefinite expansion) or throwing exception.
* Currently the first overflow for any single bucket DOES NOT throw an exception,
* only second time (per symbol table instance)
*/
private void _handleSpillOverflow(int bucketIndex, Bucket newBucket, int mainIndex)
{
if (_overflows == null) {
_overflows = new BitSet();
_overflows.set(bucketIndex);
} else {
if (_overflows.get(bucketIndex)) {
// Has happened once already for this bucket index, so probably not coincidental...
if (JsonFactory.Feature.FAIL_ON_SYMBOL_HASH_OVERFLOW.enabledIn(_flags)) {
reportTooManyCollisions(MAX_COLL_CHAIN_LENGTH);
}
// but even if we don't fail, we will stop canonicalizing as safety measure
// (so as not to cause problems with PermGen)
_canonicalize = false;
} else {
_overflows.set(bucketIndex);
}
}
// regardless, if we get this far, clear up the bucket, adjust size appropriately.
_symbols[mainIndex] = newBucket.symbol;
_buckets[bucketIndex] = null;
// newBucket contains new symbol; but we will
_size -= (newBucket.length);
// we could calculate longest; but for now just mark as invalid
_longestCollisionList = -1;
}
/**
* Helper method that takes in a "raw" hash value, shuffles it as necessary,
* and truncates to be used as the index.
*/
public int _hashToIndex(int rawHash) {
// doing these seems to help a bit
rawHash += (rawHash >>> 15);
rawHash ^= (rawHash << 7);
rawHash += (rawHash >>> 3);
return (rawHash & _indexMask);
}
/**
* Implementation of a hashing method for variable length
* Strings. Most of the time intention is that this calculation
* is done by caller during parsing, not here; however, sometimes
* it needs to be done for parsed "String" too.
*
* @param len Length of String; has to be at least 1 (caller guarantees
* this pre-condition)
*/
public int calcHash(char[] buffer, int start, int len) {
int hash = _seed;
for (int i = start, end = start+len; i < end; ++i) {
hash = (hash * HASH_MULT) + (int) buffer[i];
}
// NOTE: shuffling, if any, is done in 'findSymbol()', not here:
return (hash == 0) ? 1 : hash;
}
public int calcHash(String key)
{
final int len = key.length();
int hash = _seed;
for (int i = 0; i < len; ++i) {
hash = (hash * HASH_MULT) + (int) key.charAt(i);
}
// NOTE: shuffling, if any, is done in 'findSymbol()', not here:
return (hash == 0) ? 1 : hash;
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
/**
* Method called when copy-on-write is needed; generally when first
* change is made to a derived symbol table.
*/
private void copyArrays() {
final String[] oldSyms = _symbols;
_symbols = Arrays.copyOf(oldSyms, oldSyms.length);
final Bucket[] oldBuckets = _buckets;
_buckets = Arrays.copyOf(oldBuckets, oldBuckets.length);
}
/**
* Method called when size (number of entries) of symbol table grows
* so big that load factor is exceeded. Since size has to remain
* power of two, arrays will then always be doubled. Main work
* is really redistributing old entries into new String/Bucket
* entries.
*/
private void rehash() {
final int size = _symbols.length;
int newSize = size + size;
/* 12-Mar-2010, tatu: Let's actually limit maximum size we are
* prepared to use, to guard against OOME in case of unbounded
* name sets (unique [non-repeating] names)
*/
if (newSize > MAX_T_SIZE) {
// If this happens, there's no point in either growing or shrinking hash areas.
// Rather, let's just cut our losses and stop canonicalizing.
_size = 0;
_canonicalize = false;
// in theory, could just leave these as null, but...
_symbols = new String[DEFAULT_T_SIZE];
_buckets = new Bucket[DEFAULT_T_SIZE>>1];
_indexMask = DEFAULT_T_SIZE-1;
_hashShared = false;
return;
}
final String[] oldSyms = _symbols;
final Bucket[] oldBuckets = _buckets;
_symbols = new String[newSize];
_buckets = new Bucket[newSize >> 1];
// Let's update index mask, threshold, now (needed for rehashing)
_indexMask = newSize - 1;
_sizeThreshold = _thresholdSize(newSize);
int count = 0; // let's do sanity check
// Need to do two loops, unfortunately, since spill-over area is
// only half the size:
int maxColl = 0;
for (int i = 0; i < size; ++i) {
String symbol = oldSyms[i];
if (symbol != null) {
++count;
int index = _hashToIndex(calcHash(symbol));
if (_symbols[index] == null) {
_symbols[index] = symbol;
} else {
int bix = (index >> 1);
Bucket newB = new Bucket(symbol, _buckets[bix]);
_buckets[bix] = newB;
maxColl = Math.max(maxColl, newB.length);
}
}
}
final int bucketSize = (size >> 1);
for (int i = 0; i < bucketSize; ++i) {
Bucket b = oldBuckets[i];
while (b != null) {
++count;
String symbol = b.symbol;
int index = _hashToIndex(calcHash(symbol));
if (_symbols[index] == null) {
_symbols[index] = symbol;
} else {
int bix = (index >> 1);
Bucket newB = new Bucket(symbol, _buckets[bix]);
_buckets[bix] = newB;
maxColl = Math.max(maxColl, newB.length);
}
b = b.next;
}
}
_longestCollisionList = maxColl;
_overflows = null;
if (count != _size) {
throw new IllegalStateException(String.format(
"Internal error on SymbolTable.rehash(): had %d entries; now have %d",
_size, count));
}
}
/**
* @since 2.1
*/
protected void reportTooManyCollisions(int maxLen) {
throw new IllegalStateException("Longest collision chain in symbol table (of size "+_size
+") now exceeds maximum, "+maxLen+" -- suspect a DoS attack based on hash collisions");
}
// since 2.10, for tests only
/**
* Diagnostics method that will verify that internal data structures are consistent;
* not meant as user-facing method but only for test suites and possible troubleshooting.
*
* @since 2.10
*/
protected void verifyInternalConsistency() {
int count = 0;
final int size = _symbols.length;
for (int i = 0; i < size; ++i) {
String symbol = _symbols[i];
if (symbol != null) {
++count;
}
}
final int bucketSize = (size >> 1);
for (int i = 0; i < bucketSize; ++i) {
for (Bucket b = _buckets[i]; b != null; b = b.next) {
++count;
}
}
if (count != _size) {
throw new IllegalStateException(String.format("Internal error: expected internal size %d vs calculated count %d",
_size, count));
}
}
// For debugging, comment out
/*
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
int primaryCount = 0;
for (String s : _symbols) {
if (s != null) ++primaryCount;
}
sb.append("[BytesToNameCanonicalizer, size: ");
sb.append(_size);
sb.append('/');
sb.append(_symbols.length);
sb.append(", ");
sb.append(primaryCount);
sb.append('/');
sb.append(_size - primaryCount);
sb.append(" coll; avg length: ");
// Average length: minimum of 1 for all (1 == primary hit);
// and then 1 per each traversal for collisions/buckets
//int maxDist = 1;
int pathCount = _size;
for (Bucket b : _buckets) {
if (b != null) {
int spillLen = b.length;
for (int j = 1; j <= spillLen; ++j) {
pathCount += j;
}
}
}
double avgLength;
if (_size == 0) {
avgLength = 0.0;
} else {
avgLength = (double) pathCount / (double) _size;
}
// let's round up a bit (two 2 decimal places)
//avgLength -= (avgLength % 0.01);
sb.append(avgLength);
sb.append(']');
return sb.toString();
}
*/
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
/**
* This class is a symbol table entry. Each entry acts as a node
* in a linked list.
*/
static final class Bucket
{
public final String symbol;
public final Bucket next;
public final int length;
public Bucket(String s, Bucket n) {
symbol = s;
next = n;
length = (n == null) ? 1 : n.length+1;
}
public String has(char[] buf, int start, int len) {
if (symbol.length() != len) {
return null;
}
int i = 0;
do {
if (symbol.charAt(i) != buf[start+i]) {
return null;
}
} while (++i < len);
return symbol;
}
}
/**
* Immutable value class used for sharing information as efficiently
* as possible, by only require synchronization of reference manipulation
* but not access to contents.
*
* @since 2.8.7
*/
private final static class TableInfo
{
final int size;
final int longestCollisionList;
final String[] symbols;
final Bucket[] buckets;
public TableInfo(int size, int longestCollisionList,
String[] symbols, Bucket[] buckets)
{
this.size = size;
this.longestCollisionList = longestCollisionList;
this.symbols = symbols;
this.buckets = buckets;
}
public TableInfo(CharsToNameCanonicalizer src)
{
this.size = src._size;
this.longestCollisionList = src._longestCollisionList;
this.symbols = src._symbols;
this.buckets = src._buckets;
}
public static TableInfo createInitial(int sz) {
return new TableInfo(0,
0, // longestCollisionList
new String[sz], new Bucket[sz >> 1]);
}
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sym/Name.java 0000664 0000000 0000000 00000002551 13561642473 0030675 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.sym;
/**
* Base class for tokenized names (key strings in objects) that have
* been tokenized from byte-based input sources (like
* {@link java.io.InputStream}.
*
* @author Tatu Saloranta
*/
public abstract class Name
{
protected final String _name;
protected final int _hashCode;
protected Name(String name, int hashCode) {
_name = name;
_hashCode = hashCode;
}
public String getName() { return _name; }
/*
/**********************************************************
/* Methods for package/core parser
/**********************************************************
*/
public abstract boolean equals(int q1);
public abstract boolean equals(int q1, int q2);
/**
* @since 2.6
*/
public abstract boolean equals(int q1, int q2, int q3);
public abstract boolean equals(int[] quads, int qlen);
/*
/**********************************************************
/* Overridden standard methods
/**********************************************************
*/
@Override public String toString() { return _name; }
@Override public final int hashCode() { return _hashCode; }
@Override public boolean equals(Object o)
{
// Canonical instances, can usually just do identity comparison
return (o == this);
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sym/Name1.java 0000664 0000000 0000000 00000002027 13561642473 0030754 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.sym;
/**
* Specialized implementation of PName: can be used for short Strings
* that consists of at most 4 bytes. Usually this means short
* ascii-only names.
*
* The reason for such specialized classes is mostly space efficiency;
* and to a lesser degree performance. Both are achieved for short
* Strings by avoiding another level of indirection (via quad arrays)
*/
public final class Name1 extends Name
{
private final static Name1 EMPTY = new Name1("", 0, 0);
private final int q;
Name1(String name, int hash, int quad) {
super(name, hash);
q = quad;
}
public static Name1 getEmptyName() { return EMPTY; }
@Override public boolean equals(int quad) { return (quad == q); }
@Override public boolean equals(int quad1, int quad2) { return (quad1 == q) && (quad2 == 0); }
@Override public boolean equals(int q1, int q2, int q3) { return false; }
@Override public boolean equals(int[] quads, int qlen) { return (qlen == 1 && quads[0] == q); }
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sym/Name2.java 0000664 0000000 0000000 00000001770 13561642473 0030761 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.sym;
/**
* Specialized implementation of PName: can be used for short Strings
* that consists of 5 to 8 bytes. Usually this means relatively short
* ascii-only names.
*
* The reason for such specialized classes is mostly space efficiency;
* and to a lesser degree performance. Both are achieved for short
* Strings by avoiding another level of indirection (via quad arrays)
*/
public final class Name2 extends Name
{
private final int q1, q2;
Name2(String name, int hash, int quad1, int quad2) {
super(name, hash);
q1 = quad1;
q2 = quad2;
}
@Override
public boolean equals(int quad) { return false; }
@Override
public boolean equals(int quad1, int quad2) { return (quad1 == q1) && (quad2 == q2); }
@Override public boolean equals(int quad1, int quad2, int q3) { return false; }
@Override
public boolean equals(int[] quads, int qlen) { return (qlen == 2 && quads[0] == q1 && quads[1] == q2); }
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sym/Name3.java 0000664 0000000 0000000 00000001777 13561642473 0030771 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.sym;
/**
* Specialized implementation of PName: can be used for short Strings
* that consists of 9 to 12 bytes. It's the longest special purpose
* implementaion; longer ones are expressed using {@link NameN}.
*/
public final class Name3 extends Name
{
private final int q1, q2, q3;
Name3(String name, int hash, int i1, int i2, int i3) {
super(name, hash);
q1 = i1;
q2 = i2;
q3 = i3;
}
// Implies quad length == 1, never matches
@Override
public boolean equals(int quad) { return false; }
// Implies quad length == 2, never matches
@Override
public boolean equals(int quad1, int quad2) { return false; }
@Override
public boolean equals(int quad1, int quad2, int quad3) {
return (q1 == quad1) && (q2 == quad2) && (q3 == quad3);
}
@Override
public boolean equals(int[] quads, int qlen) {
return (qlen == 3) && (quads[0] == q1) && (quads[1] == q2) && (quads[2] == q3);
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sym/NameN.java 0000664 0000000 0000000 00000005160 13561642473 0031012 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.sym;
import java.util.Arrays;
/**
* Generic implementation of PName used for "long" names, where long
* means that its byte (UTF-8) representation is 13 bytes or more.
*/
public final class NameN extends Name
{
private final int q1, q2, q3, q4; // first four quads
private final int qlen; // total number of quads (4 + q.length)
private final int[] q;
NameN(String name, int hash, int q1, int q2, int q3, int q4,
int[] quads, int quadLen) {
super(name, hash);
this.q1 = q1;
this.q2 = q2;
this.q3 = q3;
this.q4 = q4;
q = quads;
qlen = quadLen;
}
public static NameN construct(String name, int hash, int[] q, int qlen)
{
/* We have specialized implementations for shorter
* names, so let's not allow runt instances here
*/
if (qlen < 4) {
throw new IllegalArgumentException();
}
int q1 = q[0];
int q2 = q[1];
int q3 = q[2];
int q4 = q[3];
int rem = qlen - 4;
int[] buf;
if (rem > 0) {
buf = Arrays.copyOfRange(q, 4, qlen);
} else {
buf = null;
}
return new NameN(name, hash, q1, q2, q3, q4, buf, qlen);
}
// Implies quad length == 1, never matches
@Override
public boolean equals(int quad) { return false; }
// Implies quad length == 2, never matches
@Override
public boolean equals(int quad1, int quad2) { return false; }
// Implies quad length == 3, never matches
@Override
public boolean equals(int quad1, int quad2, int quad3) { return false; }
@Override
public boolean equals(int[] quads, int len) {
if (len != qlen) { return false; }
// Will always have >= 4 quads, can unroll
if (quads[0] != q1) return false;
if (quads[1] != q2) return false;
if (quads[2] != q3) return false;
if (quads[3] != q4) return false;
switch (len) {
default:
return _equals2(quads);
case 8:
if (quads[7] != q[3]) return false;
case 7:
if (quads[6] != q[2]) return false;
case 6:
if (quads[5] != q[1]) return false;
case 5:
if (quads[4] != q[0]) return false;
case 4:
}
return true;
}
private final boolean _equals2(int[] quads)
{
final int end = qlen-4;
for (int i = 0; i < end; ++i) {
if (quads[i+4] != q[i]) {
return false;
}
}
return true;
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sym/package-info.java 0000664 0000000 0000000 00000000234 13561642473 0032335 0 ustar 00root root 0000000 0000000 /**
* Internal implementation classes for efficient handling of
* of symbols in JSON (field names in Objects)
*/
package com.fasterxml.jackson.core.sym;
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/type/ 0000775 0000000 0000000 00000000000 13561642473 0027320 5 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/type/ResolvedType.java 0000664 0000000 0000000 00000012733 13561642473 0032616 0 ustar 00root root 0000000 0000000 package 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
* For example: for type like {@link java.util.HashMap}, raw type is
* {@link java.util.HashMap}; but this method would return
* {@link java.util.Map}, because relevant type parameters that are
* resolved (and accessible using {@link #containedType(int)} and
* {@link #getKeyType()}) are parameter for {@link java.util.Map}
* (which may or may not be same as type parameters for subtype;
* in case of {@link java.util.HashMap} they are, but for further
* subtypes they may be different parameters or possibly none at all).
*
* @since 2.5
*
* @deprecated Since 2.7: does not have meaning as parameters depend on type
* resolved.
*/
@Deprecated // since 2.7
public Class> getParameterSource() {
return null;
}
/**
* Method for accessing key type for this type, assuming type
* has such a concept (only Map types do)
*/
public abstract ResolvedType getKeyType();
/**
* Method for accessing content type of this type, if type has
* such a thing: simple types do not, structured types do
* (like arrays, Collections and Maps)
*/
public abstract ResolvedType getContentType();
/**
* Method for accessing type of value that instances of this
* type references, if any.
*
* @return Referenced type, if any; null if not.
*
* @since 2.6
*/
public abstract ResolvedType getReferencedType();
/**
* Method for checking how many contained types this type
* has. Contained types are usually generic types, so that
* generic Maps have 2 contained types.
*/
public abstract int containedTypeCount();
/**
* Method for accessing definitions of contained ("child")
* types.
*
* @param index Index of contained type to return
*
* @return Contained type at index, or null if no such type
* exists (no exception thrown)
*/
public abstract ResolvedType containedType(int index);
/**
* Method for accessing name of type variable in indicated
* position. If no name is bound, will use placeholders (derived
* from 0-based index); if no type variable or argument exists
* with given index, null is returned.
*
* @param index Index of contained type to return
*
* @return Contained type at index, or null if no such type
* exists (no exception thrown)
*/
public abstract String containedTypeName(int index);
/*
/**********************************************************
/* Public API, other
/**********************************************************
*/
/**
* Method that can be used to serialize type into form from which
* it can be fully deserialized from at a later point (using
*
* Usage is by sub-classing: here is one way to instantiate reference
* to generic type
* Usual usage pattern is such that instance of this class is passed on two calls that are
* needed for outputting type id (and possible additional wrapping, depending on format;
* JSON, for example, requires wrapping as type id is part of regular data): first, a "prefix"
* write (which usually includes actual id), performed before value write; and then
* matching "suffix" write after value serialization.
*
* @since 2.9
*/
public class WritableTypeId
{
/**
* Enumeration of values that matches enum `As` from annotation
* `JsonTypeInfo`: separate definition to avoid dependency between
* streaming core and annotations packages; also allows more flexibility
* in case new values needed at this level of internal API.
*
* NOTE: in most cases this only matters with formats that do NOT have native
* type id capabilities, and require type id to be included within regular
* data (whether exposed as Java properties or not). Formats with native
* types usually use native type id functionality regardless, unless
* overridden by a feature to use "non-native" type inclusion.
*/
public enum Inclusion {
/**
* Inclusion as wrapper Array (1st element type id, 2nd element value).
*
* Corresponds to
* Corresponds to
* NOTE: if shape of typed value to write is NOT Object, will instead use
* {@link #WRAPPER_ARRAY} inclusion.
*
* Corresponds to
* Regarding handling, type id is ONLY written as native type id; if no native
* type ids available, caller is assumed to handle output some other way.
* This is different from {@link #METADATA_PROPERTY}.
*
* NOTE: if shape of typed value to write is NOT Object, will instead use
* {@link #WRAPPER_ARRAY} inclusion.
*
* Corresponds to
* Corresponds to
* NOTE: if "wrap-as-Object" is used, this does NOT contain property name to
* use but `null`.
*/
public String asProperty;
/**
* Property used to indicate style of inclusion for this type id, in cases where
* no native type id may be used (either because format has none, like JSON; or
* because use of native type ids is disabled [with YAML]).
*/
public Inclusion include;
/**
* Information about intended shape of the value being written (that is, {@link #forValue});
* in case of structured values, start token of the structure; for scalars, value token.
* Main difference is between structured values
* ({@link JsonToken#START_ARRAY}, {@link JsonToken#START_OBJECT})
* and scalars ({@link JsonToken#VALUE_STRING}): specific scalar type may not be
* important for processing.
*/
public JsonToken valueShape;
/**
* Flag that can be set to indicate that wrapper structure was written (during
* prefix-writing); used to determine if suffix requires matching close markers.
*/
public boolean wrapperWritten;
/**
* Optional additional information that generator may add during "prefix write",
* to be available on matching "suffix write".
*/
public Object extra;
public WritableTypeId() { }
/**
* Constructor used when calling a method for generating and writing Type Id;
* caller only knows value object and its intended shape.
*/
public WritableTypeId(Object value, JsonToken valueShape0) {
this(value, valueShape0, null);
}
/**
* Constructor used when calling a method for generating and writing Type Id,
* but where actual type to use for generating id is NOT the type of value
* (but its supertype).
*/
public WritableTypeId(Object value, Class> valueType0, JsonToken valueShape0) {
this(value, valueShape0, null);
forValueType = valueType0;
}
/**
* Constructor used when calling a method for writing Type Id;
* caller knows value object, its intended shape as well as id to
* use; but not details of wrapping (if any).
*/
public WritableTypeId(Object value, JsonToken valueShape0, Object id0)
{
forValue = value;
id = id0;
valueShape = valueShape0;
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/type/package-info.java 0000664 0000000 0000000 00000001157 13561642473 0032513 0 ustar 00root root 0000000 0000000 /**
* Contains classes needed for type introspection, mostly used by data binding
* functionality. Most of this functionality is needed to properly handled
* generic types, and to simplify and unify processing of things Jackson needs
* to determine how contained types (of {@link java.util.Collection} and
* {@link java.util.Map} classes) are to be handled.
*
* With 2.9, an additional type ({@link com.fasterxml.jackson.core.type.WritableTypeId})
* was added to help handling of type identifiers needed to support polymorphic
* type serialization, deserialization.
*/
package com.fasterxml.jackson.core.type;
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/ 0000775 0000000 0000000 00000000000 13561642473 0027314 5 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/BufferRecycler.java 0000664 0000000 0000000 00000013730 13561642473 0033065 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.util;
import java.util.concurrent.atomic.AtomicReferenceArray;
/**
* This is a small utility class, whose main functionality is to allow
* simple reuse of raw byte/char buffers. It is usually used through
*
* Rewritten in 2.10 to be thread-safe (see [jackson-core#479] for details),
* to not rely on {@code ThreadLocal} access.
*/
public class BufferRecycler
{
/**
* Buffer used for reading byte-based input.
*/
public final static int BYTE_READ_IO_BUFFER = 0;
/**
* Buffer used for temporarily storing encoded content; used
* for example by UTF-8 encoding writer
*/
public final static int BYTE_WRITE_ENCODING_BUFFER = 1;
/**
* Buffer used for temporarily concatenating output; used for
* example when requesting output as byte array.
*/
public final static int BYTE_WRITE_CONCAT_BUFFER = 2;
/**
* Buffer used for concatenating binary data that is either being
* encoded as base64 output, or decoded from base64 input.
*
* @since 2.1
*/
public final static int BYTE_BASE64_CODEC_BUFFER = 3;
/**
* Buffer used as input buffer for tokenization for character-based parsers.
*/
public final static int CHAR_TOKEN_BUFFER = 0;
/**
* Buffer used by generators; for byte-backed generators for buffering of
* {@link String} values to output (before encoding into UTF-8),
* and for char-backed generators as actual concatenation buffer.
*/
public final static int CHAR_CONCAT_BUFFER = 1;
/**
* Used through {@link TextBuffer}: directly by parsers (to concatenate
* String values)
* and indirectly via
* {@link com.fasterxml.jackson.core.io.SegmentedStringWriter}
* when serializing (databind level {@code ObjectMapper} and
* {@code ObjectWriter}). In both cases used as segments (and not for whole value),
* but may result in retention of larger chunks for big content
* (long text values during parsing; bigger output documents for generation).
*/
public final static int CHAR_TEXT_BUFFER = 2;
/**
* For parsers, temporary buffer into which {@code char[]} for names is copied
* when requested as such; for {@code WriterBasedGenerator} used for buffering
* during {@code writeString(Reader)} operation (not commonly used).
*/
public final static int CHAR_NAME_COPY_BUFFER = 3;
// Buffer lengths
private final static int[] BYTE_BUFFER_LENGTHS = new int[] { 8000, 8000, 2000, 2000 };
private final static int[] CHAR_BUFFER_LENGTHS = new int[] { 4000, 4000, 200, 200 };
// Note: changed from simple array in 2.10:
protected final AtomicReferenceArray
* Also implements {@link OutputStream} to allow
* efficient aggregation of output content as a byte array, similar
* to how {@link java.io.ByteArrayOutputStream} works, but somewhat more
* efficiently for many use cases.
*
* NOTE: maximum size limited to Java Array maximum, 2 gigabytes: this
* because usage pattern is to collect content for a `byte[]` and so although
* theoretically this builder can aggregate more content it will not be usable
* as things are. Behavior may be improved if we solve the access problem.
*/
public final class ByteArrayBuilder extends OutputStream
{
public final static byte[] NO_BYTES = new byte[0];
// Size of the first block we will allocate.
private final static int INITIAL_BLOCK_SIZE = 500;
// Maximum block size we will use for individual non-aggregated blocks.
// For 2.10, let's limit to using 128k chunks (was 256k up to 2.9)
private final static int MAX_BLOCK_SIZE = (1 << 17);
final static int DEFAULT_BLOCK_ARRAY_SIZE = 40;
// Optional buffer recycler instance that we can use for allocating the first block.
private final BufferRecycler _bufferRecycler;
private final LinkedList
* Note: simply constructs a {@link SerializedString} out of parameter,
* calls {@link #DefaultPrettyPrinter(SerializableString)}
*
* @param rootSeparator
*
* @since 2.1
*/
public DefaultPrettyPrinter(String rootSeparator) {
this((rootSeparator == null) ? null : new SerializedString(rootSeparator));
}
/**
* Constructor that specifies separator String to use between root values;
* if null, no separator is printed.
*
* @param rootSeparator
*
* @since 2.1
*/
public DefaultPrettyPrinter(SerializableString rootSeparator) {
_rootSeparator = rootSeparator;
withSeparators(DEFAULT_SEPARATORS);
}
public DefaultPrettyPrinter(DefaultPrettyPrinter base) {
this(base, base._rootSeparator);
}
public DefaultPrettyPrinter(DefaultPrettyPrinter base,
SerializableString rootSeparator)
{
_arrayIndenter = base._arrayIndenter;
_objectIndenter = base._objectIndenter;
_spacesInObjectEntries = base._spacesInObjectEntries;
_nesting = base._nesting;
_separators = base._separators;
_objectFieldValueSeparatorWithSpaces = base._objectFieldValueSeparatorWithSpaces;
_rootSeparator = rootSeparator;
}
public DefaultPrettyPrinter withRootSeparator(SerializableString rootSeparator)
{
if (_rootSeparator == rootSeparator ||
(rootSeparator != null && rootSeparator.equals(_rootSeparator))) {
return this;
}
return new DefaultPrettyPrinter(this, rootSeparator);
}
/**
* @since 2.6
*/
public DefaultPrettyPrinter withRootSeparator(String rootSeparator) {
return withRootSeparator((rootSeparator == null) ? null : new SerializedString(rootSeparator));
}
public void indentArraysWith(Indenter i) {
_arrayIndenter = (i == null) ? NopIndenter.instance : i;
}
public void indentObjectsWith(Indenter i) {
_objectIndenter = (i == null) ? NopIndenter.instance : i;
}
/**
* @since 2.3
*/
public DefaultPrettyPrinter withArrayIndenter(Indenter i) {
if (i == null) {
i = NopIndenter.instance;
}
if (_arrayIndenter == i) {
return this;
}
DefaultPrettyPrinter pp = new DefaultPrettyPrinter(this);
pp._arrayIndenter = i;
return pp;
}
/**
* @since 2.3
*/
public DefaultPrettyPrinter withObjectIndenter(Indenter i) {
if (i == null) {
i = NopIndenter.instance;
}
if (_objectIndenter == i) {
return this;
}
DefaultPrettyPrinter pp = new DefaultPrettyPrinter(this);
pp._objectIndenter = i;
return pp;
}
/**
* "Mutant factory" method that will return a pretty printer instance
* that does use spaces inside object entries; if 'this' instance already
* does this, it is returned; if not, a new instance will be constructed
* and returned.
*
* @since 2.3
*/
public DefaultPrettyPrinter withSpacesInObjectEntries() {
return _withSpaces(true);
}
/**
* "Mutant factory" method that will return a pretty printer instance
* that does not use spaces inside object entries; if 'this' instance already
* does this, it is returned; if not, a new instance will be constructed
* and returned.
*
* @since 2.3
*/
public DefaultPrettyPrinter withoutSpacesInObjectEntries() {
return _withSpaces(false);
}
protected DefaultPrettyPrinter _withSpaces(boolean state)
{
if (_spacesInObjectEntries == state) {
return this;
}
DefaultPrettyPrinter pp = new DefaultPrettyPrinter(this);
pp._spacesInObjectEntries = state;
return pp;
}
/**
* @since 2.9
*/
public DefaultPrettyPrinter withSeparators(Separators separators) {
_separators = separators;
_objectFieldValueSeparatorWithSpaces = " " + separators.getObjectFieldValueSeparator() + " ";
return this;
}
/*
/**********************************************************
/* Instantiatable impl
/**********************************************************
*/
@Override
public DefaultPrettyPrinter createInstance() {
if (getClass() != DefaultPrettyPrinter.class) { // since 2.10
throw new IllegalStateException("Failed `createInstance()`: "+getClass().getName()
+" does not override method; it has to");
}
return new DefaultPrettyPrinter(this);
}
/*
/**********************************************************
/* PrettyPrinter impl
/**********************************************************
*/
@Override
public void writeRootValueSeparator(JsonGenerator g) throws IOException
{
if (_rootSeparator != null) {
g.writeRaw(_rootSeparator);
}
}
@Override
public void writeStartObject(JsonGenerator g) throws IOException
{
g.writeRaw('{');
if (!_objectIndenter.isInline()) {
++_nesting;
}
}
@Override
public void beforeObjectEntries(JsonGenerator g) throws IOException
{
_objectIndenter.writeIndentation(g, _nesting);
}
/**
* Method called after an object field has been output, but
* before the value is output.
*
* Default handling (without pretty-printing) will output a single
* colon to separate the two. Pretty-printer is
* to output a colon as well, but can surround that with other
* (white-space) decoration.
*/
@Override
public void writeObjectFieldValueSeparator(JsonGenerator g) throws IOException
{
if (_spacesInObjectEntries) {
g.writeRaw(_objectFieldValueSeparatorWithSpaces);
} else {
g.writeRaw(_separators.getObjectFieldValueSeparator());
}
}
/**
* Method called after an object entry (field:value) has been completely
* output, and before another value is to be output.
*
* Default handling (without pretty-printing) will output a single
* comma to separate the two. Pretty-printer is
* to output a comma as well, but can surround that with other
* (white-space) decoration.
*/
@Override
public void writeObjectEntrySeparator(JsonGenerator g) throws IOException
{
g.writeRaw(_separators.getObjectEntrySeparator());
_objectIndenter.writeIndentation(g, _nesting);
}
@Override
public void writeEndObject(JsonGenerator g, int nrOfEntries) throws IOException
{
if (!_objectIndenter.isInline()) {
--_nesting;
}
if (nrOfEntries > 0) {
_objectIndenter.writeIndentation(g, _nesting);
} else {
g.writeRaw(' ');
}
g.writeRaw('}');
}
@Override
public void writeStartArray(JsonGenerator g) throws IOException
{
if (!_arrayIndenter.isInline()) {
++_nesting;
}
g.writeRaw('[');
}
@Override
public void beforeArrayValues(JsonGenerator g) throws IOException {
_arrayIndenter.writeIndentation(g, _nesting);
}
/**
* Method called after an array value has been completely
* output, and before another value is to be output.
*
* Default handling (without pretty-printing) will output a single
* comma to separate the two. Pretty-printer is
* to output a comma as well, but can surround that with other
* (white-space) decoration.
*/
@Override
public void writeArrayValueSeparator(JsonGenerator g) throws IOException
{
g.writeRaw(_separators.getArrayValueSeparator());
_arrayIndenter.writeIndentation(g, _nesting);
}
@Override
public void writeEndArray(JsonGenerator g, int nrOfValues) throws IOException
{
if (!_arrayIndenter.isInline()) {
--_nesting;
}
if (nrOfValues > 0) {
_arrayIndenter.writeIndentation(g, _nesting);
} else {
g.writeRaw(' ');
}
g.writeRaw(']');
}
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
/**
* Dummy implementation that adds no indentation whatsoever
*/
public static class NopIndenter
implements Indenter, java.io.Serializable
{
public static final NopIndenter instance = new NopIndenter();
@Override
public void writeIndentation(JsonGenerator g, int level) throws IOException { }
@Override
public boolean isInline() { return true; }
}
/**
* This is a very simple indenter that only adds a
* single space for indentation. It is used as the default
* indenter for array values.
*/
public static class FixedSpaceIndenter extends NopIndenter
{
public static final FixedSpaceIndenter instance = new FixedSpaceIndenter();
@Override
public void writeIndentation(JsonGenerator g, int level) throws IOException
{
g.writeRaw(' ');
}
@Override
public boolean isInline() { return true; }
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/Instantiatable.java 0000664 0000000 0000000 00000001374 13561642473 0033126 0 ustar 00root root 0000000 0000000 package 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
* Note: that this class extends {@link LinkedHashMap} is an implementation
* detail -- no code should ever directly call Map methods.
*/
public final class InternCache
extends ConcurrentHashMap
* One consideration is possible attack via colliding {@link String#hashCode};
* because of this, limit to reasonably low setting.
*/
private final static int MAX_ENTRIES = 180;
public final static InternCache instance = new InternCache();
/**
* As minor optimization let's try to avoid "flush storms",
* cases where multiple threads might try to concurrently
* flush the map.
*/
private final Object lock = new Object();
private InternCache() { super(MAX_ENTRIES, 0.8f, 4); }
public String intern(String input) {
String result = get(input);
if (result != null) { return result; }
/* 18-Sep-2013, tatu: We used to use LinkedHashMap, which has simple LRU
* method. No such functionality exists with CHM; and let's use simplest
* possible limitation: just clear all contents. This because otherwise
* we are simply likely to keep on clearing same, commonly used entries.
*/
if (size() >= MAX_ENTRIES) {
/* Not incorrect wrt well-known double-locking anti-pattern because underlying
* storage gives close enough answer to real one here; and we are
* more concerned with flooding than starvation.
*/
synchronized (lock) {
if (size() >= MAX_ENTRIES) {
clear();
}
}
}
result = input.intern();
put(result, result);
return result;
}
}
JsonGeneratorDelegate.java 0000664 0000000 0000000 00000036104 13561642473 0034317 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util package com.fasterxml.jackson.core.util;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.io.CharacterEscapes;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.math.BigInteger;
public class JsonGeneratorDelegate extends JsonGenerator
{
/**
* Delegate object that method calls are delegated to.
*/
protected JsonGenerator delegate;
/**
* Whether copy methods
* ({@link #copyCurrentEvent}, {@link #copyCurrentStructure}, {@link #writeTree} and {@link #writeObject})
* are to be called (true), or handled by this object (false).
*/
protected boolean delegateCopyMethods;
/*
/**********************************************************
/* Construction, initialization
/**********************************************************
*/
public JsonGeneratorDelegate(JsonGenerator d) {
this(d, true);
}
/**
* @param delegateCopyMethods Flag assigned to
* Fairly simple use of {@link JsonParserDelegate}: only need
* to override {@link #nextToken} to handle transition
*/
public class JsonParserSequence extends JsonParserDelegate
{
/**
* Parsers other than the first one (which is initially assigned
* as delegate)
*/
protected final JsonParser[] _parsers;
/**
* Configuration that determines whether state of parsers is first verified
* to see if parser already points to a token (that is,
* {@link JsonParser#hasCurrentToken()} returns
* Default setting is
* Beyond purely minimal implementation, there is limited amount of
* configurability which may be useful for actual use: for example,
* it is possible to redefine separator used between root-level
* values (default is single space; can be changed to line-feed).
*
* Note: does NOT implement {@link Instantiatable} since this is
* a stateless implementation; that is, a single instance can be
* shared between threads.
*/
public class MinimalPrettyPrinter
implements PrettyPrinter, java.io.Serializable
{
private static final long serialVersionUID = 1L;
protected String _rootValueSeparator;
/**
* @since 2.9
*/
protected Separators _separators;
/*
/**********************************************************
/* Life-cycle, construction, configuration
/**********************************************************
*/
public MinimalPrettyPrinter() {
this(DEFAULT_ROOT_VALUE_SEPARATOR.toString());
}
public MinimalPrettyPrinter(String rootValueSeparator) {
_rootValueSeparator = rootValueSeparator;
_separators = DEFAULT_SEPARATORS;
}
public void setRootValueSeparator(String sep) {
_rootValueSeparator = sep;
}
/**
* @since 2.9
*/
public MinimalPrettyPrinter setSeparators(Separators separators) {
_separators = separators;
return this;
}
/*
/**********************************************************
/* PrettyPrinter impl
/**********************************************************
*/
@Override
public void writeRootValueSeparator(JsonGenerator g) throws IOException
{
if (_rootValueSeparator != null) {
g.writeRaw(_rootValueSeparator);
}
}
@Override
public void writeStartObject(JsonGenerator g) throws IOException
{
g.writeRaw('{');
}
@Override
public void beforeObjectEntries(JsonGenerator g) throws IOException
{
// nothing special, since no indentation is added
}
/**
* Method called after an object field has been output, but
* before the value is output.
*
* Default handling will just output a single
* colon to separate the two, without additional spaces.
*/
@Override
public void writeObjectFieldValueSeparator(JsonGenerator g) throws IOException
{
g.writeRaw(_separators.getObjectFieldValueSeparator());
}
/**
* Method called after an object entry (field:value) has been completely
* output, and before another value is to be output.
*
* Default handling (without pretty-printing) will output a single
* comma to separate the two.
*/
@Override
public void writeObjectEntrySeparator(JsonGenerator g) throws IOException
{
g.writeRaw(_separators.getObjectEntrySeparator());
}
@Override
public void writeEndObject(JsonGenerator g, int nrOfEntries) throws IOException
{
g.writeRaw('}');
}
@Override
public void writeStartArray(JsonGenerator g) throws IOException
{
g.writeRaw('[');
}
@Override
public void beforeArrayValues(JsonGenerator g) throws IOException
{
// nothing special, since no indentation is added
}
/**
* Method called after an array value has been completely
* output, and before another value is to be output.
*
* Default handling (without pretty-printing) will output a single
* comma to separate values.
*/
@Override
public void writeArrayValueSeparator(JsonGenerator g) throws IOException
{
g.writeRaw(_separators.getArrayValueSeparator());
}
@Override
public void writeEndArray(JsonGenerator g, int nrOfValues) throws IOException
{
g.writeRaw(']');
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/RequestPayload.java 0000664 0000000 0000000 00000003527 13561642473 0033130 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.util;
import java.io.IOException;
/**
* Container object used to contain optional information on content
* being parsed, passed to {@link com.fasterxml.jackson.core.JsonParseException} in case of
* exception being thrown; this may be useful for caller to display
* information on failure.
*
* @since 2.8
*/
public class RequestPayload
implements java.io.Serializable // just in case, even though likely included as transient
{
private static final long serialVersionUID = 1L;
// request payload as byte[]
protected byte[] _payloadAsBytes;
// request payload as String
protected CharSequence _payloadAsText;
// Charset if the request payload is set in bytes
protected String _charset;
public RequestPayload(byte[] bytes, String charset) {
if (bytes == null) {
throw new IllegalArgumentException();
}
_payloadAsBytes = bytes;
_charset = (charset == null || charset.isEmpty()) ? "UTF-8" : charset;
}
public RequestPayload(CharSequence str) {
if (str == null) {
throw new IllegalArgumentException();
}
_payloadAsText = str;
}
/**
* Returns the raw request payload object i.e, either byte[] or String
*
* @return Object which is a raw request payload i.e, either byte[] or
* String
*/
public Object getRawPayload() {
if (_payloadAsBytes != null) {
return _payloadAsBytes;
}
return _payloadAsText;
}
@Override
public String toString() {
if (_payloadAsBytes != null) {
try {
return new String(_payloadAsBytes, _charset);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return _payloadAsText.toString();
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/Separators.java 0000664 0000000 0000000 00000003602 13561642473 0032303 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.util;
import java.io.Serializable;
/**
* Value class used with some {@link com.fasterxml.jackson.core.PrettyPrinter}
* implements
*
* @see com.fasterxml.jackson.core.util.DefaultPrettyPrinter
* @see com.fasterxml.jackson.core.util.MinimalPrettyPrinter
*
* @since 2.9
*/
public class Separators implements Serializable
{
private static final long serialVersionUID = 1;
private final char objectFieldValueSeparator;
private final char objectEntrySeparator;
private final char arrayValueSeparator;
public static Separators createDefaultInstance() {
return new Separators();
}
public Separators() {
this(':', ',', ',');
}
public Separators(char objectFieldValueSeparator,
char objectEntrySeparator, char arrayValueSeparator) {
this.objectFieldValueSeparator = objectFieldValueSeparator;
this.objectEntrySeparator = objectEntrySeparator;
this.arrayValueSeparator = arrayValueSeparator;
}
public Separators withObjectFieldValueSeparator(char sep) {
return (objectFieldValueSeparator == sep) ? this
: new Separators(sep, objectEntrySeparator, arrayValueSeparator);
}
public Separators withObjectEntrySeparator(char sep) {
return (objectEntrySeparator == sep) ? this
: new Separators(objectFieldValueSeparator, sep, arrayValueSeparator);
}
public Separators withArrayValueSeparator(char sep) {
return (arrayValueSeparator == sep) ? this
: new Separators(objectFieldValueSeparator, objectEntrySeparator, sep);
}
public char getObjectFieldValueSeparator() {
return objectFieldValueSeparator;
}
public char getObjectEntrySeparator() {
return objectEntrySeparator;
}
public char getArrayValueSeparator() {
return arrayValueSeparator;
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java 0000664 0000000 0000000 00000065474 13561642473 0032255 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.util;
import java.io.*;
import java.math.BigDecimal;
import java.util.*;
import com.fasterxml.jackson.core.io.NumberInput;
/**
* TextBuffer is a class similar to {@link StringBuffer}, with
* following differences:
*
* Reduced from 1000 down to 500 in 2.10.
*/
final static int MIN_SEGMENT_LEN = 500;
/**
* Let's limit maximum segment length to something sensible.
* For 2.10, let's limit to using 64kc chunks (128 kB) -- was 256kC/512kB up to 2.9
*/
final static int MAX_SEGMENT_LEN = 0x10000;
/*
/**********************************************************
/* Configuration:
/**********************************************************
*/
private final BufferRecycler _allocator;
/*
/**********************************************************
/* Shared input buffers
/**********************************************************
*/
/**
* Shared input buffer; stored here in case some input can be returned
* as is, without being copied to collector's own buffers. Note that
* this is read-only for this Object.
*/
private char[] _inputBuffer;
/**
* Character offset of first char in input buffer; -1 to indicate
* that input buffer currently does not contain any useful char data
*/
private int _inputStart;
private int _inputLen;
/*
/**********************************************************
/* Aggregation segments (when not using input buf)
/**********************************************************
*/
/**
* List of segments prior to currently active segment.
*/
private ArrayList
* NOTE: assumption is that {@link SoftReference} has its {@code equals()} and
* {@code hashCode()} implementations defined so that they use object identity, so
* we do not need to use something like {@link IdentityHashMap}
*/
private final Map
* Note that this class can be used in two roles: first, as a static
* utility class for loading purposes, and second, as a singleton
* loader of per-module version information.
*
* Note that method for accessing version information changed between versions
* 2.1 and 2.2; earlier code used file named "VERSION.txt"; but this has serious
* performance issues on some platforms (Android), so a replacement system
* was implemented to use class generation and dynamic class loading.
*
* Note that functionality for reading "VERSION.txt" was removed completely
* from Jackson 2.6.
*/
public class VersionUtil
{
private final static Pattern V_SEP = Pattern.compile("[-_./;:]");
/*
/**********************************************************
/* Instance life-cycle
/**********************************************************
*/
protected VersionUtil() { }
@Deprecated // since 2.9
public Version version() { return Version.unknownVersion(); }
/*
/**********************************************************
/* Static load methods
/**********************************************************
*/
/**
* Helper method that will try to load version information for specified
* class. Implementation is as follows:
*
* First, tries to load version info from a class named
* "PackageVersion" in the same package as the class.
*
* If no version information is found, {@link Version#unknownVersion()} is returned.
*/
public static Version versionFor(Class> cls)
{
Version version = packageVersionFor(cls);
return version == null ? Version.unknownVersion() : version;
}
/**
* Loads version information by introspecting a class named
* "PackageVersion" in the same package as the given class.
*
* If the class could not be found or does not have a public
* static Version field named "VERSION", returns null.
*/
public static Version packageVersionFor(Class> cls)
{
Version v = null;
try {
String versionInfoClassName = cls.getPackage().getName() + ".PackageVersion";
Class> vClass = Class.forName(versionInfoClassName, true, cls.getClassLoader());
// However, if class exists, it better work correctly, no swallowing exceptions
try {
v = ((Versioned) vClass.getDeclaredConstructor().newInstance()).version();
} catch (Exception e) {
throw new IllegalArgumentException("Failed to get Versioned out of "+vClass);
}
} catch (Exception e) { // ok to be missing (not good but acceptable)
;
}
return (v == null) ? Version.unknownVersion() : v;
}
/**
* Will attempt to load the maven version for the given groupId and
* artifactId. Maven puts a pom.properties file in
* META-INF/maven/groupId/artifactId, containing the groupId,
* artifactId and version of the library.
*
* @param cl the ClassLoader to load the pom.properties file from
* @param groupId the groupId of the library
* @param artifactId the artifactId of the library
* @return The version
*
* @deprecated Since 2.6: functionality not used by any official Jackson component, should be
* moved out if anyone needs it
*/
@SuppressWarnings("resource")
@Deprecated // since 2.6
public static Version mavenVersionFor(ClassLoader cl, String groupId, String artifactId)
{
InputStream pomProperties = cl.getResourceAsStream("META-INF/maven/"
+ groupId.replaceAll("\\.", "/")+ "/" + artifactId + "/pom.properties");
if (pomProperties != null) {
try {
Properties props = new Properties();
props.load(pomProperties);
String versionStr = props.getProperty("version");
String pomPropertiesArtifactId = props.getProperty("artifactId");
String pomPropertiesGroupId = props.getProperty("groupId");
return parseVersion(versionStr, pomPropertiesGroupId, pomPropertiesArtifactId);
} catch (IOException e) {
// Ignore
} finally {
_close(pomProperties);
}
}
return Version.unknownVersion();
}
/**
* Method used by (1 << N)
*/
public int getMask();
/**
* Convenience method for checking whether feature is enabled in given bitmask
*/
public boolean enabledIn(int flags);
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/FormatSchema.java 0000664 0000000 0000000 00000002525 13561642473 0031557 0 ustar 00root root 0000000 0000000 /* 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.
*intern()
ing) is disabled) as protective
* measure.
*false
as JSON does NOT
* require stable ordering. Formats that require ordering include positional
* textual formats like CSV
, and schema-based binary formats
* like Avro
.
*
* @since 2.3
*/
@Override
public boolean requiresPropertyOrdering() { return false; }
/**
* Introspection method that higher-level functionality may call
* to see whether underlying data format can read and write binary
* data natively; that is, embeded it as-is without using encodings
* such as Base64.
*false
as JSON does not
* support native access: all binary content must use Base64 encoding.
* Most binary formats (like Smile and Avro) support native binary content.
*
* @since 2.3
*/
@Override
public boolean canHandleBinaryNatively() { return false; }
/**
* Introspection method that can be used by base factory to check
* whether access using char[]
is something that actual
* parser implementations can take advantage of, over having to
* use {@link java.io.Reader}. Sub-types are expected to override
* definition; default implementation (suitable for JSON) alleges
* that optimization are possible; and thereby is likely to try
* to access {@link java.lang.String} content by first copying it into
* recyclable intermediate buffer.
*
* @since 2.4
*/
public boolean canUseCharArrays() { return true; }
/**
* Introspection method that can be used to check whether this
* factory can create non-blocking parsers: parsers that do not
* use blocking I/O abstractions but instead use a
* {@link com.fasterxml.jackson.core.async.NonBlockingInputFeeder}.
*
* @since 2.9
*/
@Override
public boolean canParseAsync() {
// 31-May-2017, tatu: Jackson 2.9 does support async parsing for JSON,
// but not all other formats, so need to do this:
return _isJSONFactory();
}
@Override
public Class extends FormatFeature> getFormatReadFeatureType() {
return null;
}
@Override
public Class extends FormatFeature> getFormatWriteFeatureType() {
return null;
}
/*
/**********************************************************
/* Format detection functionality
/**********************************************************
*/
/**
* Method that can be used to quickly check whether given schema
* is something that parsers and/or generators constructed by this
* factory could use. Note that this means possible use, at the level
* of data format (i.e. schema is for same data format as parsers and
* generators this factory constructs); individual schema instances
* may have further usage restrictions.
*
* @since 2.1
*/
@Override
public boolean canUseSchema(FormatSchema schema) {
if (schema == null){
return false;
}
String ourFormat = getFormatName();
return (ourFormat != null) && ourFormat.equals(schema.getSchemaType());
}
/**
* Method that returns short textual id identifying format
* this factory supports.
*com.fasterxml.jackson.databind.ObjectMapper
)
* with this factory (and more importantly, parsers and generators
* it constructs). This is needed to use data-binding methods
* of {@link JsonParser} and {@link JsonGenerator} instances.
*/
public JsonFactory setCodec(ObjectCodec oc) {
_objectCodec = oc;
return this;
}
public ObjectCodec getCodec() { return _objectCodec; }
/*
/**********************************************************
/* Parser factories, traditional (blocking) I/O sources
/**********************************************************
*/
/**
* Method for constructing JSON parser instance to parse
* contents of specified file.
*
*char[]
object for accessing content.
*
* @since 2.4
*/
protected JsonParser _createParser(char[] data, int offset, int len, IOContext ctxt,
boolean recyclable) throws IOException {
return new ReaderBasedJsonParser(ctxt, _parserFeatures, null, _objectCodec,
_rootCharSymbols.makeChild(_factoryFeatures),
data, offset, offset+len, recyclable);
}
/**
* Overridable factory method that actually instantiates parser
* using given {@link Reader} object for reading content
* passed as raw byte array.
*ObjectMapper
*/
public BufferRecycler _getBufferRecycler()
{
/* 23-Apr-2015, tatu: Let's allow disabling of buffer recycling
* scheme, for cases where it is considered harmful (possibly
* on Android, for example)
*/
if (Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING.enabledIn(_factoryFeatures)) {
return BufferRecyclers.getBufferRecycler();
}
return new BufferRecycler();
}
/**
* Overridable factory method that actually instantiates desired
* context object.
*/
protected IOContext _createContext(Object srcRef, boolean resourceManaged) {
return new IOContext(_getBufferRecycler(), srcRef, resourceManaged);
}
/**
* Overridable factory method that actually instantiates desired
* context object for async (non-blocking) parsing
*
* @since 2.9.7
*/
protected IOContext _createNonBlockingContext(Object srcRef) {
// [jackson-core#479]: allow recycling for non-blocking parser again
// now that access is thread-safe
return new IOContext(_getBufferRecycler(), srcRef, false);
}
/*
/**********************************************************
/* Internal helper methods
/**********************************************************
*/
/**
* Helper method called to work around the problem of this class both defining
* general API for constructing parsers+generators AND implementing the API
* for JSON handling. Problem here is that when adding new functionality
* via factory methods, it is not possible to leave these methods abstract
* (because we are implementing them for JSON); but there is risk that
* sub-classes do not override them all (plus older version can not implement).
* So a work-around is to add a check to ensure that factory is still one
* used for JSON; and if not, make base implementation of a factory method fail.
*
* @since 2.9
*/
private final void _requireJSONFactory(String msg) {
if (!_isJSONFactory()) {
throw new UnsupportedOperationException(String.format(msg, getFormatName()));
}
}
private final boolean _isJSONFactory() {
// NOTE: since we only really care about whether this is standard JSON-backed factory,
// or its sub-class / delegated to one, no need to check for equality, identity is enough
return getFormatName() == FORMAT_NAME_JSON;
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonFactoryBuilder.java 0000664 0000000 0000000 00000017240 13561642473 0032756 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core;
import com.fasterxml.jackson.core.io.CharacterEscapes;
import com.fasterxml.jackson.core.io.SerializedString;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.core.json.JsonWriteFeature;
/**
* {@link com.fasterxml.jackson.core.TSFBuilder}
* implementation for constructing vanilla {@link JsonFactory}
* instances for reading/writing JSON encoded content.
*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).
*mask
.
* Functionally equivalent to
*
* int oldState = getFeatureMask();
* int newState = (oldState & ~mask) | (values & mask);
* setFeatureMask(newState);
*
* but preferred as this lets caller more efficiently specify actual changes made.
*
* @param values Bit mask of set/clear state for features to change
* @param mask Bit mask of features to change
*
* @since 2.6
*/
public JsonGenerator overrideStdFeatures(int values, int mask) {
int oldState = getFeatureMask();
int newState = (oldState & ~mask) | (values & mask);
return setFeatureMask(newState);
}
/**
* Bulk access method for getting state of all {@link FormatFeature}s, format-specific
* on/off configuration settings.
*
* @return Bit mask that defines current states of all standard {@link FormatFeature}s.
*
* @since 2.6
*/
public int getFormatFeatures() {
return 0;
}
/**
* Bulk set method for (re)setting states of {@link FormatFeature}s,
* by specifying values (set / clear) along with a mask, to determine
* which features to change, if any.
*
* getOutputContext().getCurrentValue();
*
*
* getOutputContext().setCurrentValue(v);
*
*
* @since 2.5
*/
public void setCurrentValue(Object v) {
JsonStreamContext ctxt = getOutputContext();
if (ctxt != null) {
ctxt.setCurrentValue(v);
}
}
/*
/**********************************************************
/* Public API, capability introspection methods
/**********************************************************
*/
/**
* Method that can be used to verify that given schema can be used with
* this generator (using {@link #setSchema}).
*
* @param schema Schema to check
*
* @return True if this generator can use given schema; false if not
*/
public boolean canUseSchema(FormatSchema schema) { return false; }
/**
* Introspection method that may be called to see if the underlying
* data format supports some kind of Object Ids natively (many do not;
* for example, JSON doesn't).
* This method must be called prior to calling
* {@link #writeObjectId} or {@link #writeObjectRef}.
*String
* and call {@link #writeFieldName(String)}.
*
* @since 2.8
*/
public void writeFieldId(long id) throws IOException {
writeFieldName(Long.toString(id));
}
/*
/**********************************************************
/* Public API, write methods, scalar arrays (2.8)
/**********************************************************
*/
/**
* Value write method that can be called to write a single
* array (sequence of {@link JsonToken#START_ARRAY}, zero or
* more {@link JsonToken#VALUE_NUMBER_INT}, {@link JsonToken#END_ARRAY})
*
* @since 2.8
*
* @param array Array that contains values to write
* @param offset Offset of the first element to write, within array
* @param length Number of elements in array to write, from `offset` to `offset + len - 1`
*/
public void writeArray(int[] array, int offset, int length) throws IOException
{
if (array == null) {
throw new IllegalArgumentException("null array");
}
_verifyOffsets(array.length, offset, length);
writeStartArray(array, length);
for (int i = offset, end = offset+length; i < end; ++i) {
writeNumber(array[i]);
}
writeEndArray();
}
/**
* Value write method that can be called to write a single
* array (sequence of {@link JsonToken#START_ARRAY}, zero or
* more {@link JsonToken#VALUE_NUMBER_INT}, {@link JsonToken#END_ARRAY})
*
* @since 2.8
*
* @param array Array that contains values to write
* @param offset Offset of the first element to write, within array
* @param length Number of elements in array to write, from `offset` to `offset + len - 1`
*/
public void writeArray(long[] array, int offset, int length) throws IOException
{
if (array == null) {
throw new IllegalArgumentException("null array");
}
_verifyOffsets(array.length, offset, length);
writeStartArray(array, length);
for (int i = offset, end = offset+length; i < end; ++i) {
writeNumber(array[i]);
}
writeEndArray();
}
/**
* Value write method that can be called to write a single
* array (sequence of {@link JsonToken#START_ARRAY}, zero or
* more {@link JsonToken#VALUE_NUMBER_FLOAT}, {@link JsonToken#END_ARRAY})
*
* @since 2.8
*
* @param array Array that contains values to write
* @param offset Offset of the first element to write, within array
* @param length Number of elements in array to write, from `offset` to `offset + len - 1`
*/
public void writeArray(double[] array, int offset, int length) throws IOException
{
if (array == null) {
throw new IllegalArgumentException("null array");
}
_verifyOffsets(array.length, offset, length);
writeStartArray(array, length);
for (int i = offset, end = offset+length; i < end; ++i) {
writeNumber(array[i]);
}
writeEndArray();
}
/*
/**********************************************************
/* Public API, write methods, text/String values
/**********************************************************
*/
/**
* Method for outputting a String value. Depending on context
* this means either array element, (object) field value or
* a stand alone String; but in all cases, String will be
* surrounded in double quotes, and contents will be properly
* escaped as required by JSON specification.
*/
public abstract void writeString(String text) throws IOException;
/**
* Method for outputting a String value. Depending on context
* this means either array element, (object) field value or
* a stand alone String; but in all cases, String will be
* surrounded in double quotes, and contents will be properly
* escaped as required by JSON specification.
* If the reader is null, then write a null.
* If len is < 0, then write all contents of the reader.
* Otherwise, write only len characters.
*
* @since 2.9
*/
public void writeString(Reader reader, int len) throws IOException {
// Let's implement this as "unsupported" to make it easier to add new parser impls
_reportUnsupportedOperation();
}
/**
* Method for outputting a String value. Depending on context
* this means either array element, (object) field value or
* a stand alone String; but in all cases, String will be
* surrounded in double quotes, and contents will be properly
* escaped as required by JSON specification.
*/
public abstract void writeString(char[] text, int offset, int len) throws IOException;
/**
* Method similar to {@link #writeString(String)}, but that takes
* {@link SerializableString} which can make this potentially
* more efficient to call as generator may be able to reuse
* quoted and/or encoded representation.
*data
MUST provide at least
* that many bytes: if not, an exception will be thrown.
* Note that implementations
* need not support cases where length is not known in advance; this
* depends on underlying data format: JSON output does NOT require length,
* other formats may.
*
* @return Number of bytes read from data
and written as binary payload
*
* @since 2.1
*/
public abstract int writeBinary(Base64Variant bv,
InputStream data, int dataLength) throws IOException;
/*
/**********************************************************
/* Public API, write methods, numeric
/**********************************************************
*/
/**
* Method for outputting given value as JSON number.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*
* @since 2.2
*/
public void writeNumber(short v) throws IOException { writeNumber((int) v); }
/**
* Method for outputting given value as JSON number.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*/
public abstract void writeNumber(int v) throws IOException;
/**
* Method for outputting given value as JSON number.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*/
public abstract void writeNumber(long v) throws IOException;
/**
* Method for outputting given value as JSON number.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*/
public abstract void writeNumber(BigInteger v) throws IOException;
/**
* Method for outputting indicate JSON numeric value.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*/
public abstract void writeNumber(double v) throws IOException;
/**
* Method for outputting indicate JSON numeric value.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*/
public abstract void writeNumber(float v) throws IOException;
/**
* Method for outputting indicate JSON numeric value.
* Can be called in any context where a value is expected
* (Array value, Object field value, root-level value).
* Additional white space may be added around the value
* if pretty-printing is enabled.
*
* @param v Number value to write
*/
public abstract void writeNumber(BigDecimal v) throws IOException;
/**
* Write method that can be used for custom numeric types that can
* not be (easily?) converted to "standard" Java number types.
* Because numbers are not surrounded by double quotes, regular
* {@link #writeString} method can not be used; nor
* {@link #writeRaw} because that does not properly handle
* value separators needed in Array or Object contexts.
*
* writeFieldName(fieldName);
* writeString(value);
*
*
* writeFieldName(fieldName);
* writeBoolean(value);
*
*/
public final void writeBooleanField(String fieldName, boolean value) throws IOException {
writeFieldName(fieldName);
writeBoolean(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has JSON literal value null. Equivalent to:
*
* writeFieldName(fieldName);
* writeNull();
*
*/
public final void writeNullField(String fieldName) throws IOException {
writeFieldName(fieldName);
writeNull();
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value. Equivalent to:
*
* writeFieldName(fieldName);
* writeNumber(value);
*
*/
public final void writeNumberField(String fieldName, int value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value. Equivalent to:
*
* writeFieldName(fieldName);
* writeNumber(value);
*
*/
public final void writeNumberField(String fieldName, long value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value. Equivalent to:
*
* writeFieldName(fieldName);
* writeNumber(value);
*
*/
public final void writeNumberField(String fieldName, double value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value. Equivalent to:
*
* writeFieldName(fieldName);
* writeNumber(value);
*
*/
public final void writeNumberField(String fieldName, float value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that has the specified numeric value.
* Equivalent to:
*
* writeFieldName(fieldName);
* writeNumber(value);
*
*/
public final void writeNumberField(String fieldName, BigDecimal value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
/**
* Convenience method for outputting a field entry ("member")
* that contains specified data in base64-encoded form.
* Equivalent to:
*
* writeFieldName(fieldName);
* writeBinary(value);
*
*/
public final void writeBinaryField(String fieldName, byte[] data) throws IOException {
writeFieldName(fieldName);
writeBinary(data);
}
/**
* Convenience method for outputting a field entry ("member")
* (that will contain a JSON Array value), and the START_ARRAY marker.
* Equivalent to:
*
* writeFieldName(fieldName);
* writeStartArray();
*
*
* writeFieldName(fieldName);
* writeStartObject();
*
*
* writeFieldName(fieldName);
* writeObject(pojo);
*
*/
public final void writeObjectField(String fieldName, Object pojo) throws IOException {
writeFieldName(fieldName);
writeObject(pojo);
}
/**
* Method called to indicate that a property in this position was
* skipped. It is usually only called for generators that return
* false
from {@link #canOmitFields()}.
*
*
*transient
since 2.2 so that Location itself is Serializable.
*/
final transient Object _sourceRef;
public JsonLocation(Object srcRef, long totalChars, int lineNr, int colNr)
{
/* Unfortunately, none of legal encodings are straight single-byte
* encodings. Could determine offset for UTF-16/UTF-32, but the
* most important one is UTF-8...
* so for now, we'll just not report any real byte count
*/
this(srcRef, -1L, totalChars, lineNr, colNr);
}
public JsonLocation(Object sourceRef, long totalBytes, long totalChars,
int lineNr, int columnNr)
{
_sourceRef = sourceRef;
_totalBytes = totalBytes;
_totalChars = totalChars;
_lineNr = lineNr;
_columnNr = columnNr;
}
/**
* Reference to the original resource being read, if one available.
* For example, when a parser has been constructed by passing
* a {@link java.io.File} instance, this method would return
* that File. Will return null if no such reference is available,
* for example when {@link java.io.InputStream} was used to
* construct the parser instance.
*/
public Object getSourceRef() { return _sourceRef; }
/**
* @return Line number of the location (1-based)
*/
public int getLineNr() { return _lineNr; }
/**
* @return Column number of the location (1-based)
*/
public int getColumnNr() { return _columnNr; }
/**
* @return Character offset within underlying stream, reader or writer,
* if available; -1 if not.
*/
public long getCharOffset() { return _totalChars; }
/**
* @return Byte offset within underlying stream, reader or writer,
* if available; -1 if not.
*/
public long getByteOffset()
{
return _totalBytes;
}
/**
* Accessor for getting a textual description of source reference
* (Object returned by {@link #getSourceRef()}), as included in
* description returned by {@link #toString()}.
*
*
*["value1",,"value3",]
* as ["value1", null, "value3", null]
* [true,true,]
is equivalent to
* [true, true]
and {"a": true,}
is equivalent to
* {"a": true}
.
* ALLOW_MISSING_VALUES
, this feature takes priority, and
* the final trailing comma in an array declaration does not imply a missing
* (null
) value. For example, when both ALLOW_MISSING_VALUES
* and ALLOW_TRAILING_COMMA
are enabled, [true,true,]
is
* equivalent to [true, true]
, and [true,true,,]
is equivalent to
* [true, true, null]
.
*
* getParsingContext().getCurrentValue();
*
*
* getParsingContext().setCurrentValue(v);
*
*
* @since 2.5
*/
public void setCurrentValue(Object v) {
JsonStreamContext ctxt = getParsingContext();
if (ctxt != null) {
ctxt.setCurrentValue(v);
}
}
/**
* Sets the payload to be passed if {@link JsonParseException} is thrown.
*
* @since 2.8
*/
public void setRequestPayloadOnError(RequestPayload payload) {
_requestPayload = payload;
}
/**
* Sets the byte[] request payload and the charset
*
* @since 2.8
*/
public void setRequestPayloadOnError(byte[] payload, String charset) {
_requestPayload = (payload == null) ? null : new RequestPayload(payload, charset);
}
/**
* Sets the String request payload
*
* @since 2.8
*/
public void setRequestPayloadOnError(String payload) {
_requestPayload = (payload == null) ? null : new RequestPayload(payload);
}
/*
/**********************************************************
/* Format support
/**********************************************************
*/
/**
* Method to call to make this parser use specified schema. Method must
* be called before trying to parse any content, right after parser instance
* has been created.
* Note that not all parsers support schemas; and those that do usually only
* accept specific types of schemas: ones defined for data format parser can read.
*false
returned)
* input is read by blocking
*
* @since 2.9
*/
public boolean canParseAsync() { return false; }
/**
* Method that will either return a feeder instance (if parser uses
* non-blocking, aka asynchronous access); or null
for
* parsers that use blocking I/O.
*
* @since 2.9
*/
public NonBlockingInputFeeder getNonBlockingInputFeeder() {
return null;
}
/*
/**********************************************************
/* Versioned
/**********************************************************
*/
/**
* Accessor for getting version of the core package, given a parser instance.
* Left for sub-classes to implement.
*/
@Override
public abstract Version version();
/*
/**********************************************************
/* Closeable implementation
/**********************************************************
*/
/**
* Closes the parser so that no further iteration or data access
* can be made; will also close the underlying input source
* if parser either owns the input source, or feature
* {@link Feature#AUTO_CLOSE_SOURCE} is enabled.
* Whether parser owns the input source depends on factory
* method that was used to construct instance (so check
* {@link com.fasterxml.jackson.core.JsonFactory} for details,
* but the general
* idea is that if caller passes in closable resource (such
* as {@link InputStream} or {@link Reader}) parser does NOT
* own the source; but if it passes a reference (such as
* {@link java.io.File} or {@link java.net.URL} and creates
* stream or reader it does own them.
*/
@Override
public abstract void close() throws IOException;
/**
* Method that can be called to determine whether this parser
* is closed or not. If it is closed, no new tokens can be
* retrieved by calling {@link #nextToken} (and the underlying
* stream may be closed). Closing may be due to an explicit
* call to {@link #close} or because parser has encountered
* end of input.
*/
public abstract boolean isClosed();
/*
/**********************************************************
/* Public API, simple location, context accessors
/**********************************************************
*/
/**
* Method that can be used to access current parsing context reader
* is in. There are 3 different types: root, array and object contexts,
* with slightly different available information. Contexts are
* hierarchically nested, and can be used for example for figuring
* out part of the input document that correspond to specific
* array or object (for highlighting purposes, or error reporting).
* Contexts can also be used for simple xpath-like matching of
* input, if so desired.
*/
public abstract JsonStreamContext getParsingContext();
/**
* Method that return the starting location of the current
* token; that is, position of the first character from input
* that starts the current token.
*/
public abstract JsonLocation getTokenLocation();
/**
* Method that returns location of the last processed character;
* usually for error reporting purposes.
*/
public abstract JsonLocation getCurrentLocation();
/*
/**********************************************************
/* Buffer handling
/**********************************************************
*/
/**
* Method that can be called to push back any content that
* has been read but not consumed by the parser. This is usually
* done after reading all content of interest using parser.
* Content is released by writing it to given stream if possible;
* if underlying input is byte-based it can released, if not (char-based)
* it can not.
*
* @return -1 if the underlying content source is not byte based
* (that is, input can not be sent to {@link OutputStream};
* otherwise number of bytes released (0 if there was nothing to release)
*
* @throws IOException if write to stream threw exception
*/
public int releaseBuffered(OutputStream out) throws IOException {
return -1;
}
/**
* Method that can be called to push back any content that
* has been read but not consumed by the parser.
* This is usually
* done after reading all content of interest using parser.
* Content is released by writing it to given writer if possible;
* if underlying input is char-based it can released, if not (byte-based)
* it can not.
*
* @return -1 if the underlying content source is not char-based
* (that is, input can not be sent to {@link Writer};
* otherwise number of chars released (0 if there was nothing to release)
*
* @throws IOException if write using Writer threw exception
*/
public int releaseBuffered(Writer w) throws IOException { return -1; }
/*
/***************************************************
/* Public API, configuration
/***************************************************
*/
/**
* Method for enabling specified parser feature
* (check {@link Feature} for list of features)
*/
public JsonParser enable(Feature f) {
_features |= f.getMask();
return this;
}
/**
* Method for disabling specified feature
* (check {@link Feature} for list of features)
*/
public JsonParser disable(Feature f) {
_features &= ~f.getMask();
return this;
}
/**
* Method for enabling or disabling specified feature
* (check {@link Feature} for list of features)
*/
public JsonParser configure(Feature f, boolean state) {
if (state) enable(f); else disable(f);
return this;
}
/**
* Method for checking whether specified {@link Feature} is enabled.
*/
public boolean isEnabled(Feature f) { return f.enabledIn(_features); }
/**
* Method for checking whether specified {@link Feature} is enabled.
*
* @since 2.10
*/
public boolean isEnabled(StreamReadFeature f) { return f.mappedFeature().enabledIn(_features); }
/**
* Bulk access method for getting state of all standard {@link Feature}s.
*
* @return Bit mask that defines current states of all standard {@link Feature}s.
*
* @since 2.3
*/
public int getFeatureMask() { return _features; }
/**
* Bulk set method for (re)setting states of all standard {@link Feature}s
*
* @return This parser object, to allow chaining of calls
*
* @since 2.3
*
* @deprecated Since 2.7, use {@link #overrideStdFeatures(int, int)} instead
*/
@Deprecated
public JsonParser setFeatureMask(int mask) {
_features = mask;
return this;
}
/**
* Bulk set method for (re)setting states of features specified by mask
.
* Functionally equivalent to
*
* int oldState = getFeatureMask();
* int newState = (oldState & ~mask) | (values & mask);
* setFeatureMask(newState);
*
* but preferred as this lets caller more efficiently specify actual changes made.
*
* @param values Bit mask of set/clear state for features to change
* @param mask Bit mask of features to change
*
* @since 2.6
*/
public JsonParser overrideStdFeatures(int values, int mask) {
int newState = (_features & ~mask) | (values & mask);
return setFeatureMask(newState);
}
/**
* Bulk access method for getting state of all {@link FormatFeature}s, format-specific
* on/off configuration settings.
*
* @return Bit mask that defines current states of all standard {@link FormatFeature}s.
*
* @since 2.6
*/
public int getFormatFeatures() {
return 0;
}
/**
* Bulk set method for (re)setting states of {@link FormatFeature}s,
* by specifying values (set / clear) along with a mask, to determine
* which features to change, if any.
*
* return (nextToken() == JsonToken.FIELD_NAME) && str.getValue().equals(getCurrentName());
*
* but may be faster for parser to verify, and can therefore be used if caller
* expects to get such a property name from input next.
*
* @param str Property name to compare next token to (if next token is
* JsonToken.FIELD_NAME
)
*/
public boolean nextFieldName(SerializableString str) throws IOException {
return (nextToken() == JsonToken.FIELD_NAME) && str.getValue().equals(getCurrentName());
}
/**
* Method that fetches next token (as if calling {@link #nextToken}) and
* verifies whether it is {@link JsonToken#FIELD_NAME}; if it is,
* returns same as {@link #getCurrentName()}, otherwise null.
*
* @since 2.5
*/
public String nextFieldName() throws IOException {
return (nextToken() == JsonToken.FIELD_NAME) ? getCurrentName() : null;
}
/**
* Method that fetches next token (as if calling {@link #nextToken}) and
* if it is {@link JsonToken#VALUE_STRING} returns contained String value;
* otherwise returns null.
* It is functionally equivalent to:
*
* return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
*
* but may be faster for parser to process, and can therefore be used if caller
* expects to get a String value next from input.
*/
public String nextTextValue() throws IOException {
return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
}
/**
* Method that fetches next token (as if calling {@link #nextToken}) and
* if it is {@link JsonToken#VALUE_NUMBER_INT} returns 32-bit int value;
* otherwise returns specified default value
* It is functionally equivalent to:
*
* return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
*
* but may be faster for parser to process, and can therefore be used if caller
* expects to get an int value next from input.
*/
public int nextIntValue(int defaultValue) throws IOException {
return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
}
/**
* Method that fetches next token (as if calling {@link #nextToken}) and
* if it is {@link JsonToken#VALUE_NUMBER_INT} returns 64-bit long value;
* otherwise returns specified default value
* It is functionally equivalent to:
*
* return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
*
* but may be faster for parser to process, and can therefore be used if caller
* expects to get a long value next from input.
*/
public long nextLongValue(long defaultValue) throws IOException {
return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
}
/**
* Method that fetches next token (as if calling {@link #nextToken}) and
* if it is {@link JsonToken#VALUE_TRUE} or {@link JsonToken#VALUE_FALSE}
* returns matching Boolean value; otherwise return null.
* It is functionally equivalent to:
*
* JsonToken t = nextToken();
* if (t == JsonToken.VALUE_TRUE) return Boolean.TRUE;
* if (t == JsonToken.VALUE_FALSE) return Boolean.FALSE;
* return null;
*
* but may be faster for parser to process, and can therefore be used if caller
* expects to get a Boolean value next from input.
*/
public Boolean nextBooleanValue() throws IOException {
JsonToken t = nextToken();
if (t == JsonToken.VALUE_TRUE) { return Boolean.TRUE; }
if (t == JsonToken.VALUE_FALSE) { return Boolean.FALSE; }
return null;
}
/**
* Method that will skip all child tokens of an array or
* object token that the parser currently points to,
* iff stream points to
* {@link JsonToken#START_OBJECT} or {@link JsonToken#START_ARRAY}.
* If not, it will do nothing.
* After skipping, stream will point to matching
* {@link JsonToken#END_OBJECT} or {@link JsonToken#END_ARRAY}
* (possibly skipping nested pairs of START/END OBJECT/ARRAY tokens
* as well as value tokens).
* The idea is that after calling this method, application
* will call {@link #nextToken} to point to the next
* available token, if any.
*/
public abstract JsonParser skipChildren() throws IOException;
/**
* Method that may be used to force full handling of the current token
* so that even if lazy processing is enabled, the whole contents are
* read for possible retrieval. This is usually used to ensure that
* the token end location is available, as well as token contents
* (similar to what calling, say {@link #getTextCharacters()}, would
* achieve).
*int
instead of {@link JsonToken} (enum value).
*int
matching one of constants from {@link JsonTokenId}.
*/
public int currentTokenId() {
return getCurrentTokenId();
}
/**
* Alias for {@link #currentToken()}, will be deprecated in Jackson 2.9
*/
public abstract JsonToken getCurrentToken();
/**
* Alias for {@link #currentTokenId()}, will be deprecated in Jackson 2.9
*/
public abstract int getCurrentTokenId();
/**
* Method for checking whether parser currently points to
* a token (and data for that token is available).
* Equivalent to check for parser.getCurrentToken() != null
.
*
* @return True if the parser just returned a valid
* token via {@link #nextToken}; false otherwise (parser
* was just constructed, encountered end-of-input
* and returned null from {@link #nextToken}, or the token
* has been consumed)
*/
public abstract boolean hasCurrentToken();
/**
* Method that is functionally equivalent to:
*
* return currentTokenId() == id
*
* but may be more efficiently implemented.
*
* return currentToken() == t
*
* but may be more efficiently implemented.
*
* currentToken() == JsonToken.START_ARRAY
*
* but may be overridden by custom parser implementations.
*
* @return True if the current token can be considered as a
* start-array marker (such {@link JsonToken#START_ARRAY});
* false if not.
*/
public boolean isExpectedStartArrayToken() { return currentToken() == JsonToken.START_ARRAY; }
/**
* Similar to {@link #isExpectedStartArrayToken()}, but checks whether stream
* currently points to {@link JsonToken#START_OBJECT}.
*
* @since 2.5
*/
public boolean isExpectedStartObjectToken() { return currentToken() == JsonToken.START_OBJECT; }
/**
* Access for checking whether current token is a numeric value token, but
* one that is of "not-a-number" (NaN) variety (including both "NaN" AND
* positive/negative infinity!): not supported by all formats,
* but often supported for {@link JsonToken#VALUE_NUMBER_FLOAT}.
* NOTE: roughly equivalent to calling !Double.isFinite()
* on value you would get from calling {@link #getDoubleValue()}.
*
* @since 2.9
*/
public boolean isNaN() throws IOException {
return false;
}
/*
/**********************************************************
/* Public API, token state overrides
/**********************************************************
*/
/**
* Method called to "consume" the current token by effectively
* removing it so that {@link #hasCurrentToken} returns false, and
* {@link #getCurrentToken} null).
* Cleared token value can still be accessed by calling
* {@link #getLastClearedToken} (if absolutely needed), but
* usually isn't.
*
* writer.write(parser.getText());
*
* but should typically be more efficient as longer content does need to
* be combined into a single String
to return, and write
* can occur directly from intermediate buffers Jackson uses.
*
* @return The number of characters written to the Writer
*
* @since 2.8
*/
public int getText(Writer writer) throws IOException, UnsupportedOperationException
{
String str = getText();
if (str == null) {
return 0;
}
writer.write(str);
return str.length();
}
/**
* Method similar to {@link #getText}, but that will return
* underlying (unmodifiable) character array that contains
* textual value, instead of constructing a String object
* to contain this information.
* Note, however, that:
*
*
*MappingJsonFactory
(from "jackson-databind" jar)
* but not for {@link JsonFactory} (unless its setCodec
* method has been explicitly called).
*MappingJsonFactory
(defined in 'jackson-databind' bundle)
* but not for {@link JsonFactory} (unless its setCodec
* method has been explicitly called).
*
*
*
* @param tail {@link JsonPointer} instance to append to this one, to create a new pointer instance
*
* @return Either `this` instance, `tail`, or a newly created combination, as per description above.
*/
public JsonPointer append(JsonPointer tail) {
if (this == EMPTY) {
return tail;
}
if (tail == EMPTY) {
return this;
}
// 21-Mar-2017, tatu: Not superbly efficient; could probably improve by not concatenating,
// re-decoding -- by stitching together segments -- but for now should be fine.
String currentJsonPointer = _asString;
if (currentJsonPointer.endsWith("/")) {
//removes final slash
currentJsonPointer = currentJsonPointer.substring(0, currentJsonPointer.length()-1);
}
return compile(currentJsonPointer + tail._asString);
}
/**
* Method that may be called to see if the pointer would match property
* (of a JSON Object) with given name.
*
* @since 2.5
*/
public boolean matchesProperty(String name) {
return (_nextSegment != null) && _matchingPropertyName.equals(name);
}
public JsonPointer matchProperty(String name) {
if ((_nextSegment != null) && _matchingPropertyName.equals(name)) {
return _nextSegment;
}
return null;
}
/**
* Method that may be called to see if the pointer would match
* array element (of a JSON Array) with given index.
*
* @since 2.5
*/
public boolean matchesElement(int index) {
return (index == _matchingElementIndex) && (index >= 0);
}
/**
* @since 2.6
*/
public JsonPointer matchElement(int index) {
if ((index != _matchingElementIndex) || (index < 0)) {
return null;
}
return _nextSegment;
}
/**
* Accessor for getting a "sub-pointer", instance where current segment
* has been removed and pointer includes rest of segments.
* For matching state, will return null.
*/
public JsonPointer tail() {
return _nextSegment;
}
/**
* Accessor for getting a pointer instance that is identical to this
* instance except that the last segment has been dropped.
* For example, for JSON Point "/root/branch/leaf", this method would
* return pointer "/root/branch" (compared to {@link #tail()} that
* would return "/branch/leaf").
* For leaf
*
* @since 2.5
*/
public JsonPointer head() {
JsonPointer h = _head;
if (h == null) {
if (this != EMPTY) {
h = _constructHead();
}
_head = h;
}
return h;
}
/*
/**********************************************************
/* Standard method overrides
/**********************************************************
*/
@Override public String toString() { return _asString; }
@Override public int hashCode() { return _asString.hashCode(); }
@Override public boolean equals(Object o) {
if (o == this) return true;
if (o == null) return false;
if (!(o instanceof JsonPointer)) return false;
return _asString.equals(((JsonPointer) o)._asString);
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
private final static int _parseIndex(String str) {
final int len = str.length();
// [core#133]: beware of super long indexes; assume we never
// have arrays over 2 billion entries so ints are fine.
if (len == 0 || len > 10) {
return -1;
}
// [core#176]: no leading zeroes allowed
char c = str.charAt(0);
if (c <= '0') {
return (len == 1 && c == '0') ? 0 : -1;
}
if (c > '9') {
return -1;
}
for (int i = 1; i < len; ++i) {
c = str.charAt(i);
if (c > '9' || c < '0') {
return -1;
}
}
if (len == 10) {
long l = NumberInput.parseLong(str);
if (l > Integer.MAX_VALUE) {
return -1;
}
}
return NumberInput.parseInt(str);
}
protected static JsonPointer _parseTail(String input) {
final int end = input.length();
// first char is the contextual slash, skip
for (int i = 1; i < end; ) {
char c = input.charAt(i);
if (c == '/') { // common case, got a segment
return new JsonPointer(input, input.substring(1, i),
_parseTail(input.substring(i)));
}
++i;
// quoting is different; offline this case
if (c == '~' && i < end) { // possibly, quote
return _parseQuotedTail(input, i);
}
// otherwise, loop on
}
// end of the road, no escapes
return new JsonPointer(input, input.substring(1), EMPTY);
}
/**
* Method called to parse tail of pointer path, when a potentially
* escaped character has been seen.
*
* @param input Full input for the tail being parsed
* @param i Offset to character after tilde
*/
protected static JsonPointer _parseQuotedTail(String input, int i) {
final int end = input.length();
StringBuilder sb = new StringBuilder(Math.max(16, end));
if (i > 2) {
sb.append(input, 1, i-1);
}
_appendEscape(sb, input.charAt(i++));
while (i < end) {
char c = input.charAt(i);
if (c == '/') { // end is nigh!
return new JsonPointer(input, sb.toString(),
_parseTail(input.substring(i)));
}
++i;
if (c == '~' && i < end) {
_appendEscape(sb, input.charAt(i++));
continue;
}
sb.append(c);
}
// end of the road, last segment
return new JsonPointer(input, sb.toString(), EMPTY);
}
protected JsonPointer _constructHead()
{
// ok; find out who we are to drop
JsonPointer last = last();
if (last == this) {
return EMPTY;
}
// and from that, length of suffix to drop
int suffixLength = last._asString.length();
JsonPointer next = _nextSegment;
return new JsonPointer(_asString.substring(0, _asString.length() - suffixLength), _matchingPropertyName,
_matchingElementIndex, next._constructHead(suffixLength, last));
}
protected JsonPointer _constructHead(int suffixLength, JsonPointer last)
{
if (this == last) {
return EMPTY;
}
JsonPointer next = _nextSegment;
String str = _asString;
return new JsonPointer(str.substring(0, str.length() - suffixLength), _matchingPropertyName,
_matchingElementIndex, next._constructHead(suffixLength, last));
}
private static void _appendEscape(StringBuilder sb, char c) {
if (c == '0') {
c = '~';
} else if (c == '1') {
c = '/';
} else {
sb.append('~');
}
sb.append(c);
}
}
JsonProcessingException.java 0000664 0000000 0000000 00000010341 13561642473 0033747 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*/
package com.fasterxml.jackson.core;
/**
* Intermediate base class for all problems encountered when
* processing (parsing, generating) JSON content
* that are not pure I/O problems.
* Regular {@link java.io.IOException}s will be passed through as is.
* Sub-class of {@link java.io.IOException} for convenience.
*/
public class JsonProcessingException extends java.io.IOException
{
final static long serialVersionUID = 123; // Stupid eclipse...
protected JsonLocation _location;
protected JsonProcessingException(String msg, JsonLocation loc, Throwable rootCause) {
super(msg);
if (rootCause != null) {
initCause(rootCause);
}
_location = loc;
}
protected JsonProcessingException(String msg) {
super(msg);
}
protected JsonProcessingException(String msg, JsonLocation loc) {
this(msg, loc, null);
}
protected JsonProcessingException(String msg, Throwable rootCause) {
this(msg, null, rootCause);
}
protected JsonProcessingException(Throwable rootCause) {
this(null, null, rootCause);
}
/*
/**********************************************************
/* Extended API
/**********************************************************
*/
public JsonLocation getLocation() { return _location; }
/**
* Method that allows to remove context information from this exception's message.
* Useful when you are parsing security-sensitive data and don't want original data excerpts
* to be present in Jackson parser error messages.
*
* @since 2.9
*/
public void clearLocation() { _location = null; }
/**
* Method that allows accessing the original "message" argument,
* without additional decorations (like location information)
* that overridden {@link #getMessage} adds.
*
* @since 2.1
*/
public String getOriginalMessage() { return super.getMessage(); }
/**
* Method that allows accessing underlying processor that triggered
* this exception; typically either {@link JsonParser} or {@link JsonGenerator}
* for exceptions that originate from streaming API.
* Note that it is possible that `null` may be returned if code throwing
* exception either has no access to processor; or has not been retrofitted
* to set it; this means that caller needs to take care to check for nulls.
* Subtypes override this method with co-variant return type, for more
* type-safe access.
*
* @return Originating processor, if available; null if not.
*
* @since 2.7
*/
public Object getProcessor() { return null; }
/*
/**********************************************************
/* Methods for sub-classes to use, override
/**********************************************************
*/
/**
* Accessor that sub-classes can override to append additional
* information right after the main message, but before
* source location information.
*/
protected String getMessageSuffix() { return null; }
/*
/**********************************************************
/* Overrides of standard methods
/**********************************************************
*/
/**
* Default method overridden so that we can add location information
*/
@Override public String getMessage() {
String msg = super.getMessage();
if (msg == null) {
msg = "N/A";
}
JsonLocation loc = getLocation();
String suffix = getMessageSuffix();
// mild optimization, if nothing extra is needed:
if (loc != null || suffix != null) {
StringBuilder sb = new StringBuilder(100);
sb.append(msg);
if (suffix != null) {
sb.append(suffix);
}
if (loc != null) {
sb.append('\n');
sb.append(" at ");
sb.append(loc.toString());
}
msg = sb.toString();
}
return msg;
}
@Override public String toString() { return getClass().getName()+": "+getMessage(); }
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonStreamContext.java 0000664 0000000 0000000 00000021633 13561642473 0032641 0 ustar 00root root 0000000 0000000 /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*/
package com.fasterxml.jackson.core;
import com.fasterxml.jackson.core.io.CharTypes;
/**
* Shared base class for streaming processing contexts used during
* reading and writing of Json content using Streaming API.
* This context is also exposed to applications:
* context object can be used by applications to get an idea of
* relative position of the parser/generator within json content
* being processed. This allows for some contextual processing: for
* example, output within Array context can differ from that of
* Object context.
*/
public abstract class JsonStreamContext
{
// // // Type constants used internally
protected final static int TYPE_ROOT = 0;
protected final static int TYPE_ARRAY = 1;
protected final static int TYPE_OBJECT = 2;
protected int _type;
/**
* Index of the currently processed entry. Starts with -1 to signal
* that no entries have been started, and gets advanced each
* time a new entry is started, either by encountering an expected
* separator, or with new values if no separators are expected
* (the case for root context).
*/
protected int _index;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
protected JsonStreamContext() { }
/**
* Copy constructor used by sub-classes for creating copies for
* buffering.
*
* @since 2.9
*/
protected JsonStreamContext(JsonStreamContext base) {
_type = base._type;
_index = base._index;
}
/**
* @since 2.9
*/
protected JsonStreamContext(int type, int index) {
_type = type;
_index = index;
}
/*
/**********************************************************
/* Public API, accessors
/**********************************************************
*/
/**
* Accessor for finding parent context of this context; will
* return null for root context.
*/
public abstract JsonStreamContext getParent();
/**
* Method that returns true if this context is an Array context;
* that is, content is being read from or written to a Json Array.
*/
public final boolean inArray() { return _type == TYPE_ARRAY; }
/**
* Method that returns true if this context is a Root context;
* that is, content is being read from or written to without
* enclosing array or object structure.
*/
public final boolean inRoot() { return _type == TYPE_ROOT; }
/**
* Method that returns true if this context is an Object context;
* that is, content is being read from or written to a Json Object.
*/
public final boolean inObject() { return _type == TYPE_OBJECT; }
/**
* Method for accessing simple type description of current context;
* either ROOT (for root-level values), OBJECT (for field names and
* values of JSON Objects) or ARRAY (for values of JSON Arrays)
*
* @deprecated Since 2.8 use {@link #typeDesc} instead
*/
@Deprecated // since 2.8
public final String getTypeDesc() {
switch (_type) {
case TYPE_ROOT: return "ROOT";
case TYPE_ARRAY: return "ARRAY";
case TYPE_OBJECT: return "OBJECT";
}
return "?";
}
/**
* @since 2.8
*/
public String typeDesc() {
switch (_type) {
case TYPE_ROOT: return "root";
case TYPE_ARRAY: return "Array";
case TYPE_OBJECT: return "Object";
}
return "?";
}
/**
* @return Number of entries that are complete and started.
*/
public final int getEntryCount() { return _index + 1; }
/**
* @return Index of the currently processed entry, if any
*/
public final int getCurrentIndex() { return (_index < 0) ? 0 : _index; }
/**
* Method that may be called to verify whether this context has valid index:
* will return `false` before the first entry of Object context or before
* first element of Array context; otherwise returns `true`.
*
* @since 2.9
*/
public boolean hasCurrentIndex() { return _index >= 0; }
/**
* Method that may be called to check if this context is either:
*
*
* and if so, return `true`; otherwise return `false`. Latter case includes
* Root context (always), and Object/Array contexts before any entries/elements
* have been read or written.
*FIELD_NAME
and value events that directly
* follow field names; null for root level and array values.
*/
public abstract String getCurrentName();
/**
* @since 2.9
*/
public boolean hasCurrentName() { return getCurrentName() != null; }
/**
* Method for accessing currently active value being used by data-binding
* (as the source of streaming data to write, or destination of data being
* read), at this level in hierarchy.
*JsonReadContext
in 2.9, to allow use for
* "non-standard" read contexts.
*
* @since 2.9
*/
public JsonLocation getStartLocation(Object srcRef) {
return JsonLocation.NA;
}
/**
* Overridden to provide developer readable "JsonPath" representation
* of the context.
*
* @since 2.9
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder(64);
switch (_type) {
case TYPE_ROOT:
sb.append("/");
break;
case TYPE_ARRAY:
sb.append('[');
sb.append(getCurrentIndex());
sb.append(']');
break;
case TYPE_OBJECT:
default:
sb.append('{');
String currentName = getCurrentName();
if (currentName != null) {
sb.append('"');
CharTypes.appendQuoted(sb, currentName);
sb.append('"');
} else {
sb.append('?');
}
sb.append('}');
break;
}
return sb.toString();
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonToken.java 0000664 0000000 0000000 00000014737 13561642473 0031130 0 ustar 00root root 0000000 0000000 /* Jackson JSON-processor.
*
* Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
*/
package com.fasterxml.jackson.core;
/**
* Enumeration for basic token types used for returning results
* of parsing JSON content.
*/
public enum JsonToken
{
/* Some notes on implementation:
*
* - Entries are to be ordered such that start/end array/object
* markers come first, then field name marker (if any), and
* finally scalar value tokens. This is assumed by some
* typing checks.
*/
/**
* NOT_AVAILABLE can be returned if {@link JsonParser}
* implementation can not currently return the requested
* token (usually next one), or even if any will be
* available, but that may be able to determine this in
* future. This is the case with non-blocking parsers --
* they can not block to wait for more data to parse and
* must return something.
*/
NOT_AVAILABLE(null, JsonTokenId.ID_NOT_AVAILABLE),
/**
* START_OBJECT is returned when encountering '{'
* which signals starting of an Object value.
*/
START_OBJECT("{", JsonTokenId.ID_START_OBJECT),
/**
* END_OBJECT is returned when encountering '}'
* which signals ending of an Object value
*/
END_OBJECT("}", JsonTokenId.ID_END_OBJECT),
/**
* START_ARRAY is returned when encountering '['
* which signals starting of an Array value
*/
START_ARRAY("[", JsonTokenId.ID_START_ARRAY),
/**
* END_ARRAY is returned when encountering ']'
* which signals ending of an Array value
*/
END_ARRAY("]", JsonTokenId.ID_END_ARRAY),
/**
* FIELD_NAME is returned when a String token is encountered
* as a field name (same lexical value, different function)
*/
FIELD_NAME(null, JsonTokenId.ID_FIELD_NAME),
/**
* Placeholder token returned when the input source has a concept
* of embedded Object that are not accessible as usual structure
* (of starting with {@link #START_OBJECT}, having values, ending with
* {@link #END_OBJECT}), but as "raw" objects.
*JsonNode
-based JSON trees, Maps, Lists and such).
*/
VALUE_EMBEDDED_OBJECT(null, JsonTokenId.ID_EMBEDDED_OBJECT),
/**
* VALUE_STRING is returned when a String token is encountered
* in value context (array element, field value, or root-level
* stand-alone value)
*/
VALUE_STRING(null, JsonTokenId.ID_STRING),
/**
* VALUE_NUMBER_INT is returned when an integer numeric token is
* encountered in value context: that is, a number that does
* not have floating point or exponent marker in it (consists
* only of an optional sign, followed by one or more digits;
* or, for binary formats, is indicated as integral number
* by internal representation).
*/
VALUE_NUMBER_INT(null, JsonTokenId.ID_NUMBER_INT),
/**
* VALUE_NUMBER_FLOAT is returned when a numeric token other
* than integer is encountered: that is, a number that does
* have floating point or exponent marker in it, in addition
* to one or more digits (or, for non-textual formats,
* has internal floating-point representation).
*/
VALUE_NUMBER_FLOAT(null, JsonTokenId.ID_NUMBER_FLOAT),
/**
* VALUE_TRUE is returned when encountering literal "true" in
* value context
*/
VALUE_TRUE("true", JsonTokenId.ID_TRUE),
/**
* VALUE_FALSE is returned when encountering literal "false" in
* value context
*/
VALUE_FALSE("false", JsonTokenId.ID_FALSE),
/**
* VALUE_NULL is returned when encountering literal "null" in
* value context
*/
VALUE_NULL("null", JsonTokenId.ID_NULL),
;
final String _serialized;
final char[] _serializedChars;
final byte[] _serializedBytes;
final int _id;
final boolean _isStructStart, _isStructEnd;
final boolean _isNumber;
final boolean _isBoolean;
final boolean _isScalar;
/**
* @param token representation for this token, if there is a
* single static representation; null otherwise
*/
JsonToken(String token, int id)
{
if (token == null) {
_serialized = null;
_serializedChars = null;
_serializedBytes = null;
} else {
_serialized = token;
_serializedChars = token.toCharArray();
// It's all in ascii, can just case...
int len = _serializedChars.length;
_serializedBytes = new byte[len];
for (int i = 0; i < len; ++i) {
_serializedBytes[i] = (byte) _serializedChars[i];
}
}
_id = id;
_isBoolean = (id == JsonTokenId.ID_FALSE || id == JsonTokenId.ID_TRUE);
_isNumber = (id == JsonTokenId.ID_NUMBER_INT || id == JsonTokenId.ID_NUMBER_FLOAT);
_isStructStart = (id == JsonTokenId.ID_START_OBJECT || id == JsonTokenId.ID_START_ARRAY);
_isStructEnd = (id == JsonTokenId.ID_END_OBJECT || id == JsonTokenId.ID_END_ARRAY);
_isScalar = !_isStructStart && !_isStructEnd
&& (id != JsonTokenId.ID_FIELD_NAME)
&& (id != JsonTokenId.ID_NOT_AVAILABLE);
}
public final int id() { return _id; }
public final String asString() { return _serialized; }
public final char[] asCharArray() { return _serializedChars; }
public final byte[] asByteArray() { return _serializedBytes; }
public final boolean isNumeric() { return _isNumber; }
/**
* Accessor that is functionally equivalent to:
*
* this == JsonToken.START_OBJECT || this == JsonToken.START_ARRAY
*
*
* @since 2.3
*/
public final boolean isStructStart() { return _isStructStart; }
/**
* Accessor that is functionally equivalent to:
*
* this == JsonToken.END_OBJECT || this == JsonToken.END_ARRAY
*
*
* @since 2.3
*/
public final boolean isStructEnd() { return _isStructEnd; }
/**
* Method that can be used to check whether this token represents
* a valid non-structured value. This means all tokens other than
* Object/Array start/end markers all field names.
*/
public final boolean isScalarValue() { return _isScalar; }
public final boolean isBoolean() { return _isBoolean; }
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonTokenId.java 0000664 0000000 0000000 00000004526 13561642473 0031400 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core;
/**
* Interface defined to contain ids accessible with {@link JsonToken#id()}.
* Needed because it is impossible to define these constants in
* {@link JsonToken} itself, as static constants (oddity of how Enums
* are implemented by JVM).
*
* @since 2.3
*/
public interface JsonTokenId
{
/**
* Id used to represent {@link JsonToken#NOT_AVAILABLE}, used in
* cases where a token may become available when more input
* is available: this occurs in non-blocking use cases.
*/
public final static int ID_NOT_AVAILABLE = -1;
/**
* Id used to represent the case where no {@link JsonToken}
* is available: either because {@link JsonParser} has not been
* advanced to first token, or because no more tokens will be
* available (end-of-input or explicit closing of parser}.
*/
public final static int ID_NO_TOKEN = 0;
/**
* Id used to represent {@link JsonToken#START_OBJECT}
*/
public final static int ID_START_OBJECT = 1;
/**
* Id used to represent {@link JsonToken#END_OBJECT}
*/
public final static int ID_END_OBJECT = 2;
/**
* Id used to represent {@link JsonToken#START_ARRAY}
*/
public final static int ID_START_ARRAY = 3;
/**
* Id used to represent {@link JsonToken#END_ARRAY}
*/
public final static int ID_END_ARRAY = 4;
/**
* Id used to represent {@link JsonToken#FIELD_NAME}
*/
public final static int ID_FIELD_NAME = 5;
/**
* Id used to represent {@link JsonToken#VALUE_STRING}
*/
public final static int ID_STRING = 6;
/**
* Id used to represent {@link JsonToken#VALUE_NUMBER_INT}
*/
public final static int ID_NUMBER_INT = 7;
/**
* Id used to represent {@link JsonToken#VALUE_NUMBER_FLOAT}
*/
public final static int ID_NUMBER_FLOAT = 8;
/**
* Id used to represent {@link JsonToken#VALUE_TRUE}
*/
public final static int ID_TRUE = 9;
/**
* Id used to represent {@link JsonToken#VALUE_FALSE}
*/
public final static int ID_FALSE = 10;
/**
* Id used to represent {@link JsonToken#VALUE_NULL}
*/
public final static int ID_NULL = 11;
/**
* Id used to represent {@link JsonToken#VALUE_EMBEDDED_OBJECT}
*/
public final static int ID_EMBEDDED_OBJECT = 12;
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/JsonpCharacterEscapes.java0000664 0000000 0000000 00000002642 13561642473 0033420 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core;
import com.fasterxml.jackson.core.io.CharacterEscapes;
import com.fasterxml.jackson.core.io.SerializedString;
/**
* Convenience {@link CharacterEscapes} implementation that escapes
* Unicode characters `0x2028` and `0x2029` (in addition to characters
* escaped otherwise), which are apparently considered linefeeds as
* per newer Javascript specifications, and consequently problematic
* when using JSONP (see https://en.wikipedia.org/wiki/JSONP).
*
* @since 2.8
*/
public class JsonpCharacterEscapes extends CharacterEscapes
{
private static final long serialVersionUID = 1L;
private static final int[] asciiEscapes = CharacterEscapes.standardAsciiEscapesForJSON();
private static final SerializedString escapeFor2028 = new SerializedString("\\u2028");
private static final SerializedString escapeFor2029 = new SerializedString("\\u2029");
private static final JsonpCharacterEscapes sInstance = new JsonpCharacterEscapes();
public static JsonpCharacterEscapes instance() {
return sInstance;
}
@Override
public SerializableString getEscapeSequence(int ch)
{
switch (ch) {
case 0x2028:
return escapeFor2028;
case 0x2029:
return escapeFor2029;
default:
return null;
}
}
@Override
public int[] getEscapeCodesForAscii() {
return asciiEscapes;
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/ObjectCodec.java 0000664 0000000 0000000 00000013515 13561642473 0031353 0 ustar 00root root 0000000 0000000 /* 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).
*com.fasterxml.jackson.databind.ObjectMapper
,
* defined in the "jackson-databind".
*/
public abstract class ObjectCodec
extends TreeCodec // since 2.3
implements Versioned // since 2.3
{
protected ObjectCodec() { }
// Since 2.3
@Override
public abstract Version version();
/*
/**********************************************************
/* API for de-serialization (JSON-to-Object)
/**********************************************************
*/
/**
* Method to deserialize JSON content into a non-container
* type (it can be an array type, however): typically a bean, array
* or a wrapper type (like {@link java.lang.Boolean}).
*
* getValue().length();
*
*/
int charLength();
/*
/**********************************************************
/* Accessors for byte sequences
/**********************************************************
*/
/**
* Returns JSON quoted form of the String, as character array.
* Result can be embedded as-is in textual JSON as property name or JSON String.
*/
char[] asQuotedChars();
/**
* Returns UTF-8 encoded version of unquoted String.
* Functionally equivalent to (but more efficient than):
*
* getValue().getBytes("UTF-8");
*
*/
byte[] asUnquotedUTF8();
/**
* Returns UTF-8 encoded version of JSON-quoted String.
* Functionally equivalent to (but more efficient than):
*
* new String(asQuotedChars()).getBytes("UTF-8");
*
*/
byte[] asQuotedUTF8();
/*
/**********************************************************
/* Helper methods for appending byte/char sequences
/**********************************************************
*/
/**
* Method that will append quoted UTF-8 bytes of this String into given
* buffer, if there is enough room; if not, returns -1.
* Functionally equivalent to:
*
* byte[] bytes = str.asQuotedUTF8();
* System.arraycopy(bytes, 0, buffer, offset, bytes.length);
* return bytes.length;
*
*
* @return Number of bytes appended, if successful, otherwise -1
*/
int appendQuotedUTF8(byte[] buffer, int offset);
/**
* Method that will append quoted characters of this String into given
* buffer. Functionally equivalent to:
*
* char[] ch = str.asQuotedChars();
* System.arraycopy(ch, 0, buffer, offset, ch.length);
* return ch.length;
*
*
* @return Number of characters appended, if successful, otherwise -1
*/
int appendQuoted(char[] buffer, int offset);
/**
* Method that will append unquoted ('raw') UTF-8 bytes of this String into given
* buffer. Functionally equivalent to:
*
* byte[] bytes = str.asUnquotedUTF8();
* System.arraycopy(bytes, 0, buffer, offset, bytes.length);
* return bytes.length;
*
*
* @return Number of bytes appended, if successful, otherwise -1
*/
int appendUnquotedUTF8(byte[] buffer, int offset);
/**
* Method that will append unquoted characters of this String into given
* buffer. Functionally equivalent to:
*
* char[] ch = str.getValue().toCharArray();
* System.arraycopy(bytes, 0, buffer, offset, ch.length);
* return ch.length;
*
*
* @return Number of characters appended, if successful, otherwise -1
*/
int appendUnquoted(char[] buffer, int offset);
/*
/**********************************************************
/* Helper methods for writing out byte sequences
/**********************************************************
*/
/**
* @return Number of bytes written
*/
int writeQuotedUTF8(OutputStream out) throws IOException;
/**
* @return Number of bytes written
*/
int writeUnquotedUTF8(OutputStream out) throws IOException;
/**
* @return Number of bytes put, if successful, otherwise -1
*/
int putQuotedUTF8(ByteBuffer buffer) throws IOException;
/**
* @return Number of bytes put, if successful, otherwise -1
*/
int putUnquotedUTF8(ByteBuffer out) throws IOException;
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java 0000664 0000000 0000000 00000013326 13561642473 0032552 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core;
import java.io.InputStream;
import java.io.Reader;
/**
* Token reader (parser) features not-specific to any particular format backend.
* Eventual replacement for non-JSON-specific {@link com.fasterxml.jackson.core.JsonParser.Feature}s.
*
* @since 2.10
*/
public enum StreamReadFeature
{
// // // Low-level I/O handling features:
/**
* Feature that determines whether parser will automatically
* close underlying input source that is NOT owned by the
* parser. If disabled, calling application has to separately
* close the underlying {@link InputStream} and {@link Reader}
* instances used to create the parser. If enabled, parser
* will handle closing, as long as parser itself gets closed:
* this happens when end-of-input is encountered, or parser
* is closed by a call to {@link JsonParser#close}.
*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).
*false
as JSON does NOT
* require stable ordering. Formats that require ordering include positional
* textual formats like CSV
, and schema-based binary formats
* like Avro
.
*/
public abstract boolean requiresPropertyOrdering();
/**
* Introspection method that higher-level functionality may call
* to see whether underlying data format can read and write binary
* data natively; that is, embeded it as-is without using encodings
* such as Base64.
*false
as JSON does not
* support native access: all binary content must use Base64 encoding.
* Most binary formats (like Smile and Avro) support native binary content.
*/
public abstract boolean canHandleBinaryNatively();
/**
* Introspection method that can be used to check whether this
* factory can create non-blocking parsers: parsers that do not
* use blocking I/O abstractions but instead use a
* {@link com.fasterxml.jackson.core.async.NonBlockingInputFeeder}.
*/
public abstract boolean canParseAsync();
/**
* Method for accessing kind of {@link FormatFeature} that a parser
* {@link JsonParser} produced by this factory would accept, if any;
* null
returned if none.
*
* @since 2.6
*/
public abstract Class extends FormatFeature> getFormatReadFeatureType();
/**
* Method for accessing kind of {@link FormatFeature} that a parser
* {@link JsonGenerator} produced by this factory would accept, if any;
* null
returned if none.
*
* @since 2.6
*/
public abstract Class extends FormatFeature> getFormatWriteFeatureType();
/*
/**********************************************************
/* Format detection functionality
/**********************************************************
*/
/**
* Method that can be used to quickly check whether given schema
* is something that parsers and/or generators constructed by this
* factory could use. Note that this means possible use, at the level
* of data format (i.e. schema is for same data format as parsers and
* generators this factory constructs); individual schema instances
* may have further usage restrictions.
*
* @since 2.1
*/
public abstract boolean canUseSchema(FormatSchema schema);
/**
* Method that returns short textual id identifying format
* this factory supports.
*/
public abstract String getFormatName();
/*
/**********************************************************
/* Configuration access
/**********************************************************
*/
public abstract boolean isEnabled(JsonParser.Feature f);
public abstract boolean isEnabled(JsonGenerator.Feature f);
public abstract int getParserFeatures();
public abstract int getGeneratorFeatures();
public abstract int getFormatParserFeatures();
public abstract int getFormatGeneratorFeatures();
/*
/**********************************************************
/* Factory methods, parsers
/**********************************************************
*/
public abstract JsonParser createParser(byte[] data) throws IOException;
public abstract JsonParser createParser(byte[] data, int offset, int len) throws IOException;
public abstract JsonParser createParser(char[] content) throws IOException;
public abstract JsonParser createParser(char[] content, int offset, int len) throws IOException;
public abstract JsonParser createParser(DataInput in) throws IOException;
public abstract JsonParser createParser(File f) throws IOException;
public abstract JsonParser createParser(InputStream in) throws IOException;
public abstract JsonParser createParser(Reader r) throws IOException;
public abstract JsonParser createParser(String content) throws IOException;
public abstract JsonParser createParser(URL url) throws IOException;
public abstract JsonParser createNonBlockingByteArrayParser() throws IOException;
/*
/**********************************************************
/* Factory methods, generators
/**********************************************************
*/
public abstract JsonGenerator createGenerator(DataOutput out, JsonEncoding enc) throws IOException;
public abstract JsonGenerator createGenerator(DataOutput out) throws IOException;
public abstract JsonGenerator createGenerator(File f, JsonEncoding enc) throws IOException;
public abstract JsonGenerator createGenerator(OutputStream out) throws IOException;
public abstract JsonGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException;
public abstract JsonGenerator createGenerator(Writer w) throws IOException;
/*
/**********************************************************
/* Internal factory methods, other
/**********************************************************
*/
protected OutputStream _createDataOutputWrapper(DataOutput out) {
return new DataOutputAsStream(out);
}
/**
* Helper methods used for constructing an optimal stream for
* parsers to use, when input is to be read from an URL.
* This helps when reading file content via URL.
*/
protected InputStream _optimizedStreamFromURL(URL url) throws IOException {
if ("file".equals(url.getProtocol())) {
/* Can not do this if the path refers
* to a network drive on windows. This fixes the problem;
* might not be needed on all platforms (NFS?), but should not
* matter a lot: performance penalty of extra wrapping is more
* relevant when accessing local file system.
*/
String host = url.getHost();
if (host == null || host.length() == 0) {
// [core#48]: Let's try to avoid probs with URL encoded stuff
String path = url.getPath();
if (path.indexOf('%') < 0) {
return new FileInputStream(url.getPath());
}
// otherwise, let's fall through and let URL decoder do its magic
}
}
return url.openStream();
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/TreeCodec.java 0000664 0000000 0000000 00000001455 13561642473 0031044 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core;
import java.io.IOException;
/**
* Interface that defines objects that can read and write
* {@link TreeNode} instances using Streaming API.
*
* @since 2.3
*/
public abstract class TreeCodec
{
public abstract JsonNode
* base class in mapper
package.
*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.
*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.
*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
* return at(JsonPointer.valueOf(jsonPointerExpression));
*
*nextToken
is called). Once application using
* non-blocking parser has no more data to feed it should call
* {@link #endOfInput} to indicate end of logical input (stream) to parse.
*
* @since 2.9
*/
public interface NonBlockingInputFeeder
{
/**
* Method called to check whether it is ok to feed more data: parser returns true
* if it has no more content to parse (and it is ok to feed more); otherwise false
* (and no data should yet be fed).
*/
public boolean needMoreInput();
/**
* Method that should be called after last chunk of data to parse has been fed
* (with feedInput
in sub-class); can be called regardless of what {@link #needMoreInput}
* returns. After calling this method, no more data can be fed; and parser assumes
* no more data will be available.
*/
public void endOfInput();
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/async/package-info.java 0000664 0000000 0000000 00000000623 13561642473 0032644 0 ustar 00root root 0000000 0000000 /**
* Package that contains abstractions needed to support optional
* non-blocking decoding (parsing) functionality.
* Although parsers are constructed normally via
* {@link com.fasterxml.jackson.core.JsonFactory}
* (and are, in fact, sub-types of {@link com.fasterxml.jackson.core.JsonParser}),
* the way input is provided differs.
*
* @since 2.9
*/
package com.fasterxml.jackson.core.async;
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/base/ 0000775 0000000 0000000 00000000000 13561642473 0027251 5 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java 0000664 0000000 0000000 00000041446 13561642473 0032646 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.base;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.json.DupDetector;
import com.fasterxml.jackson.core.json.JsonWriteContext;
import com.fasterxml.jackson.core.json.PackageVersion;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
/**
* This base class implements part of API that a JSON generator exposes
* to applications, adds shared internal methods that sub-classes
* can use and adds some abstract methods sub-classes must implement.
*/
public abstract class GeneratorBase extends JsonGenerator
{
public final static int SURR1_FIRST = 0xD800;
public final static int SURR1_LAST = 0xDBFF;
public final static int SURR2_FIRST = 0xDC00;
public final static int SURR2_LAST = 0xDFFF;
/**
* Set of feature masks related to features that need updates of other
* local configuration or state.
*
* @since 2.5
*/
@SuppressWarnings("deprecation")
protected final static int DERIVED_FEATURES_MASK =
Feature.WRITE_NUMBERS_AS_STRINGS.getMask()
| Feature.ESCAPE_NON_ASCII.getMask()
| Feature.STRICT_DUPLICATE_DETECTION.getMask()
;
// // // Constants for validation messages (since 2.6)
protected final static String WRITE_BINARY = "write a binary value";
protected final static String WRITE_BOOLEAN = "write a boolean value";
protected final static String WRITE_NULL = "write a null";
protected final static String WRITE_NUMBER = "write a number";
protected final static String WRITE_RAW = "write a raw (unencoded) value";
protected final static String WRITE_STRING = "write a string";
/**
* This value is the limit of scale allowed for serializing {@link BigDecimal}
* in "plain" (non-engineering) notation; intent is to prevent asymmetric
* attack whereupon simple eng-notation with big scale is used to generate
* huge "plain" serialization. See [core#315] for details.
*
* @since 2.7.7
*/
protected final static int MAX_BIG_DECIMAL_SCALE = 9999;
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
protected ObjectCodec _objectCodec;
/**
* Bit flag composed of bits that indicate which
* {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s
* are enabled.
*/
protected int _features;
/**
* Flag set to indicate that implicit conversion from number
* to JSON String is needed (as per
* {@link com.fasterxml.jackson.core.JsonGenerator.Feature#WRITE_NUMBERS_AS_STRINGS}).
*/
protected boolean _cfgNumbersAsStrings;
/*
/**********************************************************
/* State
/**********************************************************
*/
/**
* Object that keeps track of the current contextual state
* of the generator.
*/
protected JsonWriteContext _writeContext;
/**
* Flag that indicates whether generator is closed or not. Gets
* set when it is closed by an explicit call
* ({@link #close}).
*/
protected boolean _closed;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
@SuppressWarnings("deprecation")
protected GeneratorBase(int features, ObjectCodec codec) {
super();
_features = features;
_objectCodec = codec;
DupDetector dups = Feature.STRICT_DUPLICATE_DETECTION.enabledIn(features)
? DupDetector.rootDetector(this) : null;
_writeContext = JsonWriteContext.createRootContext(dups);
_cfgNumbersAsStrings = Feature.WRITE_NUMBERS_AS_STRINGS.enabledIn(features);
}
/**
* @since 2.5
*/
@SuppressWarnings("deprecation")
protected GeneratorBase(int features, ObjectCodec codec, JsonWriteContext ctxt) {
super();
_features = features;
_objectCodec = codec;
_writeContext = ctxt;
_cfgNumbersAsStrings = Feature.WRITE_NUMBERS_AS_STRINGS.enabledIn(features);
}
/**
* Implemented with standard version number detection algorithm, typically using
* a simple generated class, with information extracted from Maven project file
* during build.
*/
@Override public Version version() { return PackageVersion.VERSION; }
@Override
public Object getCurrentValue() {
return _writeContext.getCurrentValue();
}
@Override
public void setCurrentValue(Object v) {
if (_writeContext != null) {
_writeContext.setCurrentValue(v);
}
}
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
@Override public final boolean isEnabled(Feature f) { return (_features & f.getMask()) != 0; }
@Override public int getFeatureMask() { return _features; }
//public JsonGenerator configure(Feature f, boolean state) { }
@SuppressWarnings("deprecation")
@Override
public JsonGenerator enable(Feature f) {
final int mask = f.getMask();
_features |= mask;
if ((mask & DERIVED_FEATURES_MASK) != 0) {
// why not switch? Requires addition of a generated class, alas
if (f == Feature.WRITE_NUMBERS_AS_STRINGS) {
_cfgNumbersAsStrings = true;
} else if (f == Feature.ESCAPE_NON_ASCII) {
setHighestNonEscapedChar(127);
} else if (f == Feature.STRICT_DUPLICATE_DETECTION) {
if (_writeContext.getDupDetector() == null) { // but only if disabled currently
_writeContext = _writeContext.withDupDetector(DupDetector.rootDetector(this));
}
}
}
return this;
}
@SuppressWarnings("deprecation")
@Override
public JsonGenerator disable(Feature f) {
final int mask = f.getMask();
_features &= ~mask;
if ((mask & DERIVED_FEATURES_MASK) != 0) {
if (f == Feature.WRITE_NUMBERS_AS_STRINGS) {
_cfgNumbersAsStrings = false;
} else if (f == Feature.ESCAPE_NON_ASCII) {
setHighestNonEscapedChar(0);
} else if (f == Feature.STRICT_DUPLICATE_DETECTION) {
_writeContext = _writeContext.withDupDetector(null);
}
}
return this;
}
@Override
@Deprecated
public JsonGenerator setFeatureMask(int newMask) {
int changed = newMask ^ _features;
_features = newMask;
if (changed != 0) {
_checkStdFeatureChanges(newMask, changed);
}
return this;
}
@Override // since 2.7
public JsonGenerator overrideStdFeatures(int values, int mask) {
int oldState = _features;
int newState = (oldState & ~mask) | (values & mask);
int changed = oldState ^ newState;
if (changed != 0) {
_features = newState;
_checkStdFeatureChanges(newState, changed);
}
return this;
}
/**
* Helper method called to verify changes to standard features.
*
* @param newFeatureFlags Bitflag of standard features after they were changed
* @param changedFeatures Bitflag of standard features for which setting
* did change
*
* @since 2.7
*/
@SuppressWarnings("deprecation")
protected void _checkStdFeatureChanges(int newFeatureFlags, int changedFeatures)
{
if ((changedFeatures & DERIVED_FEATURES_MASK) == 0) {
return;
}
_cfgNumbersAsStrings = Feature.WRITE_NUMBERS_AS_STRINGS.enabledIn(newFeatureFlags);
if (Feature.ESCAPE_NON_ASCII.enabledIn(changedFeatures)) {
if (Feature.ESCAPE_NON_ASCII.enabledIn(newFeatureFlags)) {
setHighestNonEscapedChar(127);
} else {
setHighestNonEscapedChar(0);
}
}
if (Feature.STRICT_DUPLICATE_DETECTION.enabledIn(changedFeatures)) {
if (Feature.STRICT_DUPLICATE_DETECTION.enabledIn(newFeatureFlags)) { // enabling
if (_writeContext.getDupDetector() == null) { // but only if disabled currently
_writeContext = _writeContext.withDupDetector(DupDetector.rootDetector(this));
}
} else { // disabling
_writeContext = _writeContext.withDupDetector(null);
}
}
}
@Override public JsonGenerator useDefaultPrettyPrinter() {
// Should not override a pretty printer if one already assigned.
if (getPrettyPrinter() != null) {
return this;
}
return setPrettyPrinter(_constructDefaultPrettyPrinter());
}
@Override public JsonGenerator setCodec(ObjectCodec oc) {
_objectCodec = oc;
return this;
}
@Override public ObjectCodec getCodec() { return _objectCodec; }
/*
/**********************************************************
/* Public API, accessors
/**********************************************************
*/
/**
* Note: type was co-variant until Jackson 2.7; reverted back to
* base type in 2.8 to allow for overriding by subtypes that use
* custom context type.
*/
@Override public JsonStreamContext getOutputContext() { return _writeContext; }
/*
/**********************************************************
/* Public API, write methods, structural
/**********************************************************
*/
//public void writeStartArray() throws IOException
//public void writeEndArray() throws IOException
//public void writeStartObject() throws IOException
//public void writeEndObject() throws IOException
@Override // since 2.8
public void writeStartObject(Object forValue) throws IOException
{
writeStartObject();
if (forValue != null) {
setCurrentValue(forValue);
}
}
/*
/**********************************************************
/* Public API, write methods, textual
/**********************************************************
*/
@Override public void writeFieldName(SerializableString name) throws IOException {
writeFieldName(name.getValue());
}
//public abstract void writeString(String text) throws IOException;
//public abstract void writeString(char[] text, int offset, int len) throws IOException;
//public abstract void writeString(Reader reader, int len) throws IOException;
//public abstract void writeRaw(String text) throws IOException,;
//public abstract void writeRaw(char[] text, int offset, int len) throws IOException;
@Override
public void writeString(SerializableString text) throws IOException {
writeString(text.getValue());
}
@Override public void writeRawValue(String text) throws IOException {
_verifyValueWrite("write raw value");
writeRaw(text);
}
@Override public void writeRawValue(String text, int offset, int len) throws IOException {
_verifyValueWrite("write raw value");
writeRaw(text, offset, len);
}
@Override public void writeRawValue(char[] text, int offset, int len) throws IOException {
_verifyValueWrite("write raw value");
writeRaw(text, offset, len);
}
@Override public void writeRawValue(SerializableString text) throws IOException {
_verifyValueWrite("write raw value");
writeRaw(text);
}
@Override
public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) throws IOException {
// Let's implement this as "unsupported" to make it easier to add new parser impls
_reportUnsupportedOperation();
return 0;
}
/*
/**********************************************************
/* Public API, write methods, primitive
/**********************************************************
*/
// Not implemented at this level, added as placeholders
/*
public abstract void writeNumber(int i)
public abstract void writeNumber(long l)
public abstract void writeNumber(double d)
public abstract void writeNumber(float f)
public abstract void writeNumber(BigDecimal dec)
public abstract void writeBoolean(boolean state)
public abstract void writeNull()
*/
/*
/**********************************************************
/* Public API, write methods, POJOs, trees
/**********************************************************
*/
@Override
public void writeObject(Object value) throws IOException {
if (value == null) {
// important: call method that does check value write:
writeNull();
} else {
/* 02-Mar-2009, tatu: we are NOT to call _verifyValueWrite here,
* because that will be done when codec actually serializes
* contained POJO. If we did call it it would advance state
* causing exception later on
*/
if (_objectCodec != null) {
_objectCodec.writeValue(this, value);
return;
}
_writeSimpleObject(value);
}
}
@Override
public void writeTree(TreeNode rootNode) throws IOException {
// As with 'writeObject()', we are not check if write would work
if (rootNode == null) {
writeNull();
} else {
if (_objectCodec == null) {
throw new IllegalStateException("No ObjectCodec defined");
}
_objectCodec.writeValue(this, rootNode);
}
}
/*
/**********************************************************
/* Public API, low-level output handling
/**********************************************************
*/
@Override public abstract void flush() throws IOException;
@Override public void close() throws IOException { _closed = true; }
@Override public boolean isClosed() { return _closed; }
/*
/**********************************************************
/* Package methods for this, sub-classes
/**********************************************************
*/
/**
* Method called to release any buffers generator may be holding,
* once generator is being closed.
*/
protected abstract void _releaseBuffers();
/**
* Method called before trying to write a value (scalar or structured),
* to verify that this is legal in current output state, as well as to
* output separators if and as necessary.
*
* @param typeMsg Additional message used for generating exception message
* if value output is NOT legal in current generator output state.
*/
protected abstract void _verifyValueWrite(String typeMsg) throws IOException;
/**
* Overridable factory method called to instantiate an appropriate {@link PrettyPrinter}
* for case of "just use the default one", when {@link #useDefaultPrettyPrinter()} is called.
*
* @since 2.6
*/
protected PrettyPrinter _constructDefaultPrettyPrinter() {
return new DefaultPrettyPrinter();
}
/**
* Helper method used to serialize a {@link java.math.BigDecimal} as a String,
* for serialization, taking into account configuration settings
*
* @since 2.7.7
*/
protected String _asString(BigDecimal value) throws IOException {
if (Feature.WRITE_BIGDECIMAL_AS_PLAIN.enabledIn(_features)) {
// 24-Aug-2016, tatu: [core#315] prevent possible DoS vector
int scale = value.scale();
if ((scale < -MAX_BIG_DECIMAL_SCALE) || (scale > MAX_BIG_DECIMAL_SCALE)) {
_reportError(String.format(
"Attempt to write plain `java.math.BigDecimal` (see JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN) with illegal scale (%d): needs to be between [-%d, %d]",
scale, MAX_BIG_DECIMAL_SCALE, MAX_BIG_DECIMAL_SCALE));
}
return value.toPlainString();
}
return value.toString();
}
/*
/**********************************************************
/* UTF-8 related helper method(s)
/**********************************************************
*/
/**
* @since 2.5
*/
protected final int _decodeSurrogate(int surr1, int surr2) throws IOException
{
// First is known to be valid, but how about the other?
if (surr2 < SURR2_FIRST || surr2 > SURR2_LAST) {
String msg = "Incomplete surrogate pair: first char 0x"+Integer.toHexString(surr1)+", second 0x"+Integer.toHexString(surr2);
_reportError(msg);
}
int c = 0x10000 + ((surr1 - SURR1_FIRST) << 10) + (surr2 - SURR2_FIRST);
return c;
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java 0000664 0000000 0000000 00000123202 13561642473 0032143 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.base;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.io.NumberInput;
import com.fasterxml.jackson.core.json.DupDetector;
import com.fasterxml.jackson.core.json.JsonReadContext;
import com.fasterxml.jackson.core.json.PackageVersion;
import com.fasterxml.jackson.core.util.ByteArrayBuilder;
import com.fasterxml.jackson.core.util.TextBuffer;
/**
* Intermediate base class used by all Jackson {@link JsonParser}
* implementations. Contains most common things that are independent
* of actual underlying input source.
*/
public abstract class ParserBase extends ParserMinimalBase
{
/*
/**********************************************************
/* Generic I/O state
/**********************************************************
*/
/**
* I/O context for this reader. It handles buffer allocation
* for the reader.
*/
final protected IOContext _ioContext;
/**
* Flag that indicates whether parser is closed or not. Gets
* set when parser is either closed by explicit call
* ({@link #close}) or when end-of-input is reached.
*/
protected boolean _closed;
/*
/**********************************************************
/* Current input data
/**********************************************************
*/
// Note: type of actual buffer depends on sub-class, can't include
/**
* Pointer to next available character in buffer
*/
protected int _inputPtr;
/**
* Index of character after last available one in the buffer.
*/
protected int _inputEnd;
/*
/**********************************************************
/* Current input location information
/**********************************************************
*/
/**
* Number of characters/bytes that were contained in previous blocks
* (blocks that were already processed prior to the current buffer).
*/
protected long _currInputProcessed;
/**
* Current row location of current point in input buffer, starting
* from 1, if available.
*/
protected int _currInputRow = 1;
/**
* Current index of the first character of the current row in input
* buffer. Needed to calculate column position, if necessary; benefit
* of not having column itself is that this only has to be updated
* once per line.
*/
protected int _currInputRowStart;
/*
/**********************************************************
/* Information about starting location of event
/* Reader is pointing to; updated on-demand
/**********************************************************
*/
// // // Location info at point when current token was started
/**
* Total number of bytes/characters read before start of current token.
* For big (gigabyte-sized) sizes are possible, needs to be long,
* unlike pointers and sizes related to in-memory buffers.
*/
protected long _tokenInputTotal;
/**
* Input row on which current token starts, 1-based
*/
protected int _tokenInputRow = 1;
/**
* Column on input row that current token starts; 0-based (although
* in the end it'll be converted to 1-based)
*/
protected int _tokenInputCol;
/*
/**********************************************************
/* Parsing state
/**********************************************************
*/
/**
* Information about parser context, context in which
* the next token is to be parsed (root, array, object).
*/
protected JsonReadContext _parsingContext;
/**
* Secondary token related to the next token after current one;
* used if its type is known. This may be value token that
* follows FIELD_NAME, for example.
*/
protected JsonToken _nextToken;
/*
/**********************************************************
/* Buffer(s) for local name(s) and text content
/**********************************************************
*/
/**
* Buffer that contains contents of String values, including
* field names if necessary (name split across boundary,
* contains escape sequence, or access needed to char array)
*/
protected final TextBuffer _textBuffer;
/**
* Temporary buffer that is needed if field name is accessed
* using {@link #getTextCharacters} method (instead of String
* returning alternatives)
*/
protected char[] _nameCopyBuffer;
/**
* Flag set to indicate whether the field name is available
* from the name copy buffer or not (in addition to its String
* representation being available via read context)
*/
protected boolean _nameCopied;
/**
* ByteArrayBuilder is needed if 'getBinaryValue' is called. If so,
* we better reuse it for remainder of content.
*/
protected ByteArrayBuilder _byteArrayBuilder;
/**
* We will hold on to decoded binary data, for duration of
* current event, so that multiple calls to
* {@link #getBinaryValue} will not need to decode data more
* than once.
*/
protected byte[] _binaryValue;
// Numeric value holders: multiple fields used for
// for efficiency
/**
* Bitfield that indicates which numeric representations
* have been calculated for the current type
*/
protected int _numTypesValid = NR_UNKNOWN;
// First primitives
protected int _numberInt;
protected long _numberLong;
protected double _numberDouble;
// And then object types
protected BigInteger _numberBigInt;
protected BigDecimal _numberBigDecimal;
// And then other information about value itself
/**
* Flag that indicates whether numeric value has a negative
* value. That is, whether its textual representation starts
* with minus character.
*/
protected boolean _numberNegative;
/**
* Length of integer part of the number, in characters
*/
protected int _intLength;
/**
* Length of the fractional part (not including decimal
* point or exponent), in characters.
* Not used for pure integer values.
*/
protected int _fractLength;
/**
* Length of the exponent part of the number, if any, not
* including 'e' marker or sign, just digits.
* Not used for pure integer values.
*/
protected int _expLength;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
protected ParserBase(IOContext ctxt, int features) {
super(features);
_ioContext = ctxt;
_textBuffer = ctxt.constructTextBuffer();
DupDetector dups = Feature.STRICT_DUPLICATE_DETECTION.enabledIn(features)
? DupDetector.rootDetector(this) : null;
_parsingContext = JsonReadContext.createRootContext(dups);
}
@Override public Version version() { return PackageVersion.VERSION; }
@Override
public Object getCurrentValue() {
return _parsingContext.getCurrentValue();
}
@Override
public void setCurrentValue(Object v) {
_parsingContext.setCurrentValue(v);
}
/*
/**********************************************************
/* Overrides for Feature handling
/**********************************************************
*/
@Override
public JsonParser enable(Feature f) {
_features |= f.getMask();
if (f == Feature.STRICT_DUPLICATE_DETECTION) { // enabling dup detection?
if (_parsingContext.getDupDetector() == null) { // but only if disabled currently
_parsingContext = _parsingContext.withDupDetector(DupDetector.rootDetector(this));
}
}
return this;
}
@Override
public JsonParser disable(Feature f) {
_features &= ~f.getMask();
if (f == Feature.STRICT_DUPLICATE_DETECTION) {
_parsingContext = _parsingContext.withDupDetector(null);
}
return this;
}
@Override
@Deprecated
public JsonParser setFeatureMask(int newMask) {
int changes = (_features ^ newMask);
if (changes != 0) {
_features = newMask;
_checkStdFeatureChanges(newMask, changes);
}
return this;
}
@Override // since 2.7
public JsonParser overrideStdFeatures(int values, int mask) {
int oldState = _features;
int newState = (oldState & ~mask) | (values & mask);
int changed = oldState ^ newState;
if (changed != 0) {
_features = newState;
_checkStdFeatureChanges(newState, changed);
}
return this;
}
/**
* Helper method called to verify changes to standard features.
*
* @param newFeatureFlags Bitflag of standard features after they were changed
* @param changedFeatures Bitflag of standard features for which setting
* did change
*
* @since 2.7
*/
protected void _checkStdFeatureChanges(int newFeatureFlags, int changedFeatures)
{
int f = Feature.STRICT_DUPLICATE_DETECTION.getMask();
if ((changedFeatures & f) != 0) {
if ((newFeatureFlags & f) != 0) {
if (_parsingContext.getDupDetector() == null) {
_parsingContext = _parsingContext.withDupDetector(DupDetector.rootDetector(this));
} else { // disabling
_parsingContext = _parsingContext.withDupDetector(null);
}
}
}
}
/*
/**********************************************************
/* JsonParser impl
/**********************************************************
*/
/**
* Method that can be called to get the name associated with
* the current event.
*/
@Override public String getCurrentName() throws IOException {
// [JACKSON-395]: start markers require information from parent
if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
JsonReadContext parent = _parsingContext.getParent();
if (parent != null) {
return parent.getCurrentName();
}
}
return _parsingContext.getCurrentName();
}
@Override public void overrideCurrentName(String name) {
// Simple, but need to look for START_OBJECT/ARRAY's "off-by-one" thing:
JsonReadContext ctxt = _parsingContext;
if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
ctxt = ctxt.getParent();
}
/* 24-Sep-2013, tatu: Unfortunate, but since we did not expose exceptions,
* need to wrap this here
*/
try {
ctxt.setCurrentName(name);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
@Override public void close() throws IOException {
if (!_closed) {
// 19-Jan-2018, tatu: as per [core#440] need to ensure no more data assumed available
_inputPtr = Math.max(_inputPtr, _inputEnd);
_closed = true;
try {
_closeInput();
} finally {
// as per [JACKSON-324], do in finally block
// Also, internal buffer(s) can now be released as well
_releaseBuffers();
}
}
}
@Override public boolean isClosed() { return _closed; }
@Override public JsonReadContext getParsingContext() { return _parsingContext; }
/**
* Method that return the starting location of the current
* token; that is, position of the first character from input
* that starts the current token.
*/
@Override
public JsonLocation getTokenLocation() {
return new JsonLocation(_getSourceReference(),
-1L, getTokenCharacterOffset(), // bytes, chars
getTokenLineNr(),
getTokenColumnNr());
}
/**
* Method that returns location of the last processed character;
* usually for error reporting purposes
*/
@Override
public JsonLocation getCurrentLocation() {
int col = _inputPtr - _currInputRowStart + 1; // 1-based
return new JsonLocation(_getSourceReference(),
-1L, _currInputProcessed + _inputPtr, // bytes, chars
_currInputRow, col);
}
/*
/**********************************************************
/* Public API, access to token information, text and similar
/**********************************************************
*/
@Override
public boolean hasTextCharacters() {
if (_currToken == JsonToken.VALUE_STRING) { return true; } // usually true
if (_currToken == JsonToken.FIELD_NAME) { return _nameCopied; }
return false;
}
@SuppressWarnings("resource")
@Override // since 2.7
public byte[] getBinaryValue(Base64Variant variant) throws IOException
{
if (_binaryValue == null) {
if (_currToken != JsonToken.VALUE_STRING) {
_reportError("Current token ("+_currToken+") not VALUE_STRING, can not access as binary");
}
ByteArrayBuilder builder = _getByteArrayBuilder();
_decodeBase64(getText(), builder, variant);
_binaryValue = builder.toByteArray();
}
return _binaryValue;
}
/*
/**********************************************************
/* Public low-level accessors
/**********************************************************
*/
public long getTokenCharacterOffset() { return _tokenInputTotal; }
public int getTokenLineNr() { return _tokenInputRow; }
public int getTokenColumnNr() {
// note: value of -1 means "not available"; otherwise convert from 0-based to 1-based
int col = _tokenInputCol;
return (col < 0) ? col : (col + 1);
}
/*
/**********************************************************
/* Abstract methods for sub-classes to implement
/**********************************************************
*/
protected abstract void _closeInput() throws IOException;
/*
/**********************************************************
/* Low-level reading, other
/**********************************************************
*/
/**
* Method called to release internal buffers owned by the base
* reader. This may be called along with {@link #_closeInput} (for
* example, when explicitly closing this reader instance), or
* separately (if need be).
*/
protected void _releaseBuffers() throws IOException {
_textBuffer.releaseBuffers();
char[] buf = _nameCopyBuffer;
if (buf != null) {
_nameCopyBuffer = null;
_ioContext.releaseNameCopyBuffer(buf);
}
}
/**
* Method called when an EOF is encountered between tokens.
* If so, it may be a legitimate EOF, but only iff there
* is no open non-root context.
*/
@Override
protected void _handleEOF() throws JsonParseException {
if (!_parsingContext.inRoot()) {
String marker = _parsingContext.inArray() ? "Array" : "Object";
_reportInvalidEOF(String.format(
": expected close marker for %s (start marker at %s)",
marker,
_parsingContext.getStartLocation(_getSourceReference())),
null);
}
}
/**
* @since 2.4
*/
protected final int _eofAsNextChar() throws JsonParseException {
_handleEOF();
return -1;
}
/*
/**********************************************************
/* Internal/package methods: shared/reusable builders
/**********************************************************
*/
public ByteArrayBuilder _getByteArrayBuilder()
{
if (_byteArrayBuilder == null) {
_byteArrayBuilder = new ByteArrayBuilder();
} else {
_byteArrayBuilder.reset();
}
return _byteArrayBuilder;
}
/*
/**********************************************************
/* Methods from former JsonNumericParserBase
/**********************************************************
*/
// // // Life-cycle of number-parsing
protected final JsonToken reset(boolean negative, int intLen, int fractLen, int expLen)
{
if (fractLen < 1 && expLen < 1) { // integer
return resetInt(negative, intLen);
}
return resetFloat(negative, intLen, fractLen, expLen);
}
protected final JsonToken resetInt(boolean negative, int intLen)
{
_numberNegative = negative;
_intLength = intLen;
_fractLength = 0;
_expLength = 0;
_numTypesValid = NR_UNKNOWN; // to force parsing
return JsonToken.VALUE_NUMBER_INT;
}
protected final JsonToken resetFloat(boolean negative, int intLen, int fractLen, int expLen)
{
_numberNegative = negative;
_intLength = intLen;
_fractLength = fractLen;
_expLength = expLen;
_numTypesValid = NR_UNKNOWN; // to force parsing
return JsonToken.VALUE_NUMBER_FLOAT;
}
protected final JsonToken resetAsNaN(String valueStr, double value)
{
_textBuffer.resetWithString(valueStr);
_numberDouble = value;
_numTypesValid = NR_DOUBLE;
return JsonToken.VALUE_NUMBER_FLOAT;
}
@Override
public boolean isNaN() {
if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) {
if ((_numTypesValid & NR_DOUBLE) != 0) {
// 10-Mar-2017, tatu: Alas, `Double.isFinite(d)` only added in JDK 8
double d = _numberDouble;
return Double.isNaN(d) || Double.isInfinite(d);
}
}
return false;
}
/*
/**********************************************************
/* Numeric accessors of public API
/**********************************************************
*/
@Override
public Number getNumberValue() throws IOException
{
if (_numTypesValid == NR_UNKNOWN) {
_parseNumericValue(NR_UNKNOWN); // will also check event type
}
// Separate types for int types
if (_currToken == JsonToken.VALUE_NUMBER_INT) {
if ((_numTypesValid & NR_INT) != 0) {
return _numberInt;
}
if ((_numTypesValid & NR_LONG) != 0) {
return _numberLong;
}
if ((_numTypesValid & NR_BIGINT) != 0) {
return _numberBigInt;
}
// Shouldn't get this far but if we do
return _numberBigDecimal;
}
/* And then floating point types. But here optimal type
* needs to be big decimal, to avoid losing any data?
*/
if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
return _numberBigDecimal;
}
if ((_numTypesValid & NR_DOUBLE) == 0) { // sanity check
_throwInternal();
}
return _numberDouble;
}
@Override
public NumberType getNumberType() throws IOException
{
if (_numTypesValid == NR_UNKNOWN) {
_parseNumericValue(NR_UNKNOWN); // will also check event type
}
if (_currToken == JsonToken.VALUE_NUMBER_INT) {
if ((_numTypesValid & NR_INT) != 0) {
return NumberType.INT;
}
if ((_numTypesValid & NR_LONG) != 0) {
return NumberType.LONG;
}
return NumberType.BIG_INTEGER;
}
/* And then floating point types. Here optimal type
* needs to be big decimal, to avoid losing any data?
* However... using BD is slow, so let's allow returning
* double as type if no explicit call has been made to access
* data as BD?
*/
if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
return NumberType.BIG_DECIMAL;
}
return NumberType.DOUBLE;
}
@Override
public int getIntValue() throws IOException
{
if ((_numTypesValid & NR_INT) == 0) {
if (_numTypesValid == NR_UNKNOWN) { // not parsed at all
return _parseIntValue();
}
if ((_numTypesValid & NR_INT) == 0) { // wasn't an int natively?
convertNumberToInt(); // let's make it so, if possible
}
}
return _numberInt;
}
@Override
public long getLongValue() throws IOException
{
if ((_numTypesValid & NR_LONG) == 0) {
if (_numTypesValid == NR_UNKNOWN) {
_parseNumericValue(NR_LONG);
}
if ((_numTypesValid & NR_LONG) == 0) {
convertNumberToLong();
}
}
return _numberLong;
}
@Override
public BigInteger getBigIntegerValue() throws IOException
{
if ((_numTypesValid & NR_BIGINT) == 0) {
if (_numTypesValid == NR_UNKNOWN) {
_parseNumericValue(NR_BIGINT);
}
if ((_numTypesValid & NR_BIGINT) == 0) {
convertNumberToBigInteger();
}
}
return _numberBigInt;
}
@Override
public float getFloatValue() throws IOException
{
double value = getDoubleValue();
/* 22-Jan-2009, tatu: Bounds/range checks would be tricky
* here, so let's not bother even trying...
*/
/*
if (value < -Float.MAX_VALUE || value > MAX_FLOAT_D) {
_reportError("Numeric value ("+getText()+") out of range of Java float");
}
*/
return (float) value;
}
@Override
public double getDoubleValue() throws IOException
{
if ((_numTypesValid & NR_DOUBLE) == 0) {
if (_numTypesValid == NR_UNKNOWN) {
_parseNumericValue(NR_DOUBLE);
}
if ((_numTypesValid & NR_DOUBLE) == 0) {
convertNumberToDouble();
}
}
return _numberDouble;
}
@Override
public BigDecimal getDecimalValue() throws IOException
{
if ((_numTypesValid & NR_BIGDECIMAL) == 0) {
if (_numTypesValid == NR_UNKNOWN) {
_parseNumericValue(NR_BIGDECIMAL);
}
if ((_numTypesValid & NR_BIGDECIMAL) == 0) {
convertNumberToBigDecimal();
}
}
return _numberBigDecimal;
}
/*
/**********************************************************
/* Conversion from textual to numeric representation
/**********************************************************
*/
/**
* Method that will parse actual numeric value out of a syntactically
* valid number value. Type it will parse into depends on whether
* it is a floating point number, as well as its magnitude: smallest
* legal type (of ones available) is used for efficiency.
*
* @param expType Numeric type that we will immediately need, if any;
* mostly necessary to optimize handling of floating point numbers
*/
protected void _parseNumericValue(int expType) throws IOException
{
// Int or float?
if (_currToken == JsonToken.VALUE_NUMBER_INT) {
int len = _intLength;
// First: optimization for simple int
if (len <= 9) {
int i = _textBuffer.contentsAsInt(_numberNegative);
_numberInt = i;
_numTypesValid = NR_INT;
return;
}
if (len <= 18) { // definitely fits AND is easy to parse using 2 int parse calls
long l = _textBuffer.contentsAsLong(_numberNegative);
// Might still fit in int, need to check
if (len == 10) {
if (_numberNegative) {
if (l >= MIN_INT_L) {
_numberInt = (int) l;
_numTypesValid = NR_INT;
return;
}
} else {
if (l <= MAX_INT_L) {
_numberInt = (int) l;
_numTypesValid = NR_INT;
return;
}
}
}
_numberLong = l;
_numTypesValid = NR_LONG;
return;
}
_parseSlowInt(expType);
return;
}
if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) {
_parseSlowFloat(expType);
return;
}
_reportError("Current token (%s) not numeric, can not use numeric value accessors", _currToken);
}
/**
* @since 2.6
*/
protected int _parseIntValue() throws IOException
{
// Inlined variant of: _parseNumericValue(NR_INT)
if (_currToken == JsonToken.VALUE_NUMBER_INT) {
if (_intLength <= 9) {
int i = _textBuffer.contentsAsInt(_numberNegative);
_numberInt = i;
_numTypesValid = NR_INT;
return i;
}
}
// if not optimizable, use more generic
_parseNumericValue(NR_INT);
if ((_numTypesValid & NR_INT) == 0) {
convertNumberToInt();
}
return _numberInt;
}
private void _parseSlowFloat(int expType) throws IOException
{
/* Nope: floating point. Here we need to be careful to get
* optimal parsing strategy: choice is between accurate but
* slow (BigDecimal) and lossy but fast (Double). For now
* let's only use BD when explicitly requested -- it can
* still be constructed correctly at any point since we do
* retain textual representation
*/
try {
if (expType == NR_BIGDECIMAL) {
_numberBigDecimal = _textBuffer.contentsAsDecimal();
_numTypesValid = NR_BIGDECIMAL;
} else {
// Otherwise double has to do
_numberDouble = _textBuffer.contentsAsDouble();
_numTypesValid = NR_DOUBLE;
}
} catch (NumberFormatException nex) {
// Can this ever occur? Due to overflow, maybe?
_wrapError("Malformed numeric value ("+_longNumberDesc(_textBuffer.contentsAsString())+")", nex);
}
}
private void _parseSlowInt(int expType) throws IOException
{
String numStr = _textBuffer.contentsAsString();
try {
int len = _intLength;
char[] buf = _textBuffer.getTextBuffer();
int offset = _textBuffer.getTextOffset();
if (_numberNegative) {
++offset;
}
// Some long cases still...
if (NumberInput.inLongRange(buf, offset, len, _numberNegative)) {
// Probably faster to construct a String, call parse, than to use BigInteger
_numberLong = Long.parseLong(numStr);
_numTypesValid = NR_LONG;
} else {
// 16-Oct-2018, tatu: Need to catch "too big" early due to [jackson-core#488]
if ((expType == NR_INT) || (expType == NR_LONG)) {
_reportTooLongIntegral(expType, numStr);
}
if ((expType == NR_DOUBLE) || (expType == NR_FLOAT)) {
_numberDouble = NumberInput.parseDouble(numStr);
_numTypesValid = NR_DOUBLE;
} else {
// nope, need the heavy guns... (rare case)
_numberBigInt = new BigInteger(numStr);
_numTypesValid = NR_BIGINT;
}
}
} catch (NumberFormatException nex) {
// Can this ever occur? Due to overflow, maybe?
_wrapError("Malformed numeric value ("+_longNumberDesc(numStr)+")", nex);
}
}
// @since 2.9.8
protected void _reportTooLongIntegral(int expType, String rawNum) throws IOException
{
if (expType == NR_INT) {
reportOverflowInt(rawNum);
} else {
reportOverflowLong(rawNum);
}
}
/*
/**********************************************************
/* Numeric conversions
/**********************************************************
*/
protected void convertNumberToInt() throws IOException
{
// First, converting from long ought to be easy
if ((_numTypesValid & NR_LONG) != 0) {
// Let's verify it's lossless conversion by simple roundtrip
int result = (int) _numberLong;
if (((long) result) != _numberLong) {
reportOverflowInt(getText(), currentToken());
}
_numberInt = result;
} else if ((_numTypesValid & NR_BIGINT) != 0) {
if (BI_MIN_INT.compareTo(_numberBigInt) > 0
|| BI_MAX_INT.compareTo(_numberBigInt) < 0) {
reportOverflowInt();
}
_numberInt = _numberBigInt.intValue();
} else if ((_numTypesValid & NR_DOUBLE) != 0) {
// Need to check boundaries
if (_numberDouble < MIN_INT_D || _numberDouble > MAX_INT_D) {
reportOverflowInt();
}
_numberInt = (int) _numberDouble;
} else if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
if (BD_MIN_INT.compareTo(_numberBigDecimal) > 0
|| BD_MAX_INT.compareTo(_numberBigDecimal) < 0) {
reportOverflowInt();
}
_numberInt = _numberBigDecimal.intValue();
} else {
_throwInternal();
}
_numTypesValid |= NR_INT;
}
protected void convertNumberToLong() throws IOException
{
if ((_numTypesValid & NR_INT) != 0) {
_numberLong = (long) _numberInt;
} else if ((_numTypesValid & NR_BIGINT) != 0) {
if (BI_MIN_LONG.compareTo(_numberBigInt) > 0
|| BI_MAX_LONG.compareTo(_numberBigInt) < 0) {
reportOverflowLong();
}
_numberLong = _numberBigInt.longValue();
} else if ((_numTypesValid & NR_DOUBLE) != 0) {
// Need to check boundaries
if (_numberDouble < MIN_LONG_D || _numberDouble > MAX_LONG_D) {
reportOverflowLong();
}
_numberLong = (long) _numberDouble;
} else if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
if (BD_MIN_LONG.compareTo(_numberBigDecimal) > 0
|| BD_MAX_LONG.compareTo(_numberBigDecimal) < 0) {
reportOverflowLong();
}
_numberLong = _numberBigDecimal.longValue();
} else {
_throwInternal();
}
_numTypesValid |= NR_LONG;
}
protected void convertNumberToBigInteger() throws IOException
{
if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
// here it'll just get truncated, no exceptions thrown
_numberBigInt = _numberBigDecimal.toBigInteger();
} else if ((_numTypesValid & NR_LONG) != 0) {
_numberBigInt = BigInteger.valueOf(_numberLong);
} else if ((_numTypesValid & NR_INT) != 0) {
_numberBigInt = BigInteger.valueOf(_numberInt);
} else if ((_numTypesValid & NR_DOUBLE) != 0) {
_numberBigInt = BigDecimal.valueOf(_numberDouble).toBigInteger();
} else {
_throwInternal();
}
_numTypesValid |= NR_BIGINT;
}
protected void convertNumberToDouble() throws IOException
{
/* 05-Aug-2008, tatus: Important note: this MUST start with
* more accurate representations, since we don't know which
* value is the original one (others get generated when
* requested)
*/
if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
_numberDouble = _numberBigDecimal.doubleValue();
} else if ((_numTypesValid & NR_BIGINT) != 0) {
_numberDouble = _numberBigInt.doubleValue();
} else if ((_numTypesValid & NR_LONG) != 0) {
_numberDouble = (double) _numberLong;
} else if ((_numTypesValid & NR_INT) != 0) {
_numberDouble = (double) _numberInt;
} else {
_throwInternal();
}
_numTypesValid |= NR_DOUBLE;
}
protected void convertNumberToBigDecimal() throws IOException
{
/* 05-Aug-2008, tatus: Important note: this MUST start with
* more accurate representations, since we don't know which
* value is the original one (others get generated when
* requested)
*/
if ((_numTypesValid & NR_DOUBLE) != 0) {
/* Let's actually parse from String representation, to avoid
* rounding errors that non-decimal floating operations could incur
*/
_numberBigDecimal = NumberInput.parseBigDecimal(getText());
} else if ((_numTypesValid & NR_BIGINT) != 0) {
_numberBigDecimal = new BigDecimal(_numberBigInt);
} else if ((_numTypesValid & NR_LONG) != 0) {
_numberBigDecimal = BigDecimal.valueOf(_numberLong);
} else if ((_numTypesValid & NR_INT) != 0) {
_numberBigDecimal = BigDecimal.valueOf(_numberInt);
} else {
_throwInternal();
}
_numTypesValid |= NR_BIGDECIMAL;
}
/*
/**********************************************************
/* Internal/package methods: Error reporting
/**********************************************************
*/
protected void _reportMismatchedEndMarker(int actCh, char expCh) throws JsonParseException {
JsonReadContext ctxt = getParsingContext();
_reportError(String.format(
"Unexpected close marker '%s': expected '%c' (for %s starting at %s)",
(char) actCh, expCh, ctxt.typeDesc(), ctxt.getStartLocation(_getSourceReference())));
}
@SuppressWarnings("deprecation")
protected char _handleUnrecognizedCharacterEscape(char ch) throws JsonProcessingException {
// as per [JACKSON-300]
if (isEnabled(Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)) {
return ch;
}
// and [JACKSON-548]
if (ch == '\'' && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) {
return ch;
}
_reportError("Unrecognized character escape "+_getCharDesc(ch));
return ch;
}
/**
* Method called to report a problem with unquoted control character.
* Note: it is possible to suppress some instances of
* exception by enabling {@link Feature#ALLOW_UNQUOTED_CONTROL_CHARS}.
*/
@SuppressWarnings("deprecation")
protected void _throwUnquotedSpace(int i, String ctxtDesc) throws JsonParseException {
// JACKSON-208; possible to allow unquoted control chars:
if (!isEnabled(Feature.ALLOW_UNQUOTED_CONTROL_CHARS) || i > INT_SPACE) {
char c = (char) i;
String msg = "Illegal unquoted character ("+_getCharDesc(c)+"): has to be escaped using backslash to be included in "+ctxtDesc;
_reportError(msg);
}
}
/**
* @return Description to use as "valid tokens" in an exception message about
* invalid (unrecognized) JSON token: called when parser finds something that
* looks like unquoted textual token
*
* @since 2.10
*/
protected String _validJsonTokenList() throws IOException {
return _validJsonValueList();
}
/**
* @return Description to use as "valid JSON values" in an exception message about
* invalid (unrecognized) JSON value: called when parser finds something that
* does not look like a value or separator.
*
* @since 2.10
*/
@SuppressWarnings("deprecation")
protected String _validJsonValueList() throws IOException {
if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
return "(JSON String, Number (or 'NaN'/'INF'/'+INF'), Array, Object or token 'null', 'true' or 'false')";
}
return "(JSON String, Number, Array, Object or token 'null', 'true' or 'false')";
}
/*
/**********************************************************
/* Base64 handling support
/**********************************************************
*/
/**
* Method that sub-classes must implement to support escaped sequences
* in base64-encoded sections.
* Sub-classes that do not need base64 support can leave this as is
*/
protected char _decodeEscaped() throws IOException {
throw new UnsupportedOperationException();
}
protected final int _decodeBase64Escape(Base64Variant b64variant, int ch, int index) throws IOException
{
// 17-May-2011, tatu: As per [JACKSON-xxx], need to handle escaped chars
if (ch != '\\') {
throw reportInvalidBase64Char(b64variant, ch, index);
}
int unescaped = _decodeEscaped();
// if white space, skip if first triplet; otherwise errors
if (unescaped <= INT_SPACE) {
if (index == 0) { // whitespace only allowed to be skipped between triplets
return -1;
}
}
// otherwise try to find actual triplet value
int bits = b64variant.decodeBase64Char(unescaped);
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
throw reportInvalidBase64Char(b64variant, unescaped, index);
}
}
return bits;
}
protected final int _decodeBase64Escape(Base64Variant b64variant, char ch, int index) throws IOException
{
if (ch != '\\') {
throw reportInvalidBase64Char(b64variant, ch, index);
}
char unescaped = _decodeEscaped();
// if white space, skip if first triplet; otherwise errors
if (unescaped <= INT_SPACE) {
if (index == 0) { // whitespace only allowed to be skipped between triplets
return -1;
}
}
// otherwise try to find actual triplet value
int bits = b64variant.decodeBase64Char(unescaped);
if (bits < 0) {
// second check since padding can only be 3rd or 4th byte (index #2 or #3)
if ((bits != Base64Variant.BASE64_VALUE_PADDING) || (index < 2)) {
throw reportInvalidBase64Char(b64variant, unescaped, index);
}
}
return bits;
}
protected IllegalArgumentException reportInvalidBase64Char(Base64Variant b64variant, int ch, int bindex) throws IllegalArgumentException {
return reportInvalidBase64Char(b64variant, ch, bindex, null);
}
/**
* @param bindex Relative index within base64 character unit; between 0
* and 3 (as unit has exactly 4 characters)
*/
protected IllegalArgumentException reportInvalidBase64Char(Base64Variant b64variant, int ch, int bindex, String msg) throws IllegalArgumentException {
String base;
if (ch <= INT_SPACE) {
base = String.format("Illegal white space character (code 0x%s) as character #%d of 4-char base64 unit: can only used between units",
Integer.toHexString(ch), (bindex+1));
} else if (b64variant.usesPaddingChar(ch)) {
base = "Unexpected padding character ('"+b64variant.getPaddingChar()+"') as character #"+(bindex+1)+" of 4-char base64 unit: padding only legal as 3rd or 4th character";
} else if (!Character.isDefined(ch) || Character.isISOControl(ch)) {
// Not sure if we can really get here... ? (most illegal xml chars are caught at lower level)
base = "Illegal character (code 0x"+Integer.toHexString(ch)+") in base64 content";
} else {
base = "Illegal character '"+((char)ch)+"' (code 0x"+Integer.toHexString(ch)+") in base64 content";
}
if (msg != null) {
base = base + ": " + msg;
}
return new IllegalArgumentException(base);
}
// since 2.9.8
protected void _handleBase64MissingPadding(Base64Variant b64variant) throws IOException
{
_reportError(b64variant.missingPaddingMessage());
}
/*
/**********************************************************
/* Internal/package methods: other
/**********************************************************
*/
/**
* Helper method used to encapsulate logic of including (or not) of
* "source reference" when constructing {@link JsonLocation} instances.
*
* @since 2.9
*/
protected Object _getSourceReference() {
if (JsonParser.Feature.INCLUDE_SOURCE_IN_LOCATION.enabledIn(_features)) {
return _ioContext.getSourceReference();
}
return null;
}
protected static int[] growArrayBy(int[] arr, int more)
{
if (arr == null) {
return new int[more];
}
return Arrays.copyOf(arr, arr.length + more);
}
/*
/**********************************************************
/* Stuff that was abstract and required before 2.8, but that
/* is not mandatory in 2.8 or above.
/**********************************************************
*/
@Deprecated // since 2.8
protected void loadMoreGuaranteed() throws IOException {
if (!loadMore()) { _reportInvalidEOF(); }
}
@Deprecated // since 2.8
protected boolean loadMore() throws IOException { return false; }
// Can't declare as deprecated, for now, but shouldn't be needed
protected void _finishString() throws IOException { }
}
ParserMinimalBase.java 0000664 0000000 0000000 00000061266 13561642473 0033406 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/base package com.fasterxml.jackson.core.base;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.exc.InputCoercionException;
import com.fasterxml.jackson.core.io.JsonEOFException;
import com.fasterxml.jackson.core.io.NumberInput;
import com.fasterxml.jackson.core.util.ByteArrayBuilder;
import com.fasterxml.jackson.core.util.VersionUtil;
import static com.fasterxml.jackson.core.JsonTokenId.*;
/**
* Intermediate base class used by all Jackson {@link JsonParser}
* implementations, but does not add any additional fields that depend
* on particular method of obtaining input.
*nextToken()
,
* as well as if token has been explicitly cleared
*/
protected JsonToken _currToken;
/**
* Last cleared token, if any: that is, value that was in
* effect when {@link #clearCurrentToken} was called.
*/
protected JsonToken _lastClearedToken;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
protected ParserMinimalBase() { }
protected ParserMinimalBase(int features) { super(features); }
// NOTE: had base impl in 2.3 and before; but shouldn't
// public abstract Version version();
/*
/**********************************************************
/* Configuration overrides if any
/**********************************************************
*/
// from base class:
//public void enableFeature(Feature f)
//public void disableFeature(Feature f)
//public void setFeature(Feature f, boolean state)
//public boolean isFeatureEnabled(Feature f)
/*
/**********************************************************
/* JsonParser impl
/**********************************************************
*/
@Override public abstract JsonToken nextToken() throws IOException;
@Override public JsonToken currentToken() { return _currToken; }
@Override public int currentTokenId() {
final JsonToken t = _currToken;
return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
}
@Override public JsonToken getCurrentToken() { return _currToken; }
@Override public int getCurrentTokenId() {
final JsonToken t = _currToken;
return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
}
@Override public boolean hasCurrentToken() { return _currToken != null; }
@Override public boolean hasTokenId(int id) {
final JsonToken t = _currToken;
if (t == null) {
return (JsonTokenId.ID_NO_TOKEN == id);
}
return t.id() == id;
}
@Override public boolean hasToken(JsonToken t) {
return (_currToken == t);
}
@Override public boolean isExpectedStartArrayToken() { return _currToken == JsonToken.START_ARRAY; }
@Override public boolean isExpectedStartObjectToken() { return _currToken == JsonToken.START_OBJECT; }
@Override
public JsonToken nextValue() throws IOException {
// Implementation should be as trivial as follows; only needs to change if
// we are to skip other tokens (for example, if comments were exposed as tokens)
JsonToken t = nextToken();
if (t == JsonToken.FIELD_NAME) {
t = nextToken();
}
return t;
}
@Override
public JsonParser skipChildren() throws IOException
{
if (_currToken != JsonToken.START_OBJECT
&& _currToken != JsonToken.START_ARRAY) {
return this;
}
int open = 1;
// Since proper matching of start/end markers is handled
// by nextToken(), we'll just count nesting levels here
while (true) {
JsonToken t = nextToken();
if (t == null) {
_handleEOF();
/* given constraints, above should never return;
* however, FindBugs doesn't know about it and
* complains... so let's add dummy break here
*/
return this;
}
if (t.isStructStart()) {
++open;
} else if (t.isStructEnd()) {
if (--open == 0) {
return this;
}
// 23-May-2018, tatu: [core#463] Need to consider non-blocking case...
} else if (t == JsonToken.NOT_AVAILABLE) {
// Nothing much we can do except to either return `null` (which seems wrong),
// or, what we actually do, signal error
_reportError("Not enough content available for `skipChildren()`: non-blocking parser? (%s)",
getClass().getName());
}
}
}
/**
* Method sub-classes need to implement
*/
protected abstract void _handleEOF() throws JsonParseException;
//public JsonToken getCurrentToken()
//public boolean hasCurrentToken()
@Override public abstract String getCurrentName() throws IOException;
@Override public abstract void close() throws IOException;
@Override public abstract boolean isClosed();
@Override public abstract JsonStreamContext getParsingContext();
// public abstract JsonLocation getTokenLocation();
// public abstract JsonLocation getCurrentLocation();
/*
/**********************************************************
/* Public API, token state overrides
/**********************************************************
*/
@Override public void clearCurrentToken() {
if (_currToken != null) {
_lastClearedToken = _currToken;
_currToken = null;
}
}
@Override public JsonToken getLastClearedToken() { return _lastClearedToken; }
@Override public abstract void overrideCurrentName(String name);
/*
/**********************************************************
/* Public API, access to token information, text
/**********************************************************
*/
@Override public abstract String getText() throws IOException;
@Override public abstract char[] getTextCharacters() throws IOException;
@Override public abstract boolean hasTextCharacters();
@Override public abstract int getTextLength() throws IOException;
@Override public abstract int getTextOffset() throws IOException;
/*
/**********************************************************
/* Public API, access to token information, binary
/**********************************************************
*/
@Override public abstract byte[] getBinaryValue(Base64Variant b64variant) throws IOException;
/*
/**********************************************************
/* Public API, access with conversion/coercion
/**********************************************************
*/
@Override
public boolean getValueAsBoolean(boolean defaultValue) throws IOException
{
JsonToken t = _currToken;
if (t != null) {
switch (t.id()) {
case ID_STRING:
String str = getText().trim();
if ("true".equals(str)) {
return true;
}
if ("false".equals(str)) {
return false;
}
if (_hasTextualNull(str)) {
return false;
}
break;
case ID_NUMBER_INT:
return getIntValue() != 0;
case ID_TRUE:
return true;
case ID_FALSE:
case ID_NULL:
return false;
case ID_EMBEDDED_OBJECT:
Object value = getEmbeddedObject();
if (value instanceof Boolean) {
return (Boolean) value;
}
break;
default:
}
}
return defaultValue;
}
@Override
public int getValueAsInt() throws IOException
{
JsonToken t = _currToken;
if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) {
return getIntValue();
}
return getValueAsInt(0);
}
@Override
public int getValueAsInt(int defaultValue) throws IOException
{
JsonToken t = _currToken;
if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) {
return getIntValue();
}
if (t != null) {
switch (t.id()) {
case ID_STRING:
String str = getText();
if (_hasTextualNull(str)) {
return 0;
}
return NumberInput.parseAsInt(str, defaultValue);
case ID_TRUE:
return 1;
case ID_FALSE:
return 0;
case ID_NULL:
return 0;
case ID_EMBEDDED_OBJECT:
Object value = getEmbeddedObject();
if (value instanceof Number) {
return ((Number) value).intValue();
}
}
}
return defaultValue;
}
@Override
public long getValueAsLong() throws IOException
{
JsonToken t = _currToken;
if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) {
return getLongValue();
}
return getValueAsLong(0L);
}
@Override
public long getValueAsLong(long defaultValue) throws IOException
{
JsonToken t = _currToken;
if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) {
return getLongValue();
}
if (t != null) {
switch (t.id()) {
case ID_STRING:
String str = getText();
if (_hasTextualNull(str)) {
return 0L;
}
return NumberInput.parseAsLong(str, defaultValue);
case ID_TRUE:
return 1L;
case ID_FALSE:
case ID_NULL:
return 0L;
case ID_EMBEDDED_OBJECT:
Object value = getEmbeddedObject();
if (value instanceof Number) {
return ((Number) value).longValue();
}
}
}
return defaultValue;
}
@Override
public double getValueAsDouble(double defaultValue) throws IOException
{
JsonToken t = _currToken;
if (t != null) {
switch (t.id()) {
case ID_STRING:
String str = getText();
if (_hasTextualNull(str)) {
return 0L;
}
return NumberInput.parseAsDouble(str, defaultValue);
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return getDoubleValue();
case ID_TRUE:
return 1.0;
case ID_FALSE:
case ID_NULL:
return 0.0;
case ID_EMBEDDED_OBJECT:
Object value = this.getEmbeddedObject();
if (value instanceof Number) {
return ((Number) value).doubleValue();
}
}
}
return defaultValue;
}
@Override
public String getValueAsString() throws IOException {
if (_currToken == JsonToken.VALUE_STRING) {
return getText();
}
if (_currToken == JsonToken.FIELD_NAME) {
return getCurrentName();
}
return getValueAsString(null);
}
@Override
public String getValueAsString(String defaultValue) throws IOException {
if (_currToken == JsonToken.VALUE_STRING) {
return getText();
}
if (_currToken == JsonToken.FIELD_NAME) {
return getCurrentName();
}
if (_currToken == null || _currToken == JsonToken.VALUE_NULL || !_currToken.isScalarValue()) {
return defaultValue;
}
return getText();
}
/*
/**********************************************************
/* Base64 decoding
/**********************************************************
*/
/**
* Helper method that can be used for base64 decoding in cases where
* encoded content has already been read as a String.
*/
protected void _decodeBase64(String str, ByteArrayBuilder builder, Base64Variant b64variant) throws IOException
{
try {
b64variant.decode(str, builder);
} catch (IllegalArgumentException e) {
_reportError(e.getMessage());
}
}
/*
/**********************************************************
/* Coercion helper methods (overridable)
/**********************************************************
*/
/**
* Helper method used to determine whether we are currently pointing to
* a String value of "null" (NOT a null token); and, if so, that parser
* is to recognize and return it similar to if it was real null token.
*
* @since 2.3
*/
protected boolean _hasTextualNull(String value) { return "null".equals(value); }
/*
/**********************************************************
/* Error reporting
/**********************************************************
*/
protected void reportUnexpectedNumberChar(int ch, String comment) throws JsonParseException {
String msg = String.format("Unexpected character (%s) in numeric value", _getCharDesc(ch));
if (comment != null) {
msg += ": "+comment;
}
_reportError(msg);
}
/**
* Method called to throw an exception for input token that looks like a number
* based on first character(s), but is not valid according to rules of format.
* In case of JSON this also includes invalid forms like positive sign and
* leading zeroes.
*/
protected void reportInvalidNumber(String msg) throws JsonParseException {
_reportError("Invalid numeric value: "+msg);
}
/**
* Method called to throw an exception for integral (not floating point) input
* token with value outside of Java signed 32-bit range when requested as {@link int}.
* Result will be {@link InputCoercionException} being thrown.
*/
protected void reportOverflowInt() throws IOException {
reportOverflowInt(getText());
}
// @since 2.10
protected void reportOverflowInt(String numDesc) throws IOException {
reportOverflowInt(numDesc, JsonToken.VALUE_NUMBER_INT);
}
// @since 2.10
protected void reportOverflowInt(String numDesc, JsonToken inputType) throws IOException {
_reportInputCoercion(String.format("Numeric value (%s) out of range of int (%d - %s)",
_longIntegerDesc(numDesc), Integer.MIN_VALUE, Integer.MAX_VALUE),
inputType, Integer.TYPE);
}
/**
* Method called to throw an exception for integral (not floating point) input
* token with value outside of Java signed 64-bit range when requested as {@link long}.
* Result will be {@link InputCoercionException} being thrown.
*/
protected void reportOverflowLong() throws IOException {
reportOverflowLong(getText());
}
// @since 2.10
protected void reportOverflowLong(String numDesc) throws IOException {
reportOverflowLong(numDesc, JsonToken.VALUE_NUMBER_INT);
}
// @since 2.10
protected void reportOverflowLong(String numDesc, JsonToken inputType) throws IOException {
_reportInputCoercion(String.format("Numeric value (%s) out of range of long (%d - %s)",
_longIntegerDesc(numDesc), Long.MIN_VALUE, Long.MAX_VALUE),
inputType, Long.TYPE);
}
/**
* @since 2.10
*/
protected void _reportInputCoercion(String msg, JsonToken inputType, Class> targetType)
throws InputCoercionException {
throw new InputCoercionException(this, msg, inputType, targetType);
}
// @since 2.9.8
protected String _longIntegerDesc(String rawNum) {
int rawLen = rawNum.length();
if (rawLen < 1000) {
return rawNum;
}
if (rawNum.startsWith("-")) {
rawLen -= 1;
}
return String.format("[Integer with %d digits]", rawLen);
}
// @since 2.9.8
protected String _longNumberDesc(String rawNum) {
int rawLen = rawNum.length();
if (rawLen < 1000) {
return rawNum;
}
if (rawNum.startsWith("-")) {
rawLen -= 1;
}
return String.format("[number with %d characters]", rawLen);
}
protected void _reportUnexpectedChar(int ch, String comment) throws JsonParseException
{
if (ch < 0) { // sanity check
_reportInvalidEOF();
}
String msg = String.format("Unexpected character (%s)", _getCharDesc(ch));
if (comment != null) {
msg += ": "+comment;
}
_reportError(msg);
}
protected void _reportInvalidEOF() throws JsonParseException {
_reportInvalidEOF(" in "+_currToken, _currToken);
}
/**
* @since 2.8
*/
protected void _reportInvalidEOFInValue(JsonToken type) throws JsonParseException {
String msg;
if (type == JsonToken.VALUE_STRING) {
msg = " in a String value";
} else if ((type == JsonToken.VALUE_NUMBER_INT)
|| (type == JsonToken.VALUE_NUMBER_FLOAT)) {
msg = " in a Number value";
} else {
msg = " in a value";
}
_reportInvalidEOF(msg, type);
}
/**
* @since 2.8
*/
protected void _reportInvalidEOF(String msg, JsonToken currToken) throws JsonParseException {
throw new JsonEOFException(this, currToken, "Unexpected end-of-input"+msg);
}
/**
* @deprecated Since 2.8 use {@link #_reportInvalidEOF(String, JsonToken)} instead
*/
@Deprecated // since 2.8
protected void _reportInvalidEOFInValue() throws JsonParseException {
_reportInvalidEOF(" in a value");
}
/**
* @deprecated Since 2.8 use {@link #_reportInvalidEOF(String, JsonToken)} instead
*/
@Deprecated // since 2.8
protected void _reportInvalidEOF(String msg) throws JsonParseException {
throw new JsonEOFException(this, null, "Unexpected end-of-input"+msg);
}
protected void _reportMissingRootWS(int ch) throws JsonParseException {
_reportUnexpectedChar(ch, "Expected space separating root-level values");
}
protected void _throwInvalidSpace(int i) throws JsonParseException {
char c = (char) i;
String msg = "Illegal character ("+_getCharDesc(c)+"): only regular white space (\\r, \\n, \\t) is allowed between tokens";
_reportError(msg);
}
/*
/**********************************************************
/* Error reporting, generic
/**********************************************************
*/
protected final static String _getCharDesc(int ch)
{
char c = (char) ch;
if (Character.isISOControl(c)) {
return "(CTRL-CHAR, code "+ch+")";
}
if (ch > 255) {
return "'"+c+"' (code "+ch+" / 0x"+Integer.toHexString(ch)+")";
}
return "'"+c+"' (code "+ch+")";
}
protected final void _reportError(String msg) throws JsonParseException {
throw _constructError(msg);
}
// @since 2.9
protected final void _reportError(String msg, Object arg) throws JsonParseException {
throw _constructError(String.format(msg, arg));
}
// @since 2.9
protected final void _reportError(String msg, Object arg1, Object arg2) throws JsonParseException {
throw _constructError(String.format(msg, arg1, arg2));
}
protected final void _wrapError(String msg, Throwable t) throws JsonParseException {
throw _constructError(msg, t);
}
protected final void _throwInternal() {
VersionUtil.throwInternal();
}
protected final JsonParseException _constructError(String msg, Throwable t) {
return new JsonParseException(this, msg, t);
}
protected static byte[] _asciiBytes(String str) {
byte[] b = new byte[str.length()];
for (int i = 0, len = str.length(); i < len; ++i) {
b[i] = (byte) str.charAt(i);
}
return b;
}
protected static String _ascii(byte[] b) {
try {
return new String(b, "US-ASCII");
} catch (IOException e) { // never occurs
throw new RuntimeException(e);
}
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/base/package-info.java 0000664 0000000 0000000 00000000623 13561642473 0032441 0 ustar 00root root 0000000 0000000 /**
* Base classes used by concrete Parser and Generator implementations;
* contain functionality that is not specific to JSON or input
* abstraction (byte vs char).
* Most formats extend these types, although it is also possible to
* directly extend {@link com.fasterxml.jackson.core.JsonParser} or
* {@link com.fasterxml.jackson.core.JsonGenerator}.
*/
package com.fasterxml.jackson.core.base;
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/exc/ 0000775 0000000 0000000 00000000000 13561642473 0027116 5 ustar 00root root 0000000 0000000 InputCoercionException.java 0000664 0000000 0000000 00000004373 13561642473 0034351 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/exc package com.fasterxml.jackson.core.exc;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.util.RequestPayload;
/**
* Exception type for read-side problems that are not direct decoding ("parsing")
* problems (those would be reported as {@link com.fasterxml.jackson.core.JsonParseException}s),
* but rather result from failed attempts to convert specific Java value out of valid
* but incompatible input value. One example is numeric coercions where target number type's
* range does not allow mapping of too large/too small input value.
*
* @since 2.10
*/
public class InputCoercionException extends StreamReadException {
private static final long serialVersionUID = 1L;
/**
* Input token that represents input value that failed to coerce.
*/
protected final JsonToken _inputType;
/**
* Target type that input value failed to coerce to.
*/
protected final Class> _targetType;
/**
* Constructor that uses current parsing location as location, and
* sets processor (accessible via {@link #getProcessor()}) to
* specified parser.
*/
public InputCoercionException(JsonParser p, String msg,
JsonToken inputType, Class> targetType) {
super(p, msg);
_inputType = inputType;
_targetType = targetType;
}
/**
* Fluent method that may be used to assign originating {@link JsonParser},
* to be accessed using {@link #getProcessor()}.
*nextToken()
,
* as well as if token has been explicitly cleared
*/
protected JsonToken _currToken;
/**
* Last cleared token, if any: that is, value that was in
* effect when {@link #clearCurrentToken} was called.
*/
protected JsonToken _lastClearedToken;
/**
* During traversal this is the actual "open" parse tree, which sometimes
* is the same as {@link #_exposedContext}, and at other times is ahead
* of it. Note that this context is never null.
*/
protected TokenFilterContext _headContext;
/**
* In cases where {@link #_headContext} is "ahead" of context exposed to
* caller, this context points to what is currently exposed to caller.
* When the two are in sync, this context reference will be null
.
*/
protected TokenFilterContext _exposedContext;
/**
* State that applies to the item within container, used where applicable.
* Specifically used to pass inclusion state between property name and
* property, and also used for array elements.
*/
protected TokenFilter _itemFilter;
/**
* Number of tokens for which {@link TokenFilter#INCLUDE_ALL}
* has been returned.
*/
protected int _matchCount;
/*
/**********************************************************
/* Construction, initialization
/**********************************************************
*/
public FilteringParserDelegate(JsonParser p, TokenFilter f,
boolean includePath, boolean allowMultipleMatches)
{
super(p);
rootFilter = f;
// and this is the currently active filter for root values
_itemFilter = f;
_headContext = TokenFilterContext.createRootContext(f);
_includePath = includePath;
_allowMultipleMatches = allowMultipleMatches;
}
/*
/**********************************************************
/* Extended API
/**********************************************************
*/
public TokenFilter getFilter() { return rootFilter; }
/**
* Accessor for finding number of matches, where specific token and sub-tree
* starting (if structured type) are passed.
*/
public int getMatchCount() {
return _matchCount;
}
/*
/**********************************************************
/* Public API, token accessors
/**********************************************************
*/
@Override public JsonToken getCurrentToken() { return _currToken; }
@Override public JsonToken currentToken() { return _currToken; }
@Override public final int getCurrentTokenId() {
final JsonToken t = _currToken;
return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
}
@Override public final int currentTokenId() {
final JsonToken t = _currToken;
return (t == null) ? JsonTokenId.ID_NO_TOKEN : t.id();
}
@Override public boolean hasCurrentToken() { return _currToken != null; }
@Override public boolean hasTokenId(int id) {
final JsonToken t = _currToken;
if (t == null) {
return (JsonTokenId.ID_NO_TOKEN == id);
}
return t.id() == id;
}
@Override public final boolean hasToken(JsonToken t) {
return (_currToken == t);
}
@Override public boolean isExpectedStartArrayToken() { return _currToken == JsonToken.START_ARRAY; }
@Override public boolean isExpectedStartObjectToken() { return _currToken == JsonToken.START_OBJECT; }
@Override public JsonLocation getCurrentLocation() { return delegate.getCurrentLocation(); }
@Override
public JsonStreamContext getParsingContext() {
return _filterContext();
}
// !!! TODO: Verify it works as expected: copied from standard JSON parser impl
@Override
public String getCurrentName() throws IOException {
JsonStreamContext ctxt = _filterContext();
if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
JsonStreamContext parent = ctxt.getParent();
return (parent == null) ? null : parent.getCurrentName();
}
return ctxt.getCurrentName();
}
/*
/**********************************************************
/* Public API, token state overrides
/**********************************************************
*/
@Override
public void clearCurrentToken() {
if (_currToken != null) {
_lastClearedToken = _currToken;
_currToken = null;
}
}
@Override
public JsonToken getLastClearedToken() { return _lastClearedToken; }
@Override
public void overrideCurrentName(String name) {
/* 14-Apr-2015, tatu: Not sure whether this can be supported, and if so,
* what to do with it... Delegation won't work for sure, so let's for
* now throw an exception
*/
throw new UnsupportedOperationException("Can not currently override name during filtering read");
}
/*
/**********************************************************
/* Public API, traversal
/**********************************************************
*/
@Override
public JsonToken nextToken() throws IOException
{
// 23-May-2017, tatu: To be honest, code here is rather hairy and I don't like all
// conditionals; and it seems odd to return `null` but NOT considering input
// as closed... would love a rewrite to simplify/clear up logic here.
// Check for _allowMultipleMatches - false and at least there is one token - which is _currToken
// check for no buffered context _exposedContext - null
// If all the conditions matches then check for scalar / non-scalar property
if (!_allowMultipleMatches && (_currToken != null) && (_exposedContext == null)) {
// if scalar, and scalar not present in obj/array and !includePath and INCLUDE_ALL
// matched once, return null
if (_currToken.isScalarValue() && !_headContext.isStartHandled() && !_includePath
&& (_itemFilter == TokenFilter.INCLUDE_ALL)) {
return (_currToken = null);
}
}
// Anything buffered?
TokenFilterContext ctxt = _exposedContext;
if (ctxt != null) {
while (true) {
JsonToken t = ctxt.nextTokenToRead();
if (t != null) {
_currToken = t;
return t;
}
// all done with buffered stuff?
if (ctxt == _headContext) {
_exposedContext = null;
if (ctxt.inArray()) {
t = delegate.getCurrentToken();
// Is this guaranteed to work without further checks?
// if (t != JsonToken.START_ARRAY) {
_currToken = t;
return t;
}
// Almost! Most likely still have the current token;
// with the sole exception of
/*
t = delegate.getCurrentToken();
if (t != JsonToken.FIELD_NAME) {
_currToken = t;
return t;
}
*/
break;
}
// If not, traverse down the context chain
ctxt = _headContext.findChildOf(ctxt);
_exposedContext = ctxt;
if (ctxt == null) { // should never occur
throw _constructError("Unexpected problem: chain of filtered context broken");
}
}
}
// If not, need to read more. If we got any:
JsonToken t = delegate.nextToken();
if (t == null) {
// no strict need to close, since we have no state here
_currToken = t;
return t;
}
// otherwise... to include or not?
TokenFilter f;
switch (t.id()) {
case ID_START_ARRAY:
f = _itemFilter;
if (f == TokenFilter.INCLUDE_ALL) {
_headContext = _headContext.createChildArrayContext(f, true);
return (_currToken = t);
}
if (f == null) { // does this occur?
delegate.skipChildren();
break;
}
// Otherwise still iffy, need to check
f = _headContext.checkValue(f);
if (f == null) {
delegate.skipChildren();
break;
}
if (f != TokenFilter.INCLUDE_ALL) {
f = f.filterStartArray();
}
_itemFilter = f;
if (f == TokenFilter.INCLUDE_ALL) {
_headContext = _headContext.createChildArrayContext(f, true);
return (_currToken = t);
}
_headContext = _headContext.createChildArrayContext(f, false);
// Also: only need buffering if parent path to be included
if (_includePath) {
t = _nextTokenWithBuffering(_headContext);
if (t != null) {
_currToken = t;
return t;
}
}
break;
case ID_START_OBJECT:
f = _itemFilter;
if (f == TokenFilter.INCLUDE_ALL) {
_headContext = _headContext.createChildObjectContext(f, true);
return (_currToken = t);
}
if (f == null) { // does this occur?
delegate.skipChildren();
break;
}
// Otherwise still iffy, need to check
f = _headContext.checkValue(f);
if (f == null) {
delegate.skipChildren();
break;
}
if (f != TokenFilter.INCLUDE_ALL) {
f = f.filterStartObject();
}
_itemFilter = f;
if (f == TokenFilter.INCLUDE_ALL) {
_headContext = _headContext.createChildObjectContext(f, true);
return (_currToken = t);
}
_headContext = _headContext.createChildObjectContext(f, false);
// Also: only need buffering if parent path to be included
if (_includePath) {
t = _nextTokenWithBuffering(_headContext);
if (t != null) {
_currToken = t;
return t;
}
}
// note: inclusion of surrounding Object handled separately via
// FIELD_NAME
break;
case ID_END_ARRAY:
case ID_END_OBJECT:
{
boolean returnEnd = _headContext.isStartHandled();
f = _headContext.getFilter();
if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
f.filterFinishArray();
}
_headContext = _headContext.getParent();
_itemFilter = _headContext.getFilter();
if (returnEnd) {
return (_currToken = t);
}
}
break;
case ID_FIELD_NAME:
{
final String name = delegate.getCurrentName();
// note: this will also set 'needToHandleName'
f = _headContext.setFieldName(name);
if (f == TokenFilter.INCLUDE_ALL) {
_itemFilter = f;
if (!_includePath) {
// Minor twist here: if parent NOT included, may need to induce output of
// surrounding START_OBJECT/END_OBJECT
if (_includeImmediateParent && !_headContext.isStartHandled()) {
t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled
_exposedContext = _headContext;
}
}
return (_currToken = t);
}
if (f == null) {
delegate.nextToken();
delegate.skipChildren();
break;
}
f = f.includeProperty(name);
if (f == null) {
delegate.nextToken();
delegate.skipChildren();
break;
}
_itemFilter = f;
if (f == TokenFilter.INCLUDE_ALL) {
if (_verifyAllowedMatches()) {
if (_includePath) {
return (_currToken = t);
}
} else {
delegate.nextToken();
delegate.skipChildren();
}
}
if (_includePath) {
t = _nextTokenWithBuffering(_headContext);
if (t != null) {
_currToken = t;
return t;
}
}
break;
}
default: // scalar value
f = _itemFilter;
if (f == TokenFilter.INCLUDE_ALL) {
return (_currToken = t);
}
if (f != null) {
f = _headContext.checkValue(f);
if ((f == TokenFilter.INCLUDE_ALL)
|| ((f != null) && f.includeValue(delegate))) {
if (_verifyAllowedMatches()) {
return (_currToken = t);
}
}
}
// Otherwise not included (leaves must be explicitly included)
break;
}
// We get here if token was not yet found; offlined handling
return _nextToken2();
}
/**
* Offlined handling for cases where there was no buffered token to
* return, and the token read next could not be returned as-is,
* at least not yet, but where we have not yet established that
* buffering is needed.
*/
protected final JsonToken _nextToken2() throws IOException
{
main_loop:
while (true) {
JsonToken t = delegate.nextToken();
if (t == null) { // is this even legal?
_currToken = t;
return t;
}
TokenFilter f;
switch (t.id()) {
case ID_START_ARRAY:
f = _itemFilter;
if (f == TokenFilter.INCLUDE_ALL) {
_headContext = _headContext.createChildArrayContext(f, true);
return (_currToken = t);
}
if (f == null) { // does this occur?
delegate.skipChildren();
continue main_loop;
}
// Otherwise still iffy, need to check
f = _headContext.checkValue(f);
if (f == null) {
delegate.skipChildren();
continue main_loop;
}
if (f != TokenFilter.INCLUDE_ALL) {
f = f.filterStartArray();
}
_itemFilter = f;
if (f == TokenFilter.INCLUDE_ALL) {
_headContext = _headContext.createChildArrayContext(f, true);
return (_currToken = t);
}
_headContext = _headContext.createChildArrayContext(f, false);
// but if we didn't figure it out yet, need to buffer possible events
if (_includePath) {
t = _nextTokenWithBuffering(_headContext);
if (t != null) {
_currToken = t;
return t;
}
}
continue main_loop;
case ID_START_OBJECT:
f = _itemFilter;
if (f == TokenFilter.INCLUDE_ALL) {
_headContext = _headContext.createChildObjectContext(f, true);
return (_currToken = t);
}
if (f == null) { // does this occur?
delegate.skipChildren();
continue main_loop;
}
// Otherwise still iffy, need to check
f = _headContext.checkValue(f);
if (f == null) {
delegate.skipChildren();
continue main_loop;
}
if (f != TokenFilter.INCLUDE_ALL) {
f = f.filterStartObject();
}
_itemFilter = f;
if (f == TokenFilter.INCLUDE_ALL) {
_headContext = _headContext.createChildObjectContext(f, true);
return (_currToken = t);
}
_headContext = _headContext.createChildObjectContext(f, false);
if (_includePath) {
t = _nextTokenWithBuffering(_headContext);
if (t != null) {
_currToken = t;
return t;
}
}
continue main_loop;
case ID_END_ARRAY:
case ID_END_OBJECT:
{
boolean returnEnd = _headContext.isStartHandled();
f = _headContext.getFilter();
if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
f.filterFinishArray();
}
_headContext = _headContext.getParent();
_itemFilter = _headContext.getFilter();
if (returnEnd) {
return (_currToken = t);
}
}
continue main_loop;
case ID_FIELD_NAME:
{
final String name = delegate.getCurrentName();
f = _headContext.setFieldName(name);
if (f == TokenFilter.INCLUDE_ALL) {
_itemFilter = f;
return (_currToken = t);
}
if (f == null) { // filter out the value
delegate.nextToken();
delegate.skipChildren();
continue main_loop;
}
f = f.includeProperty(name);
if (f == null) { // filter out the value
delegate.nextToken();
delegate.skipChildren();
continue main_loop;
}
_itemFilter = f;
if (f == TokenFilter.INCLUDE_ALL) {
if (_verifyAllowedMatches() && _includePath) {
return (_currToken = t);
}
// if (_includeImmediateParent) { ...
continue main_loop;
}
if (_includePath) {
t = _nextTokenWithBuffering(_headContext);
if (t != null) {
_currToken = t;
return t;
}
}
}
continue main_loop;
default: // scalar value
f = _itemFilter;
if (f == TokenFilter.INCLUDE_ALL) {
return (_currToken = t);
}
if (f != null) {
f = _headContext.checkValue(f);
if ((f == TokenFilter.INCLUDE_ALL)
|| ((f != null) && f.includeValue(delegate))) {
if (_verifyAllowedMatches()) {
return (_currToken = t);
}
}
}
// Otherwise not included (leaves must be explicitly included)
break;
}
}
}
/**
* Method called when a new potentially included context is found.
*/
protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffRoot)
throws IOException
{
main_loop:
while (true) {
JsonToken t = delegate.nextToken();
if (t == null) { // is this even legal?
return t;
}
TokenFilter f;
// One simplification here: we know for a fact that the item filter is
// neither null nor 'include all', for most cases; the only exception
// being FIELD_NAME handling
switch (t.id()) {
case ID_START_ARRAY:
f = _headContext.checkValue(_itemFilter);
if (f == null) {
delegate.skipChildren();
continue main_loop;
}
if (f != TokenFilter.INCLUDE_ALL) {
f = f.filterStartArray();
}
_itemFilter = f;
if (f == TokenFilter.INCLUDE_ALL) {
_headContext = _headContext.createChildArrayContext(f, true);
return _nextBuffered(buffRoot);
}
_headContext = _headContext.createChildArrayContext(f, false);
continue main_loop;
case ID_START_OBJECT:
f = _itemFilter;
if (f == TokenFilter.INCLUDE_ALL) {
_headContext = _headContext.createChildObjectContext(f, true);
return t;
}
if (f == null) { // does this occur?
delegate.skipChildren();
continue main_loop;
}
// Otherwise still iffy, need to check
f = _headContext.checkValue(f);
if (f == null) {
delegate.skipChildren();
continue main_loop;
}
if (f != TokenFilter.INCLUDE_ALL) {
f = f.filterStartObject();
}
_itemFilter = f;
if (f == TokenFilter.INCLUDE_ALL) {
_headContext = _headContext.createChildObjectContext(f, true);
return _nextBuffered(buffRoot);
}
_headContext = _headContext.createChildObjectContext(f, false);
continue main_loop;
case ID_END_ARRAY:
case ID_END_OBJECT:
{
// Unlike with other loops, here we know that content was NOT
// included (won't get this far otherwise)
f = _headContext.getFilter();
if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
f.filterFinishArray();
}
boolean gotEnd = (_headContext == buffRoot);
boolean returnEnd = gotEnd && _headContext.isStartHandled();
_headContext = _headContext.getParent();
_itemFilter = _headContext.getFilter();
if (returnEnd) {
return t;
}
}
continue main_loop;
case ID_FIELD_NAME:
{
final String name = delegate.getCurrentName();
f = _headContext.setFieldName(name);
if (f == TokenFilter.INCLUDE_ALL) {
_itemFilter = f;
return _nextBuffered(buffRoot);
}
if (f == null) { // filter out the value
delegate.nextToken();
delegate.skipChildren();
continue main_loop;
}
f = f.includeProperty(name);
if (f == null) { // filter out the value
delegate.nextToken();
delegate.skipChildren();
continue main_loop;
}
_itemFilter = f;
if (f == TokenFilter.INCLUDE_ALL) {
if (_verifyAllowedMatches()) {
return _nextBuffered(buffRoot);
} else {
// edge case: if no more matches allowed, reset filter
// to initial state to prevent missing a token in next iteration
_itemFilter = _headContext.setFieldName(name);
}
}
}
continue main_loop;
default: // scalar value
f = _itemFilter;
if (f == TokenFilter.INCLUDE_ALL) {
return _nextBuffered(buffRoot);
}
if (f != null) {
f = _headContext.checkValue(f);
if ((f == TokenFilter.INCLUDE_ALL)
|| ((f != null) && f.includeValue(delegate))) {
if (_verifyAllowedMatches()) {
return _nextBuffered(buffRoot);
}
}
}
// Otherwise not included (leaves must be explicitly included)
continue main_loop;
}
}
}
private JsonToken _nextBuffered(TokenFilterContext buffRoot) throws IOException
{
_exposedContext = buffRoot;
TokenFilterContext ctxt = buffRoot;
JsonToken t = ctxt.nextTokenToRead();
if (t != null) {
return t;
}
while (true) {
// all done with buffered stuff?
if (ctxt == _headContext) {
throw _constructError("Internal error: failed to locate expected buffered tokens");
/*
_exposedContext = null;
break;
*/
}
// If not, traverse down the context chain
ctxt = _exposedContext.findChildOf(ctxt);
_exposedContext = ctxt;
if (ctxt == null) { // should never occur
throw _constructError("Unexpected problem: chain of filtered context broken");
}
t = _exposedContext.nextTokenToRead();
if (t != null) {
return t;
}
}
}
private final boolean _verifyAllowedMatches() throws IOException {
if (_matchCount == 0 || _allowMultipleMatches) {
++_matchCount;
return true;
}
return false;
}
@Override
public JsonToken nextValue() throws IOException {
// Re-implemented same as ParserMinimalBase:
JsonToken t = nextToken();
if (t == JsonToken.FIELD_NAME) {
t = nextToken();
}
return t;
}
/**
* Need to override, re-implement similar to how method defined in
* {@link com.fasterxml.jackson.core.base.ParserMinimalBase}, to keep
* state correct here.
*/
@Override
public JsonParser skipChildren() throws IOException
{
if ((_currToken != JsonToken.START_OBJECT)
&& (_currToken != JsonToken.START_ARRAY)) {
return this;
}
int open = 1;
// Since proper matching of start/end markers is handled
// by nextToken(), we'll just count nesting levels here
while (true) {
JsonToken t = nextToken();
if (t == null) { // not ideal but for now, just return
return this;
}
if (t.isStructStart()) {
++open;
} else if (t.isStructEnd()) {
if (--open == 0) {
return this;
}
}
}
}
/*
/**********************************************************
/* Public API, access to token information, text
/**********************************************************
*/
@Override public String getText() throws IOException { return delegate.getText(); }
@Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); }
@Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
@Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
@Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
/*
/**********************************************************
/* Public API, access to token information, numeric
/**********************************************************
*/
@Override
public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); }
@Override
public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); }
@Override
public byte getByteValue() throws IOException { return delegate.getByteValue(); }
@Override
public short getShortValue() throws IOException { return delegate.getShortValue(); }
@Override
public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); }
@Override
public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); }
@Override
public float getFloatValue() throws IOException { return delegate.getFloatValue(); }
@Override
public int getIntValue() throws IOException { return delegate.getIntValue(); }
@Override
public long getLongValue() throws IOException { return delegate.getLongValue(); }
@Override
public NumberType getNumberType() throws IOException { return delegate.getNumberType(); }
@Override
public Number getNumberValue() throws IOException { return delegate.getNumberValue(); }
/*
/**********************************************************
/* Public API, access to token information, coercion/conversion
/**********************************************************
*/
@Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); }
@Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); }
@Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); }
@Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); }
@Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); }
@Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); }
@Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); }
@Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); }
@Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); }
@Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); }
/*
/**********************************************************
/* Public API, access to token values, other
/**********************************************************
*/
@Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); }
@Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); }
@Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); }
@Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
/*
/**********************************************************
/* Internal helper methods
/**********************************************************
*/
protected JsonStreamContext _filterContext() {
if (_exposedContext != null) {
return _exposedContext;
}
return _headContext;
}
}
JsonPointerBasedFilter.java 0000664 0000000 0000000 00000003325 13561642473 0034772 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/filter package com.fasterxml.jackson.core.filter;
import com.fasterxml.jackson.core.JsonPointer;
/**
* Simple {@link TokenFilter} implementation that takes a single
* {@link JsonPointer} and matches a single value accordingly.
* Instances are immutable and fully thread-safe, shareable,
* and efficient to use.
*
* @since 2.6
*/
public class JsonPointerBasedFilter extends TokenFilter
{
protected final JsonPointer _pathToMatch;
public JsonPointerBasedFilter(String ptrExpr) {
this(JsonPointer.compile(ptrExpr));
}
public JsonPointerBasedFilter(JsonPointer match) {
_pathToMatch = match;
}
@Override
public TokenFilter includeElement(int index) {
JsonPointer next = _pathToMatch.matchElement(index);
if (next == null) {
return null;
}
if (next.matches()) {
return TokenFilter.INCLUDE_ALL;
}
return new JsonPointerBasedFilter(next);
}
@Override
public TokenFilter includeProperty(String name) {
JsonPointer next = _pathToMatch.matchProperty(name);
if (next == null) {
return null;
}
if (next.matches()) {
return TokenFilter.INCLUDE_ALL;
}
return new JsonPointerBasedFilter(next);
}
@Override
public TokenFilter filterStartArray() {
return this;
}
@Override
public TokenFilter filterStartObject() {
return this;
}
@Override
protected boolean _includeScalar() {
// should only occur for root-level scalars, path "/"
return _pathToMatch.matches();
}
@Override
public String toString() {
return "[JsonPointerFilter at: "+_pathToMatch+"]";
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/filter/TokenFilter.java 0000664 0000000 0000000 00000030011 13561642473 0032710 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.filter;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
/**
* Strategy class that can be implemented to specify actual inclusion/exclusion
* criteria for filtering, used by {@link FilteringGeneratorDelegate}.
*
* @since 2.6
*/
public class TokenFilter
{
// // Marker values
/**
* Marker value that should be used to indicate inclusion of a structured
* value (sub-tree representing Object or Array), or value of a named
* property (regardless of type).
* Note that if this instance is returned, it will used as a marker, and
* no actual callbacks need to be made. For this reason, it is more efficient
* to return this instance if the whole sub-tree is to be included, instead
* of implementing similar filter functionality explicitly.
*/
public final static TokenFilter INCLUDE_ALL = new TokenFilter();
// Life-cycle
protected TokenFilter() { }
/*
/**********************************************************
/* API, structured values
/**********************************************************
*/
/**
* Method called to check whether Object value at current output
* location should be included in output.
* Three kinds of return values may be used as follows:
*
*
*null
to indicate that the Object should be skipped
* this
, which means that checks
* are made recursively for properties of the Object to determine possible inclusion.
*
* @return TokenFilter to use for further calls within Array, unless return value
* is null
or {@link #INCLUDE_ALL} (which have simpler semantics)
*/
public TokenFilter filterStartObject() {
return this;
}
/**
* Method called to check whether Array value at current output
* location should be included in output.
* Three kinds of return values may be used as follows:
*
*
*null
to indicate that the Array should be skipped
* this
, which means that checks
* are made recursively for elements of the array to determine possible inclusion.
*
* @return TokenFilter to use for further calls within Array, unless return value
* is null
or {@link #INCLUDE_ALL} (which have simpler semantics)
*/
public TokenFilter filterStartArray() {
return this;
}
/**
* Method called to indicate that output of non-filtered Object (one that may
* have been included either completely, or in part) is completed,
* in cases where filter other that {@link #INCLUDE_ALL} was returned.
* This occurs when {@link JsonGenerator#writeEndObject()} is called.
*/
public void filterFinishObject() { }
/**
* Method called to indicate that output of non-filtered Array (one that may
* have been included either completely, or in part) is completed,
* in cases where filter other that {@link #INCLUDE_ALL} was returned.
* This occurs when {@link JsonGenerator#writeEndArray()} is called.
*/
public void filterFinishArray() { }
/*
/**********************************************************
/* API, properties/elements
/**********************************************************
*/
/**
* Method called to check whether property value with specified name,
* at current output location, should be included in output.
* Three kinds of return values may be used as follows:
*
*
*null
to indicate that the property and its value should be skipped
* this
to continue calling
* methods on this filter object, without full inclusion or exclusion.
*
* @return TokenFilter to use for further calls within property value, unless return value
* is null
or {@link #INCLUDE_ALL} (which have simpler semantics)
*/
public TokenFilter includeProperty(String name) {
return this;
}
/**
* Method called to check whether array element with specified index (zero-based),
* at current output location, should be included in output.
* Three kinds of return values may be used as follows:
*
*
*null
to indicate that the Array element should be skipped
* this
to continue calling
* methods on this filter object, without full inclusion or exclusion.
*
* @return TokenFilter to use for further calls within element value, unless return value
* is null
or {@link #INCLUDE_ALL} (which have simpler semantics)
*/
public TokenFilter includeElement(int index) {
return this;
}
/**
* Method called to check whether root-level value,
* at current output location, should be included in output.
* Three kinds of return values may be used as follows:
*
*
*null
to indicate that the root value should be skipped
* this
to continue calling
* methods on this filter object, without full inclusion or exclusion.
*
* @return TokenFilter to use for further calls within root value, unless return value
* is null
or {@link #INCLUDE_ALL} (which have simpler semantics)
*/
public TokenFilter includeRootValue(int index) {
return this;
}
/*
/**********************************************************
/* API, scalar values (being read)
/**********************************************************
*/
/**
* Call made when verifying whether a scaler value is being
* read from a parser.
*_includeScalar()
and return
* whatever it indicates.
*/
public boolean includeValue(JsonParser p) throws IOException {
return _includeScalar();
}
/*
/**********************************************************
/* API, scalar values (being written)
/**********************************************************
*/
/**
* Call made to verify whether leaf-level
* boolean value
* should be included in output or not.
*/
public boolean includeBoolean(boolean value) {
return _includeScalar();
}
/**
* Call made to verify whether leaf-level
* null value
* should be included in output or not.
*/
public boolean includeNull() {
return _includeScalar();
}
/**
* Call made to verify whether leaf-level
* String value
* should be included in output or not.
*/
public boolean includeString(String value) {
return _includeScalar();
}
/**
* Call made to verify whether leaf-level
* int
value
* should be included in output or not.
*
* NOTE: also called for `short`, `byte`
*/
public boolean includeNumber(int v) {
return _includeScalar();
}
/**
* Call made to verify whether leaf-level
* long
value
* should be included in output or not.
*/
public boolean includeNumber(long v) {
return _includeScalar();
}
/**
* Call made to verify whether leaf-level
* float
value
* should be included in output or not.
*/
public boolean includeNumber(float v) {
return _includeScalar();
}
/**
* Call made to verify whether leaf-level
* double
value
* should be included in output or not.
*/
public boolean includeNumber(double v) {
return _includeScalar();
}
/**
* Call made to verify whether leaf-level
* {@link BigDecimal} value
* should be included in output or not.
*/
public boolean includeNumber(BigDecimal v) {
return _includeScalar();
}
/**
* Call made to verify whether leaf-level
* {@link BigInteger} value
* should be included in output or not.
*/
public boolean includeNumber(BigInteger v) {
return _includeScalar();
}
/**
* Call made to verify whether leaf-level
* Binary value
* should be included in output or not.
*
* return hasMatch() ? getMatch().getFormatName() : null;
*
*/
public String getMatchedFormatName() {
return _match.getFormatName();
}
/*
/**********************************************************
/* Public API, factory methods
/**********************************************************
*/
/**
* Convenience method for trying to construct a {@link JsonParser} for
* parsing content which is assumed to be in detected data format.
* If no match was found, returns null.
*/
public JsonParser createParserWithMatch() throws IOException {
if (_match == null) {
return null;
}
if (_originalStream == null) {
return _match.createParser(_bufferedData, _bufferedStart, _bufferedLength);
}
return _match.createParser(getDataStream());
}
/**
* Method to use for accessing input for which format detection has been done.
* This must be used instead of using stream passed to detector
* unless given stream itself can do buffering.
* Stream will return all content that was read during matching process, as well
* as remaining contents of the underlying stream.
*/
public InputStream getDataStream() {
if (_originalStream == null) {
return new ByteArrayInputStream(_bufferedData, _bufferedStart, _bufferedLength);
}
return new MergedStream(null, _originalStream, _bufferedData, _bufferedStart, _bufferedLength);
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/format/InputAccessor.java 0000664 0000000 0000000 00000010414 13561642473 0033254 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.format;
import java.io.*;
import com.fasterxml.jackson.core.JsonFactory;
/**
* Interface used to expose beginning of a data file to data format
* detection code.
*/
public interface InputAccessor
{
/**
* Method to call to check if more input is available.
* Since this may result in more content to be read (at least
* one more byte), a {@link IOException} may get thrown.
*/
boolean hasMoreBytes() throws IOException;
/**
* Returns next byte available, if any; if no more bytes are
* available, will throw {@link java.io.EOFException}.
*/
byte nextByte() throws IOException;
/**
* Method that can be called to reset accessor to read from beginning
* of input.
*/
void reset();
/*
/**********************************************************
/* Standard implementation
/**********************************************************
*/
/**
* Basic implementation that reads data from given
* {@link InputStream} and buffers it as necessary.
*/
class Std implements InputAccessor
{
protected final InputStream _in;
protected final byte[] _buffer;
protected final int _bufferedStart;
/**
* End of valid bytes in the buffer (points to one past last valid)
*/
protected int _bufferedEnd;
/**
* Pointer to next available buffered byte in {@link #_buffer}.
*/
protected int _ptr;
/**
* Constructor used when content to check is available via
* input stream and must be read.
*/
public Std(InputStream in, byte[] buffer)
{
_in = in;
_buffer = buffer;
_bufferedStart = 0;
_ptr = 0;
_bufferedEnd = 0;
}
/**
* Constructor used when the full input (or at least enough leading bytes
* of full input) is available.
*/
public Std(byte[] inputDocument)
{
_in = null;
_buffer = inputDocument;
// we have it all:
_bufferedStart = 0;
_bufferedEnd = inputDocument.length;
}
/**
* Constructor used when the full input (or at least enough leading bytes
* of full input) is available.
*
* @since 2.1
*/
public Std(byte[] inputDocument, int start, int len)
{
_in = null;
_buffer = inputDocument;
_ptr = start;
_bufferedStart = start;
_bufferedEnd = start+len;
}
@Override
public boolean hasMoreBytes() throws IOException
{
if (_ptr < _bufferedEnd) { // already got more
return true;
}
if (_in == null) { // nowhere to read from
return false;
}
int amount = _buffer.length - _ptr;
if (amount < 1) { // can not load any more
return false;
}
int count = _in.read(_buffer, _ptr, amount);
if (count <= 0) { // EOF
return false;
}
_bufferedEnd += count;
return true;
}
@Override
public byte nextByte() throws IOException
{
// should we just try loading more automatically?
if (_ptr >= _bufferedEnd) {
if (!hasMoreBytes()) {
throw new EOFException("Failed auto-detect: could not read more than "+_ptr+" bytes (max buffer size: "+_buffer.length+")");
}
}
return _buffer[_ptr++];
}
@Override
public void reset() {
_ptr = _bufferedStart;
}
/*
/**********************************************************
/* Extended API for DataFormatDetector/Matcher
/**********************************************************
*/
public DataFormatMatcher createMatcher(JsonFactory match, MatchStrength matchStrength)
{
return new DataFormatMatcher(_in, _buffer, _bufferedStart, (_bufferedEnd - _bufferedStart),
match, matchStrength);
}
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/format/MatchStrength.java 0000664 0000000 0000000 00000005026 13561642473 0033250 0 ustar 00root root 0000000 0000000 package 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.
*ESCAPE_xxx
constants, or non-zero positive
* integer (meaning of which is data format specific; for JSON it means
* that combination of backslash and character with that value is to be used)
* to indicate that specific escape sequence is to be used.
*/
public abstract int[] getEscapeCodesForAscii();
/**
* Method generators can call to get lookup table for determining
* exact escape sequence to use for given character.
* It can be called for any character, but typically is called for
* either for ASCII characters for which custom escape
* sequence is needed; or for any non-ASCII character.
*/
public abstract SerializableString getEscapeSequence(int ch);
/**
* Helper method that can be used to get a copy of standard JSON
* escape definitions; this is useful when just wanting to slightly
* customize definitions. Caller can modify this array as it sees
* fit and usually returns modified instance via {@link #getEscapeCodesForAscii}
*/
public static int[] standardAsciiEscapesForJSON()
{
int[] esc = CharTypes.get7BitOutputEscapes();
return Arrays.copyOf(esc, esc.length);
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/DataOutputAsStream.java0000664 0000000 0000000 00000001664 13561642473 0033352 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.io;
import java.io.*;
/**
* Helper class to support use of {@link DataOutput} for output, directly,
* without caller having to provide for implementation.
*
* @since 2.8
*/
public class DataOutputAsStream extends OutputStream
{
protected final DataOutput _output;
public DataOutputAsStream(DataOutput out) {
super();
_output = out;
}
@Override
public void write(int b) throws IOException {
_output.write(b);
}
@Override
public void write(byte b[]) throws IOException {
_output.write(b, 0, b.length);
}
@Override
public void write(byte b[], int offset, int length) throws IOException {
_output.write(b, offset, length);
}
// These are no-ops, base class impl works fine
/*
@Override
public void flush() throws IOException { }
@Override
public void close() throws IOException { }
*/
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/IOContext.java 0000664 0000000 0000000 00000023313 13561642473 0031467 0 ustar 00root root 0000000 0000000 package 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.
*JsonGenerator
.
*/
public final class SegmentedStringWriter extends Writer
{
final private TextBuffer _buffer;
public SegmentedStringWriter(BufferRecycler br) {
super();
_buffer = new TextBuffer(br);
}
/*
/**********************************************************
/* java.io.Writer implementation
/**********************************************************
*/
@Override
public Writer append(char c) {
write(c);
return this;
}
@Override
public Writer append(CharSequence csq) {
String str = csq.toString();
_buffer.append(str, 0, str.length());
return this;
}
@Override
public Writer append(CharSequence csq, int start, int end) {
String str = csq.subSequence(start, end).toString();
_buffer.append(str, 0, str.length());
return this;
}
@Override public void close() { } // NOP
@Override public void flush() { } // NOP
@Override
public void write(char[] cbuf) { _buffer.append(cbuf, 0, cbuf.length); }
@Override
public void write(char[] cbuf, int off, int len) { _buffer.append(cbuf, off, len); }
@Override
public void write(int c) { _buffer.append((char) c); }
@Override
public void write(String str) { _buffer.append(str, 0, str.length()); }
@Override
public void write(String str, int off, int len) { _buffer.append(str, off, len); }
/*
/**********************************************************
/* Extended API
/**********************************************************
*/
/**
* Main access method that will construct a String that contains
* all the contents, release all internal buffers we may have,
* and return result String.
* Note that the method is not idempotent -- if called second time,
* will just return an empty String.
*/
public String getAndClear() {
String result = _buffer.contentsAsString();
_buffer.releaseBuffers();
return result;
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/io/SerializedString.java 0000664 0000000 0000000 00000020554 13561642473 0033101 0 ustar 00root root 0000000 0000000 package 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.
*
*
*["value1",,"value3",]
* as ["value1", null, "value3", null]
* [true,true,]
is equivalent to
* [true, true]
and {"a": true,}
is equivalent to
* {"a": true}
.
* ALLOW_MISSING_VALUES
, this feature takes priority, and
* the final trailing comma in an array declaration does not imply a missing
* (null
) value. For example, when both ALLOW_MISSING_VALUES
* and ALLOW_TRAILING_COMMA
are enabled, [true,true,]
is
* equivalent to [true, true]
, and [true,true,,]
is equivalent to
* [true, true, null]
.
*
*
*
* @since 2.8
*/
public class UTF8DataInputJsonParser
extends ParserBase
{
final static byte BYTE_LF = (byte) '\n';
@SuppressWarnings("deprecation")
private final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask();
@SuppressWarnings("deprecation")
private final static int FEAT_MASK_LEADING_ZEROS = Feature.ALLOW_NUMERIC_LEADING_ZEROS.getMask();
@SuppressWarnings("deprecation")
private final static int FEAT_MASK_NON_NUM_NUMBERS = Feature.ALLOW_NON_NUMERIC_NUMBERS.getMask();
@SuppressWarnings("deprecation")
private final static int FEAT_MASK_ALLOW_MISSING = Feature.ALLOW_MISSING_VALUES.getMask();
private final static int FEAT_MASK_ALLOW_SINGLE_QUOTES = Feature.ALLOW_SINGLE_QUOTES.getMask();
private final static int FEAT_MASK_ALLOW_UNQUOTED_NAMES = Feature.ALLOW_UNQUOTED_FIELD_NAMES.getMask();
private final static int FEAT_MASK_ALLOW_JAVA_COMMENTS = Feature.ALLOW_COMMENTS.getMask();
private final static int FEAT_MASK_ALLOW_YAML_COMMENTS = Feature.ALLOW_YAML_COMMENTS.getMask();
// This is the main input-code lookup table, fetched eagerly
private final static int[] _icUTF8 = CharTypes.getInputCodeUtf8();
// Latin1 encoding is not supported, but we do use 8-bit subset for
// pre-processing task, to simplify first pass, keep it fast.
protected final static int[] _icLatin1 = CharTypes.getInputCodeLatin1();
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Codec used for data binding when (if) requested; typically full
* ObjectMapper
, but that abstract is not part of core
* package.
*/
protected ObjectCodec _objectCodec;
/**
* Symbol table that contains field names encountered so far
*/
final protected ByteQuadsCanonicalizer _symbols;
/*
/**********************************************************
/* Parsing state
/**********************************************************
*/
/**
* Temporary buffer used for name parsing.
*/
protected int[] _quadBuffer = new int[16];
/**
* Flag that indicates that the current token has not yet
* been fully processed, and needs to be finished for
* some access (or skipped to obtain the next token)
*/
protected boolean _tokenIncomplete;
/**
* Temporary storage for partially parsed name bytes.
*/
private int _quad1;
/*
/**********************************************************
/* Current input data
/**********************************************************
*/
protected DataInput _inputData;
/**
* Sometimes we need buffering for just a single byte we read but
* have to "push back"
*/
protected int _nextByte = -1;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
public UTF8DataInputJsonParser(IOContext ctxt, int features, DataInput inputData,
ObjectCodec codec, ByteQuadsCanonicalizer sym,
int firstByte)
{
super(ctxt, features);
_objectCodec = codec;
_symbols = sym;
_inputData = inputData;
_nextByte = firstByte;
}
@Override
public ObjectCodec getCodec() {
return _objectCodec;
}
@Override
public void setCodec(ObjectCodec c) {
_objectCodec = c;
}
/*
/**********************************************************
/* Overrides for life-cycle
/**********************************************************
*/
@Override
public int releaseBuffered(OutputStream out) throws IOException {
return 0;
}
@Override
public Object getInputSource() {
return _inputData;
}
/*
/**********************************************************
/* Overrides, low-level reading
/**********************************************************
*/
@Override
protected void _closeInput() throws IOException { }
/**
* Method called to release internal buffers owned by the base
* reader. This may be called along with {@link #_closeInput} (for
* example, when explicitly closing this reader instance), or
* separately (if need be).
*/
@Override
protected void _releaseBuffers() throws IOException
{
super._releaseBuffers();
// Merge found symbols, if any:
_symbols.release();
}
/*
/**********************************************************
/* Public API, data access
/**********************************************************
*/
@Override
public String getText() throws IOException
{
if (_currToken == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
return _finishAndReturnString(); // only strings can be incomplete
}
return _textBuffer.contentsAsString();
}
return _getText2(_currToken);
}
@Override
public int getText(Writer writer) throws IOException
{
JsonToken t = _currToken;
if (t == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
return _textBuffer.contentsToWriter(writer);
}
if (t == JsonToken.FIELD_NAME) {
String n = _parsingContext.getCurrentName();
writer.write(n);
return n.length();
}
if (t != null) {
if (t.isNumeric()) {
return _textBuffer.contentsToWriter(writer);
}
char[] ch = t.asCharArray();
writer.write(ch);
return ch.length;
}
return 0;
}
// // // Let's override default impls for improved performance
@Override
public String getValueAsString() throws IOException
{
if (_currToken == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
return _finishAndReturnString(); // only strings can be incomplete
}
return _textBuffer.contentsAsString();
}
if (_currToken == JsonToken.FIELD_NAME) {
return getCurrentName();
}
return super.getValueAsString(null);
}
@Override
public String getValueAsString(String defValue) throws IOException
{
if (_currToken == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
return _finishAndReturnString(); // only strings can be incomplete
}
return _textBuffer.contentsAsString();
}
if (_currToken == JsonToken.FIELD_NAME) {
return getCurrentName();
}
return super.getValueAsString(defValue);
}
@Override
public int getValueAsInt() throws IOException
{
JsonToken t = _currToken;
if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) {
// inlined 'getIntValue()'
if ((_numTypesValid & NR_INT) == 0) {
if (_numTypesValid == NR_UNKNOWN) {
return _parseIntValue();
}
if ((_numTypesValid & NR_INT) == 0) {
convertNumberToInt();
}
}
return _numberInt;
}
return super.getValueAsInt(0);
}
@Override
public int getValueAsInt(int defValue) throws IOException
{
JsonToken t = _currToken;
if ((t == JsonToken.VALUE_NUMBER_INT) || (t == JsonToken.VALUE_NUMBER_FLOAT)) {
// inlined 'getIntValue()'
if ((_numTypesValid & NR_INT) == 0) {
if (_numTypesValid == NR_UNKNOWN) {
return _parseIntValue();
}
if ((_numTypesValid & NR_INT) == 0) {
convertNumberToInt();
}
}
return _numberInt;
}
return super.getValueAsInt(defValue);
}
protected final String _getText2(JsonToken t)
{
if (t == null) {
return null;
}
switch (t.id()) {
case ID_FIELD_NAME:
return _parsingContext.getCurrentName();
case ID_STRING:
// fall through
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return _textBuffer.contentsAsString();
default:
return t.asString();
}
}
@Override
public char[] getTextCharacters() throws IOException
{
if (_currToken != null) { // null only before/after document
switch (_currToken.id()) {
case ID_FIELD_NAME:
if (!_nameCopied) {
String name = _parsingContext.getCurrentName();
int nameLen = name.length();
if (_nameCopyBuffer == null) {
_nameCopyBuffer = _ioContext.allocNameCopyBuffer(nameLen);
} else if (_nameCopyBuffer.length < nameLen) {
_nameCopyBuffer = new char[nameLen];
}
name.getChars(0, nameLen, _nameCopyBuffer, 0);
_nameCopied = true;
}
return _nameCopyBuffer;
case ID_STRING:
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
// fall through
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return _textBuffer.getTextBuffer();
default:
return _currToken.asCharArray();
}
}
return null;
}
@Override
public int getTextLength() throws IOException
{
if (_currToken == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
return _textBuffer.size();
}
if (_currToken == JsonToken.FIELD_NAME) {
return _parsingContext.getCurrentName().length();
}
if (_currToken != null) { // null only before/after document
if (_currToken.isNumeric()) {
return _textBuffer.size();
}
return _currToken.asCharArray().length;
}
return 0;
}
@Override
public int getTextOffset() throws IOException
{
// Most have offset of 0, only some may have other values:
if (_currToken != null) {
switch (_currToken.id()) {
case ID_FIELD_NAME:
return 0;
case ID_STRING:
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
// fall through
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return _textBuffer.getTextOffset();
default:
}
}
return 0;
}
@Override
public byte[] getBinaryValue(Base64Variant b64variant) throws IOException
{
if (_currToken != JsonToken.VALUE_STRING &&
(_currToken != JsonToken.VALUE_EMBEDDED_OBJECT || _binaryValue == null)) {
_reportError("Current token ("+_currToken+") not VALUE_STRING or VALUE_EMBEDDED_OBJECT, can not access as binary");
}
/* To ensure that we won't see inconsistent data, better clear up
* state...
*/
if (_tokenIncomplete) {
try {
_binaryValue = _decodeBase64(b64variant);
} catch (IllegalArgumentException iae) {
throw _constructError("Failed to decode VALUE_STRING as base64 ("+b64variant+"): "+iae.getMessage());
}
/* let's clear incomplete only now; allows for accessing other
* textual content in error cases
*/
_tokenIncomplete = false;
} else { // may actually require conversion...
if (_binaryValue == null) {
@SuppressWarnings("resource")
ByteArrayBuilder builder = _getByteArrayBuilder();
_decodeBase64(getText(), builder, b64variant);
_binaryValue = builder.toByteArray();
}
}
return _binaryValue;
}
@Override
public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException
{
// if we have already read the token, just use whatever we may have
if (!_tokenIncomplete || _currToken != JsonToken.VALUE_STRING) {
byte[] b = getBinaryValue(b64variant);
out.write(b);
return b.length;
}
// otherwise do "real" incremental parsing...
byte[] buf = _ioContext.allocBase64Buffer();
try {
return _readBinary(b64variant, out, buf);
} finally {
_ioContext.releaseBase64Buffer(buf);
}
}
protected int _readBinary(Base64Variant b64variant, OutputStream out,
byte[] buffer) throws IOException
{
int outputPtr = 0;
final int outputEnd = buffer.length - 3;
int outputCount = 0;
while (true) {
// first, we'll skip preceding white space, if any
int ch;
do {
ch = _inputData.readUnsignedByte();
} while (ch <= INT_SPACE);
int bits = b64variant.decodeBase64Char(ch);
if (bits < 0) { // reached the end, fair and square?
if (ch == INT_QUOTE) {
break;
}
bits = _decodeBase64Escape(b64variant, ch, 0);
if (bits < 0) { // white space to skip
continue;
}
}
// enough room? If not, flush
if (outputPtr > outputEnd) {
outputCount += outputPtr;
out.write(buffer, 0, outputPtr);
outputPtr = 0;
}
int decodedData = bits;
// then second base64 char; can't get padding yet, nor ws
ch = _inputData.readUnsignedByte();
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
bits = _decodeBase64Escape(b64variant, ch, 1);
}
decodedData = (decodedData << 6) | bits;
// third base64 char; can be padding, but not ws
ch = _inputData.readUnsignedByte();
bits = b64variant.decodeBase64Char(ch);
// First branch: can get padding (-> 1 byte)
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// could also just be 'missing' padding
if (ch == INT_QUOTE) {
decodedData >>= 4;
buffer[outputPtr++] = (byte) decodedData;
if (b64variant.usesPadding()) {
_handleBase64MissingPadding(b64variant);
}
break;
}
bits = _decodeBase64Escape(b64variant, ch, 2);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
// Ok, must get padding
ch = _inputData.readUnsignedByte();
if (!b64variant.usesPaddingChar(ch)) {
if ((ch != INT_BACKSLASH)
|| _decodeBase64Escape(b64variant, ch, 3) != Base64Variant.BASE64_VALUE_PADDING) {
throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
}
}
// Got 12 bits, only need 8, need to shift
decodedData >>= 4;
buffer[outputPtr++] = (byte) decodedData;
continue;
}
}
// Nope, 2 or 3 bytes
decodedData = (decodedData << 6) | bits;
// fourth and last base64 char; can be padding, but not ws
ch = _inputData.readUnsignedByte();
bits = b64variant.decodeBase64Char(ch);
if (bits < 0) {
if (bits != Base64Variant.BASE64_VALUE_PADDING) {
// could also just be 'missing' padding
if (ch == INT_QUOTE) {
decodedData >>= 2;
buffer[outputPtr++] = (byte) (decodedData >> 8);
buffer[outputPtr++] = (byte) decodedData;
if (b64variant.usesPadding()) {
_handleBase64MissingPadding(b64variant);
}
break;
}
bits = _decodeBase64Escape(b64variant, ch, 3);
}
if (bits == Base64Variant.BASE64_VALUE_PADDING) {
/* With padding we only get 2 bytes; but we have
* to shift it a bit so it is identical to triplet
* case with partial output.
* 3 chars gives 3x6 == 18 bits, of which 2 are
* dummies, need to discard:
*/
decodedData >>= 2;
buffer[outputPtr++] = (byte) (decodedData >> 8);
buffer[outputPtr++] = (byte) decodedData;
continue;
}
}
// otherwise, our triplet is now complete
decodedData = (decodedData << 6) | bits;
buffer[outputPtr++] = (byte) (decodedData >> 16);
buffer[outputPtr++] = (byte) (decodedData >> 8);
buffer[outputPtr++] = (byte) decodedData;
}
_tokenIncomplete = false;
if (outputPtr > 0) {
outputCount += outputPtr;
out.write(buffer, 0, outputPtr);
}
return outputCount;
}
/*
/**********************************************************
/* Public API, traversal, basic
/**********************************************************
*/
/**
* @return Next token from the stream, if any found, or null
* to indicate end-of-input
*/
@Override
public JsonToken nextToken() throws IOException
{
if (_closed) {
return null;
}
/* First: field names are special -- we will always tokenize
* (part of) value along with field name to simplify
* state handling. If so, can and need to use secondary token:
*/
if (_currToken == JsonToken.FIELD_NAME) {
return _nextAfterName();
}
// But if we didn't already have a name, and (partially?) decode number,
// need to ensure no numeric information is leaked
_numTypesValid = NR_UNKNOWN;
if (_tokenIncomplete) {
_skipString(); // only strings can be partial
}
int i = _skipWSOrEnd();
if (i < 0) { // end-of-input
// Close/release things like input source, symbol table and recyclable buffers
close();
return (_currToken = null);
}
// clear any data retained so far
_binaryValue = null;
_tokenInputRow = _currInputRow;
// Closing scope?
if (i == INT_RBRACKET || i == INT_RCURLY) {
_closeScope(i);
return _currToken;
}
// Nope: do we then expect a comma?
if (_parsingContext.expectComma()) {
if (i != INT_COMMA) {
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
i = _skipWS();
// Was that a trailing comma?
if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
if (i == INT_RBRACKET || i == INT_RCURLY) {
_closeScope(i);
return _currToken;
}
}
}
/* And should we now have a name? Always true for
* Object contexts, since the intermediate 'expect-value'
* state is never retained.
*/
if (!_parsingContext.inObject()) {
return _nextTokenNotInObject(i);
}
// So first parse the field name itself:
String n = _parseName(i);
_parsingContext.setCurrentName(n);
_currToken = JsonToken.FIELD_NAME;
i = _skipColon();
// Ok: we must have a value... what is it? Strings are very common, check first:
if (i == INT_QUOTE) {
_tokenIncomplete = true;
_nextToken = JsonToken.VALUE_STRING;
return _currToken;
}
JsonToken t;
switch (i) {
case '-':
t = _parseNegNumber();
break;
/* Should we have separate handling for plus? Although
* it is not allowed per se, it may be erroneously used,
* and could be indicate by a more specific error message.
*/
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
t = _parsePosNumber(i);
break;
case 'f':
_matchToken("false", 1);
t = JsonToken.VALUE_FALSE;
break;
case 'n':
_matchToken("null", 1);
t = JsonToken.VALUE_NULL;
break;
case 't':
_matchToken("true", 1);
t = JsonToken.VALUE_TRUE;
break;
case '[':
t = JsonToken.START_ARRAY;
break;
case '{':
t = JsonToken.START_OBJECT;
break;
default:
t = _handleUnexpectedValue(i);
}
_nextToken = t;
return _currToken;
}
private final JsonToken _nextTokenNotInObject(int i) throws IOException
{
if (i == INT_QUOTE) {
_tokenIncomplete = true;
return (_currToken = JsonToken.VALUE_STRING);
}
switch (i) {
case '[':
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
return (_currToken = JsonToken.START_ARRAY);
case '{':
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
return (_currToken = JsonToken.START_OBJECT);
case 't':
_matchToken("true", 1);
return (_currToken = JsonToken.VALUE_TRUE);
case 'f':
_matchToken("false", 1);
return (_currToken = JsonToken.VALUE_FALSE);
case 'n':
_matchToken("null", 1);
return (_currToken = JsonToken.VALUE_NULL);
case '-':
return (_currToken = _parseNegNumber());
/* Should we have separate handling for plus? Although
* it is not allowed per se, it may be erroneously used,
* and could be indicated by a more specific error message.
*/
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return (_currToken = _parsePosNumber(i));
}
return (_currToken = _handleUnexpectedValue(i));
}
private final JsonToken _nextAfterName()
{
_nameCopied = false; // need to invalidate if it was copied
JsonToken t = _nextToken;
_nextToken = null;
// Also: may need to start new context?
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return (_currToken = t);
}
@Override
public void finishToken() throws IOException {
if (_tokenIncomplete) {
_tokenIncomplete = false;
_finishString(); // only strings can be incomplete
}
}
/*
/**********************************************************
/* Public API, traversal, nextXxxValue/nextFieldName
/**********************************************************
*/
// Can not implement without look-ahead...
// public boolean nextFieldName(SerializableString str) throws IOException
@Override
public String nextFieldName() throws IOException
{
// // // Note: this is almost a verbatim copy of nextToken()
_numTypesValid = NR_UNKNOWN;
if (_currToken == JsonToken.FIELD_NAME) {
_nextAfterName();
return null;
}
if (_tokenIncomplete) {
_skipString();
}
int i = _skipWS();
_binaryValue = null;
_tokenInputRow = _currInputRow;
if (i == INT_RBRACKET || i == INT_RCURLY) {
_closeScope(i);
return null;
}
// Nope: do we then expect a comma?
if (_parsingContext.expectComma()) {
if (i != INT_COMMA) {
_reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
}
i = _skipWS();
// Was that a trailing comma?
if ((_features & FEAT_MASK_TRAILING_COMMA) != 0) {
if (i == INT_RBRACKET || i == INT_RCURLY) {
_closeScope(i);
return null;
}
}
}
if (!_parsingContext.inObject()) {
_nextTokenNotInObject(i);
return null;
}
final String nameStr = _parseName(i);
_parsingContext.setCurrentName(nameStr);
_currToken = JsonToken.FIELD_NAME;
i = _skipColon();
if (i == INT_QUOTE) {
_tokenIncomplete = true;
_nextToken = JsonToken.VALUE_STRING;
return nameStr;
}
JsonToken t;
switch (i) {
case '-':
t = _parseNegNumber();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
t = _parsePosNumber(i);
break;
case 'f':
_matchToken("false", 1);
t = JsonToken.VALUE_FALSE;
break;
case 'n':
_matchToken("null", 1);
t = JsonToken.VALUE_NULL;
break;
case 't':
_matchToken("true", 1);
t = JsonToken.VALUE_TRUE;
break;
case '[':
t = JsonToken.START_ARRAY;
break;
case '{':
t = JsonToken.START_OBJECT;
break;
default:
t = _handleUnexpectedValue(i);
}
_nextToken = t;
return nameStr;
}
@Override
public String nextTextValue() throws IOException
{
// two distinct cases; either got name and we know next type, or 'other'
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_STRING) {
if (_tokenIncomplete) {
_tokenIncomplete = false;
return _finishAndReturnString();
}
return _textBuffer.contentsAsString();
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return null;
}
return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
}
@Override
public int nextIntValue(int defaultValue) throws IOException
{
// two distinct cases; either got name and we know next type, or 'other'
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_NUMBER_INT) {
return getIntValue();
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return defaultValue;
}
return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
}
@Override
public long nextLongValue(long defaultValue) throws IOException
{
// two distinct cases; either got name and we know next type, or 'other'
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_NUMBER_INT) {
return getLongValue();
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return defaultValue;
}
return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
}
@Override
public Boolean nextBooleanValue() throws IOException
{
// two distinct cases; either got name and we know next type, or 'other'
if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
_nameCopied = false;
JsonToken t = _nextToken;
_nextToken = null;
_currToken = t;
if (t == JsonToken.VALUE_TRUE) {
return Boolean.TRUE;
}
if (t == JsonToken.VALUE_FALSE) {
return Boolean.FALSE;
}
if (t == JsonToken.START_ARRAY) {
_parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
} else if (t == JsonToken.START_OBJECT) {
_parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
}
return null;
}
JsonToken t = nextToken();
if (t == JsonToken.VALUE_TRUE) {
return Boolean.TRUE;
}
if (t == JsonToken.VALUE_FALSE) {
return Boolean.FALSE;
}
return null;
}
/*
/**********************************************************
/* Internal methods, number parsing
/**********************************************************
*/
/**
* Initial parsing method for number values. It needs to be able
* to parse enough input to be able to determine whether the
* value is to be considered a simple integer value, or a more
* generic decimal value: latter of which needs to be expressed
* as a floating point number. The basic rule is that if the number
* has no fractional or exponential part, it is an integer; otherwise
* a floating point number.
*char
s that we know will always fit
* in the output buffer after escaping
*/
protected final int _outputMaxContiguous;
/**
* Intermediate buffer in which characters of a String are copied
* before being encoded.
*/
protected char[] _charBuffer;
/**
* Length of _charBuffer
*/
protected final int _charBufferLength;
/**
* 6 character temporary buffer allocated if needed, for constructing
* escape sequences
*/
protected byte[] _entityBuffer;
/**
* Flag that indicates whether the output buffer is recycable (and
* needs to be returned to recycler once we are done) or not.
*/
protected boolean _bufferRecyclable;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
/**
* @since 2.10
*/
@SuppressWarnings("deprecation")
public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec,
OutputStream out, char quoteChar)
{
super(ctxt, features, codec);
_outputStream = out;
_quoteChar = (byte) quoteChar;
if (quoteChar != '"') { // since 2.10
_outputEscapes = CharTypes.get7BitOutputEscapes(quoteChar);
}
_bufferRecyclable = true;
_outputBuffer = ctxt.allocWriteEncodingBuffer();
_outputEnd = _outputBuffer.length;
/* To be exact, each char can take up to 6 bytes when escaped (Unicode
* escape with backslash, 'u' and 4 hex digits); but to avoid fluctuation,
* we will actually round down to only do up to 1/8 number of chars
*/
_outputMaxContiguous = _outputEnd >> 3;
_charBuffer = ctxt.allocConcatBuffer();
_charBufferLength = _charBuffer.length;
// By default we use this feature to determine additional quoting
if (isEnabled(Feature.ESCAPE_NON_ASCII)) {
setHighestNonEscapedChar(127);
}
}
/**
* @since 2.10
*/
public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec,
OutputStream out, char quoteChar,
byte[] outputBuffer, int outputOffset, boolean bufferRecyclable)
{
super(ctxt, features, codec);
_outputStream = out;
_quoteChar = (byte) quoteChar;
if (quoteChar != '"') { // since 2.10
_outputEscapes = CharTypes.get7BitOutputEscapes(quoteChar);
}
_bufferRecyclable = bufferRecyclable;
_outputTail = outputOffset;
_outputBuffer = outputBuffer;
_outputEnd = _outputBuffer.length;
// up to 6 bytes per char (see above), rounded up to 1/8
_outputMaxContiguous = (_outputEnd >> 3);
_charBuffer = ctxt.allocConcatBuffer();
_charBufferLength = _charBuffer.length;
}
@Deprecated // since 2.10
public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec,
OutputStream out) {
this(ctxt, features, codec, out, JsonFactory.DEFAULT_QUOTE_CHAR);
}
@Deprecated // since 2.10
public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec,
OutputStream out,
byte[] outputBuffer, int outputOffset, boolean bufferRecyclable)
{
this(ctxt, features, codec, out, JsonFactory.DEFAULT_QUOTE_CHAR,
outputBuffer, outputOffset, bufferRecyclable);
}
/*
/**********************************************************
/* Overridden configuration methods
/**********************************************************
*/
@Override
public Object getOutputTarget() {
return _outputStream;
}
@Override
public int getOutputBuffered() {
// Assuming tail is always valid, set to 0 on close
return _outputTail;
}
/*
/**********************************************************
/* Overridden methods
/**********************************************************
*/
@Override
public void writeFieldName(String name) throws IOException
{
if (_cfgPrettyPrinter != null) {
_writePPFieldName(name);
return;
}
final int status = _writeContext.writeFieldName(name);
if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) { // need comma
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_COMMA;
}
/* To support [JACKSON-46], we'll do this:
* (Question: should quoting of spaces (etc) still be enabled?)
*/
if (_cfgUnqNames) {
_writeStringSegments(name, false);
return;
}
final int len = name.length();
// Does it fit in buffer?
if (len > _charBufferLength) { // no, offline
_writeStringSegments(name, true);
return;
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
// But as one segment, or multiple?
if (len <= _outputMaxContiguous) {
if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
_flushBuffer();
}
_writeStringSegment(name, 0, len);
} else {
_writeStringSegments(name, 0, len);
}
// and closing quotes; need room for one more char:
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public void writeFieldName(SerializableString name) throws IOException
{
if (_cfgPrettyPrinter != null) {
_writePPFieldName(name);
return;
}
final int status = _writeContext.writeFieldName(name.getValue());
if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_COMMA;
}
if (_cfgUnqNames) {
_writeUnq(name);
return;
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
int len = name.appendQuotedUTF8(_outputBuffer, _outputTail);
if (len < 0) { // couldn't append, bit longer processing
_writeBytes(name.asQuotedUTF8());
} else {
_outputTail += len;
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
private final void _writeUnq(SerializableString name) throws IOException {
int len = name.appendQuotedUTF8(_outputBuffer, _outputTail);
if (len < 0) {
_writeBytes(name.asQuotedUTF8());
} else {
_outputTail += len;
}
}
/*
/**********************************************************
/* Output method implementations, structural
/**********************************************************
*/
@Override
public final void writeStartArray() throws IOException
{
_verifyValueWrite("start an array");
_writeContext = _writeContext.createChildArrayContext();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeStartArray(this);
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_LBRACKET;
}
}
@Override // since 2.10
public void writeStartArray(int size) throws IOException
{
_verifyValueWrite("start an array");
_writeContext = _writeContext.createChildArrayContext();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeStartArray(this);
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_LBRACKET;
}
}
@Override
public final void writeEndArray() throws IOException
{
if (!_writeContext.inArray()) {
_reportError("Current context not Array but "+_writeContext.typeDesc());
}
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount());
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_RBRACKET;
}
_writeContext = _writeContext.clearAndGetParent();
}
@Override
public final void writeStartObject() throws IOException
{
_verifyValueWrite("start an object");
_writeContext = _writeContext.createChildObjectContext();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeStartObject(this);
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_LCURLY;
}
}
@Override // since 2.8
public void writeStartObject(Object forValue) throws IOException
{
_verifyValueWrite("start an object");
JsonWriteContext ctxt = _writeContext.createChildObjectContext(forValue);
_writeContext = ctxt;
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeStartObject(this);
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = '{';
}
}
@Override
public final void writeEndObject() throws IOException
{
if (!_writeContext.inObject()) {
_reportError("Current context not Object but "+_writeContext.typeDesc());
}
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount());
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_RCURLY;
}
_writeContext = _writeContext.clearAndGetParent();
}
/**
* Specialized version of _writeFieldName
, off-lined
* to keep the "fast path" as simple (and hopefully fast) as possible.
*/
protected final void _writePPFieldName(String name) throws IOException
{
int status = _writeContext.writeFieldName(name);
if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
if ((status == JsonWriteContext.STATUS_OK_AFTER_COMMA)) {
_cfgPrettyPrinter.writeObjectEntrySeparator(this);
} else {
_cfgPrettyPrinter.beforeObjectEntries(this);
}
if (_cfgUnqNames) {
_writeStringSegments(name, false);
return;
}
final int len = name.length();
if (len > _charBufferLength) {
_writeStringSegments(name, true);
return;
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
name.getChars(0, len, _charBuffer, 0);
// But as one segment, or multiple?
if (len <= _outputMaxContiguous) {
if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
_flushBuffer();
}
_writeStringSegment(_charBuffer, 0, len);
} else {
_writeStringSegments(_charBuffer, 0, len);
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
protected final void _writePPFieldName(SerializableString name) throws IOException
{
final int status = _writeContext.writeFieldName(name.getValue());
if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
_reportError("Can not write a field name, expecting a value");
}
if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) {
_cfgPrettyPrinter.writeObjectEntrySeparator(this);
} else {
_cfgPrettyPrinter.beforeObjectEntries(this);
}
final boolean addQuotes = !_cfgUnqNames; // standard
if (addQuotes) {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
int len = name.appendQuotedUTF8(_outputBuffer, _outputTail);
if (len < 0) {
_writeBytes(name.asQuotedUTF8());
} else {
_outputTail += len;
}
if (addQuotes) {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
}
/*
/**********************************************************
/* Output method implementations, textual
/**********************************************************
*/
@Override
public void writeString(String text) throws IOException
{
_verifyValueWrite(WRITE_STRING);
if (text == null) {
_writeNull();
return;
}
// First: if we can't guarantee it all fits, quoted, within output, offline
final int len = text.length();
if (len > _outputMaxContiguous) { // nope: off-line handling
_writeStringSegments(text, true);
return;
}
if ((_outputTail + len) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
_writeStringSegment(text, 0, len); // we checked space already above
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public void writeString(Reader reader, int len) throws IOException {
_verifyValueWrite(WRITE_STRING);
if (reader == null) {
_reportError("null reader");
}
int toRead = (len >= 0) ? len : Integer.MAX_VALUE;
final char[] buf = _charBuffer;
// Add leading quote
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
// read
while (toRead > 0){
int toReadNow = Math.min(toRead, buf.length);
int numRead = reader.read(buf, 0, toReadNow);
if(numRead <= 0){
break;
}
if ((_outputTail + len) >= _outputEnd) {
_flushBuffer();
}
_writeStringSegments(buf, 0, numRead);
//decrease tracker
toRead -= numRead;
}
// Add trailing quote
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
if (toRead > 0 && len >= 0){
_reportError("Didn't read enough from reader");
}
}
@Override
public void writeString(char[] text, int offset, int len) throws IOException
{
_verifyValueWrite(WRITE_STRING);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
// One or multiple segments?
if (len <= _outputMaxContiguous) {
if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
_flushBuffer();
}
_writeStringSegment(text, offset, len);
} else {
_writeStringSegments(text, offset, len);
}
// And finally, closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public final void writeString(SerializableString text) throws IOException
{
_verifyValueWrite(WRITE_STRING);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
int len = text.appendQuotedUTF8(_outputBuffer, _outputTail);
if (len < 0) {
_writeBytes(text.asQuotedUTF8());
} else {
_outputTail += len;
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException
{
_verifyValueWrite(WRITE_STRING);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
_writeBytes(text, offset, length);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public void writeUTF8String(byte[] text, int offset, int len) throws IOException
{
_verifyValueWrite(WRITE_STRING);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
// One or multiple segments?
if (len <= _outputMaxContiguous) {
_writeUTF8Segment(text, offset, len);
} else {
_writeUTF8Segments(text, offset, len);
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
/*
/**********************************************************
/* Output method implementations, unprocessed ("raw")
/**********************************************************
*/
@Override
public void writeRaw(String text) throws IOException {
final int len = text.length();
final char[] buf = _charBuffer;
if (len <= buf.length) {
text.getChars(0, len, buf, 0);
writeRaw(buf, 0, len);
} else {
writeRaw(text, 0, len);
}
}
@Override
public void writeRaw(String text, int offset, int len) throws IOException
{
final char[] buf = _charBuffer;
final int cbufLen = buf.length;
// minor optimization: see if we can just get and copy
if (len <= cbufLen) {
text.getChars(offset, offset+len, buf, 0);
writeRaw(buf, 0, len);
return;
}
// If not, need segmented approach. For speed, let's also use input buffer
// size that is guaranteed to fit in output buffer; each char can expand to
// at most 3 bytes, so at most 1/3 of buffer size.
final int maxChunk = Math.min(cbufLen,
(_outputEnd >> 2) + (_outputEnd >> 4)); // == (1/4 + 1/16) == 5/16
final int maxBytes = maxChunk * 3;
while (len > 0) {
int len2 = Math.min(maxChunk, len);
text.getChars(offset, offset+len2, buf, 0);
if ((_outputTail + maxBytes) > _outputEnd) {
_flushBuffer();
}
// If this is NOT the last segment and if the last character looks like
// split surrogate second half, drop it
// 21-Mar-2017, tatu: Note that we could check for either `len` or `len2`;
// point here is really that we only "punt" surrogate if it is NOT the
// only character left; otherwise we'd end up with a poison pill if the
// very last character was unpaired first-surrogate
if (len2 > 1) {
char ch = buf[len2-1];
if ((ch >= SURR1_FIRST) && (ch <= SURR1_LAST)) {
--len2;
}
}
_writeRawSegment(buf, 0, len2);
offset += len2;
len -= len2;
}
}
@Override
public void writeRaw(SerializableString text) throws IOException
{
int len = text.appendUnquotedUTF8(_outputBuffer, _outputTail);
if (len < 0) {
_writeBytes(text.asUnquotedUTF8());
} else {
_outputTail += len;
}
}
// since 2.5
@Override
public void writeRawValue(SerializableString text) throws IOException {
_verifyValueWrite(WRITE_RAW);
int len = text.appendUnquotedUTF8(_outputBuffer, _outputTail);
if (len < 0) {
_writeBytes(text.asUnquotedUTF8());
} else {
_outputTail += len;
}
}
// @TODO: rewrite for speed...
@Override
public final void writeRaw(char[] cbuf, int offset, int len) throws IOException
{
// First: if we have 3 x charCount spaces, we know it'll fit just fine
{
int len3 = len+len+len;
if ((_outputTail + len3) > _outputEnd) {
// maybe we could flush?
if (_outputEnd < len3) { // wouldn't be enough...
_writeSegmentedRaw(cbuf, offset, len);
return;
}
// yes, flushing brings enough space
_flushBuffer();
}
}
len += offset; // now marks the end
// Note: here we know there is enough room, hence no output boundary checks
main_loop:
while (offset < len) {
inner_loop:
while (true) {
int ch = (int) cbuf[offset];
if (ch > 0x7F) {
break inner_loop;
}
_outputBuffer[_outputTail++] = (byte) ch;
if (++offset >= len) {
break main_loop;
}
}
char ch = cbuf[offset++];
if (ch < 0x800) { // 2-byte?
_outputBuffer[_outputTail++] = (byte) (0xc0 | (ch >> 6));
_outputBuffer[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
} else {
offset = _outputRawMultiByteChar(ch, cbuf, offset, len);
}
}
}
@Override
public void writeRaw(char ch) throws IOException
{
if ((_outputTail + 3) >= _outputEnd) {
_flushBuffer();
}
final byte[] bbuf = _outputBuffer;
if (ch <= 0x7F) {
bbuf[_outputTail++] = (byte) ch;
} else if (ch < 0x800) { // 2-byte?
bbuf[_outputTail++] = (byte) (0xc0 | (ch >> 6));
bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
} else {
/*offset =*/ _outputRawMultiByteChar(ch, null, 0, 0);
}
}
/**
* Helper method called when it is possible that output of raw section
* to output may cross buffer boundary
*/
private final void _writeSegmentedRaw(char[] cbuf, int offset, int len) throws IOException
{
final int end = _outputEnd;
final byte[] bbuf = _outputBuffer;
final int inputEnd = offset + len;
main_loop:
while (offset < inputEnd) {
inner_loop:
while (true) {
int ch = (int) cbuf[offset];
if (ch >= 0x80) {
break inner_loop;
}
// !!! TODO: fast(er) writes (roll input, output checks in one)
if (_outputTail >= end) {
_flushBuffer();
}
bbuf[_outputTail++] = (byte) ch;
if (++offset >= inputEnd) {
break main_loop;
}
}
if ((_outputTail + 3) >= _outputEnd) {
_flushBuffer();
}
char ch = cbuf[offset++];
if (ch < 0x800) { // 2-byte?
bbuf[_outputTail++] = (byte) (0xc0 | (ch >> 6));
bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
} else {
offset = _outputRawMultiByteChar(ch, cbuf, offset, inputEnd);
}
}
}
/**
* Helper method that is called for segmented write of raw content
* when explicitly outputting a segment of longer thing.
* Caller has to take care of ensuring there's no split surrogate
* pair at the end (that is, last char can not be first part of a
* surrogate char pair).
*
* @since 2.8.2
*/
private void _writeRawSegment(char[] cbuf, int offset, int end) throws IOException
{
main_loop:
while (offset < end) {
inner_loop:
while (true) {
int ch = (int) cbuf[offset];
if (ch > 0x7F) {
break inner_loop;
}
_outputBuffer[_outputTail++] = (byte) ch;
if (++offset >= end) {
break main_loop;
}
}
char ch = cbuf[offset++];
if (ch < 0x800) { // 2-byte?
_outputBuffer[_outputTail++] = (byte) (0xc0 | (ch >> 6));
_outputBuffer[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
} else {
offset = _outputRawMultiByteChar(ch, cbuf, offset, end);
}
}
}
/*
/**********************************************************
/* Output method implementations, base64-encoded binary
/**********************************************************
*/
@Override
public void writeBinary(Base64Variant b64variant,
byte[] data, int offset, int len)
throws IOException, JsonGenerationException
{
_verifyValueWrite(WRITE_BINARY);
// Starting quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
_writeBinary(b64variant, data, offset, offset+len);
// and closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public int writeBinary(Base64Variant b64variant,
InputStream data, int dataLength)
throws IOException, JsonGenerationException
{
_verifyValueWrite(WRITE_BINARY);
// Starting quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
byte[] encodingBuffer = _ioContext.allocBase64Buffer();
int bytes;
try {
if (dataLength < 0) { // length unknown
bytes = _writeBinary(b64variant, data, encodingBuffer);
} else {
int missing = _writeBinary(b64variant, data, encodingBuffer, dataLength);
if (missing > 0) {
_reportError("Too few bytes available: missing "+missing+" bytes (out of "+dataLength+")");
}
bytes = dataLength;
}
} finally {
_ioContext.releaseBase64Buffer(encodingBuffer);
}
// and closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
return bytes;
}
/*
/**********************************************************
/* Output method implementations, primitive
/**********************************************************
*/
@Override
public void writeNumber(short s) throws IOException
{
_verifyValueWrite(WRITE_NUMBER);
// up to 5 digits and possible minus sign
if ((_outputTail + 6) >= _outputEnd) {
_flushBuffer();
}
if (_cfgNumbersAsStrings) {
_writeQuotedShort(s);
return;
}
_outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail);
}
private final void _writeQuotedShort(short s) throws IOException {
if ((_outputTail + 8) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
_outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail);
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public void writeNumber(int i) throws IOException
{
_verifyValueWrite(WRITE_NUMBER);
// up to 10 digits and possible minus sign
if ((_outputTail + 11) >= _outputEnd) {
_flushBuffer();
}
if (_cfgNumbersAsStrings) {
_writeQuotedInt(i);
return;
}
_outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
}
private final void _writeQuotedInt(int i) throws IOException
{
if ((_outputTail + 13) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
_outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public void writeNumber(long l) throws IOException
{
_verifyValueWrite(WRITE_NUMBER);
if (_cfgNumbersAsStrings) {
_writeQuotedLong(l);
return;
}
if ((_outputTail + 21) >= _outputEnd) {
// up to 20 digits, minus sign
_flushBuffer();
}
_outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
}
private final void _writeQuotedLong(long l) throws IOException
{
if ((_outputTail + 23) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
_outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public void writeNumber(BigInteger value) throws IOException
{
_verifyValueWrite(WRITE_NUMBER);
if (value == null) {
_writeNull();
} else if (_cfgNumbersAsStrings) {
_writeQuotedRaw(value.toString());
} else {
writeRaw(value.toString());
}
}
@SuppressWarnings("deprecation")
@Override
public void writeNumber(double d) throws IOException
{
if (_cfgNumbersAsStrings ||
(NumberOutput.notFinite(d)
&& Feature.QUOTE_NON_NUMERIC_NUMBERS.enabledIn(_features))) {
writeString(String.valueOf(d));
return;
}
// What is the max length for doubles? 40 chars?
_verifyValueWrite(WRITE_NUMBER);
writeRaw(String.valueOf(d));
}
@SuppressWarnings("deprecation")
@Override
public void writeNumber(float f) throws IOException
{
if (_cfgNumbersAsStrings ||
(NumberOutput.notFinite(f)
&& Feature.QUOTE_NON_NUMERIC_NUMBERS.enabledIn(_features))) {
writeString(String.valueOf(f));
return;
}
// What is the max length for floats?
_verifyValueWrite(WRITE_NUMBER);
writeRaw(String.valueOf(f));
}
@Override
public void writeNumber(BigDecimal value) throws IOException
{
// Don't really know max length for big decimal, no point checking
_verifyValueWrite(WRITE_NUMBER);
if (value == null) {
_writeNull();
} else if (_cfgNumbersAsStrings) {
_writeQuotedRaw(_asString(value));
} else {
writeRaw(_asString(value));
}
}
@Override
public void writeNumber(String encodedValue) throws IOException
{
_verifyValueWrite(WRITE_NUMBER);
if (_cfgNumbersAsStrings) {
_writeQuotedRaw(encodedValue);
} else {
writeRaw(encodedValue);
}
}
private final void _writeQuotedRaw(String value) throws IOException
{
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
writeRaw(value);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public void writeBoolean(boolean state) throws IOException
{
_verifyValueWrite(WRITE_BOOLEAN);
if ((_outputTail + 5) >= _outputEnd) {
_flushBuffer();
}
byte[] keyword = state ? TRUE_BYTES : FALSE_BYTES;
int len = keyword.length;
System.arraycopy(keyword, 0, _outputBuffer, _outputTail, len);
_outputTail += len;
}
@Override
public void writeNull() throws IOException
{
_verifyValueWrite(WRITE_NULL);
_writeNull();
}
/*
/**********************************************************
/* Implementations for other methods
/**********************************************************
*/
@Override
protected final void _verifyValueWrite(String typeMsg) throws IOException
{
final int status = _writeContext.writeValue();
if (_cfgPrettyPrinter != null) {
// Otherwise, pretty printer knows what to do...
_verifyPrettyValueWrite(typeMsg, status);
return;
}
byte b;
switch (status) {
case JsonWriteContext.STATUS_OK_AS_IS:
default:
return;
case JsonWriteContext.STATUS_OK_AFTER_COMMA:
b = BYTE_COMMA;
break;
case JsonWriteContext.STATUS_OK_AFTER_COLON:
b = BYTE_COLON;
break;
case JsonWriteContext.STATUS_OK_AFTER_SPACE: // root-value separator
if (_rootValueSeparator != null) {
byte[] raw = _rootValueSeparator.asUnquotedUTF8();
if (raw.length > 0) {
_writeBytes(raw);
}
}
return;
case JsonWriteContext.STATUS_EXPECT_NAME:
_reportCantWriteValueExpectName(typeMsg);
return;
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = b;
}
/*
/**********************************************************
/* Low-level output handling
/**********************************************************
*/
@Override
public void flush() throws IOException
{
_flushBuffer();
if (_outputStream != null) {
if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
_outputStream.flush();
}
}
}
@Override
public void close() throws IOException
{
super.close();
/* 05-Dec-2008, tatu: To add [JACKSON-27], need to close open
* scopes.
*/
// First: let's see that we still have buffers...
if ((_outputBuffer != null)
&& isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
while (true) {
JsonStreamContext ctxt = getOutputContext();
if (ctxt.inArray()) {
writeEndArray();
} else if (ctxt.inObject()) {
writeEndObject();
} else {
break;
}
}
}
_flushBuffer();
_outputTail = 0; // just to ensure we don't think there's anything buffered
/* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
* on the underlying Reader, unless we "own" it, or auto-closing
* feature is enabled.
* One downside: when using UTF8Writer, underlying buffer(s)
* may not be properly recycled if we don't close the writer.
*/
if (_outputStream != null) {
if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) {
_outputStream.close();
} else if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
// If we can't close it, we should at least flush
_outputStream.flush();
}
}
// Internal buffer(s) generator has can now be released as well
_releaseBuffers();
}
@Override
protected void _releaseBuffers()
{
byte[] buf = _outputBuffer;
if (buf != null && _bufferRecyclable) {
_outputBuffer = null;
_ioContext.releaseWriteEncodingBuffer(buf);
}
char[] cbuf = _charBuffer;
if (cbuf != null) {
_charBuffer = null;
_ioContext.releaseConcatBuffer(cbuf);
}
}
/*
/**********************************************************
/* Internal methods, low-level writing, raw bytes
/**********************************************************
*/
private final void _writeBytes(byte[] bytes) throws IOException
{
final int len = bytes.length;
if ((_outputTail + len) > _outputEnd) {
_flushBuffer();
// still not enough?
if (len > MAX_BYTES_TO_BUFFER) {
_outputStream.write(bytes, 0, len);
return;
}
}
System.arraycopy(bytes, 0, _outputBuffer, _outputTail, len);
_outputTail += len;
}
private final void _writeBytes(byte[] bytes, int offset, int len) throws IOException
{
if ((_outputTail + len) > _outputEnd) {
_flushBuffer();
// still not enough?
if (len > MAX_BYTES_TO_BUFFER) {
_outputStream.write(bytes, offset, len);
return;
}
}
System.arraycopy(bytes, offset, _outputBuffer, _outputTail, len);
_outputTail += len;
}
/*
/**********************************************************
/* Internal methods, mid-level writing, String segments
/**********************************************************
*/
/**
* Method called when String to write is long enough not to fit
* completely in temporary copy buffer. If so, we will actually
* copy it in small enough chunks so it can be directly fed
* to single-segment writes (instead of maximum slices that
* would fit in copy buffer)
*/
private final void _writeStringSegments(String text, boolean addQuotes) throws IOException
{
if (addQuotes) {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
int left = text.length();
int offset = 0;
while (left > 0) {
int len = Math.min(_outputMaxContiguous, left);
if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
_flushBuffer();
}
_writeStringSegment(text, offset, len);
offset += len;
left -= len;
}
if (addQuotes) {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
}
/**
* Method called when character sequence to write is long enough that
* its maximum encoded and escaped form is not guaranteed to fit in
* the output buffer. If so, we will need to choose smaller output
* chunks to write at a time.
*/
private final void _writeStringSegments(char[] cbuf, int offset, int totalLen) throws IOException
{
do {
int len = Math.min(_outputMaxContiguous, totalLen);
if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
_flushBuffer();
}
_writeStringSegment(cbuf, offset, len);
offset += len;
totalLen -= len;
} while (totalLen > 0);
}
private final void _writeStringSegments(String text, int offset, int totalLen) throws IOException
{
do {
int len = Math.min(_outputMaxContiguous, totalLen);
if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
_flushBuffer();
}
_writeStringSegment(text, offset, len);
offset += len;
totalLen -= len;
} while (totalLen > 0);
}
/*
/**********************************************************
/* Internal methods, low-level writing, text segments
/**********************************************************
*/
/**
* This method called when the string content is already in
* a char buffer, and its maximum total encoded and escaped length
* can not exceed size of the output buffer.
* Caller must ensure that there is enough space in output buffer,
* assuming case of all non-escaped ASCII characters, as well as
* potentially enough space for other cases (but not necessarily flushed)
*/
private final void _writeStringSegment(char[] cbuf, int offset, int len)
throws IOException
{
// note: caller MUST ensure (via flushing) there's room for ASCII only
// Fast+tight loop for ASCII-only, no-escaping-needed output
len += offset; // becomes end marker, then
int outputPtr = _outputTail;
final byte[] outputBuffer = _outputBuffer;
final int[] escCodes = _outputEscapes;
while (offset < len) {
int ch = cbuf[offset];
// note: here we know that (ch > 0x7F) will cover case of escaping non-ASCII too:
if (ch > 0x7F || escCodes[ch] != 0) {
break;
}
outputBuffer[outputPtr++] = (byte) ch;
++offset;
}
_outputTail = outputPtr;
if (offset < len) {
if (_characterEscapes != null) {
_writeCustomStringSegment2(cbuf, offset, len);
} else if (_maximumNonEscapedChar == 0) {
_writeStringSegment2(cbuf, offset, len);
} else {
_writeStringSegmentASCII2(cbuf, offset, len);
}
}
}
private final void _writeStringSegment(String text, int offset, int len) throws IOException
{
// note: caller MUST ensure (via flushing) there's room for ASCII only
// Fast+tight loop for ASCII-only, no-escaping-needed output
len += offset; // becomes end marker, then
int outputPtr = _outputTail;
final byte[] outputBuffer = _outputBuffer;
final int[] escCodes = _outputEscapes;
while (offset < len) {
int ch = text.charAt(offset);
// note: here we know that (ch > 0x7F) will cover case of escaping non-ASCII too:
if (ch > 0x7F || escCodes[ch] != 0) {
break;
}
outputBuffer[outputPtr++] = (byte) ch;
++offset;
}
_outputTail = outputPtr;
if (offset < len) {
if (_characterEscapes != null) {
_writeCustomStringSegment2(text, offset, len);
} else if (_maximumNonEscapedChar == 0) {
_writeStringSegment2(text, offset, len);
} else {
_writeStringSegmentASCII2(text, offset, len);
}
}
}
/**
* Secondary method called when content contains characters to escape,
* and/or multi-byte UTF-8 characters.
*/
private final void _writeStringSegment2(final char[] cbuf, int offset, final int end) throws IOException
{
// Ok: caller guarantees buffer can have room; but that may require flushing:
if ((_outputTail + 6 * (end - offset)) > _outputEnd) {
_flushBuffer();
}
int outputPtr = _outputTail;
final byte[] outputBuffer = _outputBuffer;
final int[] escCodes = _outputEscapes;
while (offset < end) {
int ch = cbuf[offset++];
if (ch <= 0x7F) {
if (escCodes[ch] == 0) {
outputBuffer[outputPtr++] = (byte) ch;
continue;
}
int escape = escCodes[ch];
if (escape > 0) { // 2-char escape, fine
outputBuffer[outputPtr++] = BYTE_BACKSLASH;
outputBuffer[outputPtr++] = (byte) escape;
} else {
// ctrl-char, 6-byte escape...
outputPtr = _writeGenericEscape(ch, outputPtr);
}
continue;
}
if (ch <= 0x7FF) { // fine, just needs 2 byte output
outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
} else {
outputPtr = _outputMultiByteChar(ch, outputPtr);
}
}
_outputTail = outputPtr;
}
private final void _writeStringSegment2(final String text, int offset, final int end) throws IOException
{
if ((_outputTail + 6 * (end - offset)) > _outputEnd) {
_flushBuffer();
}
int outputPtr = _outputTail;
final byte[] outputBuffer = _outputBuffer;
final int[] escCodes = _outputEscapes;
while (offset < end) {
int ch = text.charAt(offset++);
if (ch <= 0x7F) {
if (escCodes[ch] == 0) {
outputBuffer[outputPtr++] = (byte) ch;
continue;
}
int escape = escCodes[ch];
if (escape > 0) { // 2-char escape, fine
outputBuffer[outputPtr++] = BYTE_BACKSLASH;
outputBuffer[outputPtr++] = (byte) escape;
} else {
// ctrl-char, 6-byte escape...
outputPtr = _writeGenericEscape(ch, outputPtr);
}
continue;
}
if (ch <= 0x7FF) { // fine, just needs 2 byte output
outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
} else {
outputPtr = _outputMultiByteChar(ch, outputPtr);
}
}
_outputTail = outputPtr;
}
/*
/**********************************************************
/* Internal methods, low-level writing, text segment
/* with additional escaping (ASCII or such)
/**********************************************************
*/
/**
* Same as _writeStringSegment2(char[], ...) _outputEnd) {
_flushBuffer();
}
int outputPtr = _outputTail;
final byte[] outputBuffer = _outputBuffer;
final int[] escCodes = _outputEscapes;
final int maxUnescaped = _maximumNonEscapedChar;
while (offset < end) {
int ch = cbuf[offset++];
if (ch <= 0x7F) {
if (escCodes[ch] == 0) {
outputBuffer[outputPtr++] = (byte) ch;
continue;
}
int escape = escCodes[ch];
if (escape > 0) { // 2-char escape, fine
outputBuffer[outputPtr++] = BYTE_BACKSLASH;
outputBuffer[outputPtr++] = (byte) escape;
} else {
// ctrl-char, 6-byte escape...
outputPtr = _writeGenericEscape(ch, outputPtr);
}
continue;
}
if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
outputPtr = _writeGenericEscape(ch, outputPtr);
continue;
}
if (ch <= 0x7FF) { // fine, just needs 2 byte output
outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
} else {
outputPtr = _outputMultiByteChar(ch, outputPtr);
}
}
_outputTail = outputPtr;
}
private final void _writeStringSegmentASCII2(final String text, int offset, final int end) throws IOException
{
// Ok: caller guarantees buffer can have room; but that may require flushing:
if ((_outputTail + 6 * (end - offset)) > _outputEnd) {
_flushBuffer();
}
int outputPtr = _outputTail;
final byte[] outputBuffer = _outputBuffer;
final int[] escCodes = _outputEscapes;
final int maxUnescaped = _maximumNonEscapedChar;
while (offset < end) {
int ch = text.charAt(offset++);
if (ch <= 0x7F) {
if (escCodes[ch] == 0) {
outputBuffer[outputPtr++] = (byte) ch;
continue;
}
int escape = escCodes[ch];
if (escape > 0) { // 2-char escape, fine
outputBuffer[outputPtr++] = BYTE_BACKSLASH;
outputBuffer[outputPtr++] = (byte) escape;
} else {
// ctrl-char, 6-byte escape...
outputPtr = _writeGenericEscape(ch, outputPtr);
}
continue;
}
if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
outputPtr = _writeGenericEscape(ch, outputPtr);
continue;
}
if (ch <= 0x7FF) { // fine, just needs 2 byte output
outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
} else {
outputPtr = _outputMultiByteChar(ch, outputPtr);
}
}
_outputTail = outputPtr;
}
/*
/**********************************************************
/* Internal methods, low-level writing, text segment
/* with fully custom escaping (and possibly escaping of non-ASCII
/**********************************************************
*/
/**
* Same as
_writeStringSegmentASCII2(char[], ...) _outputEnd) {
_flushBuffer();
}
int outputPtr = _outputTail;
final byte[] outputBuffer = _outputBuffer;
final int[] escCodes = _outputEscapes;
// may or may not have this limit
final int maxUnescaped = (_maximumNonEscapedChar <= 0) ? 0xFFFF : _maximumNonEscapedChar;
final CharacterEscapes customEscapes = _characterEscapes; // non-null
while (offset < end) {
int ch = cbuf[offset++];
if (ch <= 0x7F) {
if (escCodes[ch] == 0) {
outputBuffer[outputPtr++] = (byte) ch;
continue;
}
int escape = escCodes[ch];
if (escape > 0) { // 2-char escape, fine
outputBuffer[outputPtr++] = BYTE_BACKSLASH;
outputBuffer[outputPtr++] = (byte) escape;
} else if (escape == CharacterEscapes.ESCAPE_CUSTOM) {
SerializableString esc = customEscapes.getEscapeSequence(ch);
if (esc == null) {
_reportError("Invalid custom escape definitions; custom escape not found for character code 0x"
+Integer.toHexString(ch)+", although was supposed to have one");
}
outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
} else {
// ctrl-char, 6-byte escape...
outputPtr = _writeGenericEscape(ch, outputPtr);
}
continue;
}
if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
outputPtr = _writeGenericEscape(ch, outputPtr);
continue;
}
SerializableString esc = customEscapes.getEscapeSequence(ch);
if (esc != null) {
outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
continue;
}
if (ch <= 0x7FF) { // fine, just needs 2 byte output
outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
} else {
outputPtr = _outputMultiByteChar(ch, outputPtr);
}
}
_outputTail = outputPtr;
}
private final void _writeCustomStringSegment2(final String text, int offset, final int end) throws IOException
{
// Ok: caller guarantees buffer can have room; but that may require flushing:
if ((_outputTail + 6 * (end - offset)) > _outputEnd) {
_flushBuffer();
}
int outputPtr = _outputTail;
final byte[] outputBuffer = _outputBuffer;
final int[] escCodes = _outputEscapes;
// may or may not have this limit
final int maxUnescaped = (_maximumNonEscapedChar <= 0) ? 0xFFFF : _maximumNonEscapedChar;
final CharacterEscapes customEscapes = _characterEscapes; // non-null
while (offset < end) {
int ch = text.charAt(offset++);
if (ch <= 0x7F) {
if (escCodes[ch] == 0) {
outputBuffer[outputPtr++] = (byte) ch;
continue;
}
int escape = escCodes[ch];
if (escape > 0) { // 2-char escape, fine
outputBuffer[outputPtr++] = BYTE_BACKSLASH;
outputBuffer[outputPtr++] = (byte) escape;
} else if (escape == CharacterEscapes.ESCAPE_CUSTOM) {
SerializableString esc = customEscapes.getEscapeSequence(ch);
if (esc == null) {
_reportError("Invalid custom escape definitions; custom escape not found for character code 0x"
+Integer.toHexString(ch)+", although was supposed to have one");
}
outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
} else {
// ctrl-char, 6-byte escape...
outputPtr = _writeGenericEscape(ch, outputPtr);
}
continue;
}
if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
outputPtr = _writeGenericEscape(ch, outputPtr);
continue;
}
SerializableString esc = customEscapes.getEscapeSequence(ch);
if (esc != null) {
outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
continue;
}
if (ch <= 0x7FF) { // fine, just needs 2 byte output
outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
} else {
outputPtr = _outputMultiByteChar(ch, outputPtr);
}
}
_outputTail = outputPtr;
}
private final int _writeCustomEscape(byte[] outputBuffer, int outputPtr, SerializableString esc, int remainingChars)
throws IOException, JsonGenerationException
{
byte[] raw = esc.asUnquotedUTF8(); // must be escaped at this point, shouldn't double-quote
int len = raw.length;
if (len > 6) { // may violate constraints we have, do offline
return _handleLongCustomEscape(outputBuffer, outputPtr, _outputEnd, raw, remainingChars);
}
// otherwise will fit without issues, so:
System.arraycopy(raw, 0, outputBuffer, outputPtr, len);
return (outputPtr + len);
}
private final int _handleLongCustomEscape(byte[] outputBuffer, int outputPtr, int outputEnd, byte[] raw,
int remainingChars)
throws IOException, JsonGenerationException
{
int len = raw.length;
if ((outputPtr + len) > outputEnd) {
_outputTail = outputPtr;
_flushBuffer();
outputPtr = _outputTail;
if (len > outputBuffer.length) { // very unlikely, but possible...
_outputStream.write(raw, 0, len);
return outputPtr;
}
System.arraycopy(raw, 0, outputBuffer, outputPtr, len);
outputPtr += len;
}
// but is the invariant still obeyed? If not, flush once more
if ((outputPtr + 6 * remainingChars) > outputEnd) {
_flushBuffer();
return _outputTail;
}
return outputPtr;
}
/*
/**********************************************************
/* Internal methods, low-level writing, "raw UTF-8" segments
/**********************************************************
*/
/**
* Method called when UTF-8 encoded (but NOT yet escaped!) content is not guaranteed
* to fit in the output buffer after escaping; as such, we just need to
* chunk writes.
*/
private final void _writeUTF8Segments(byte[] utf8, int offset, int totalLen)
throws IOException, JsonGenerationException
{
do {
int len = Math.min(_outputMaxContiguous, totalLen);
_writeUTF8Segment(utf8, offset, len);
offset += len;
totalLen -= len;
} while (totalLen > 0);
}
private final void _writeUTF8Segment(byte[] utf8, final int offset, final int len)
throws IOException, JsonGenerationException
{
// fast loop to see if escaping is needed; don't copy, just look
final int[] escCodes = _outputEscapes;
for (int ptr = offset, end = offset + len; ptr < end; ) {
// 28-Feb-2011, tatu: escape codes just cover 7-bit range, so:
int ch = utf8[ptr++];
if ((ch >= 0) && escCodes[ch] != 0) {
_writeUTF8Segment2(utf8, offset, len);
return;
}
}
// yes, fine, just copy the sucker
if ((_outputTail + len) > _outputEnd) { // enough room or need to flush?
_flushBuffer(); // but yes once we flush (caller guarantees length restriction)
}
System.arraycopy(utf8, offset, _outputBuffer, _outputTail, len);
_outputTail += len;
}
private final void _writeUTF8Segment2(final byte[] utf8, int offset, int len)
throws IOException, JsonGenerationException
{
int outputPtr = _outputTail;
// Ok: caller guarantees buffer can have room; but that may require flushing:
if ((outputPtr + (len * 6)) > _outputEnd) {
_flushBuffer();
outputPtr = _outputTail;
}
final byte[] outputBuffer = _outputBuffer;
final int[] escCodes = _outputEscapes;
len += offset; // so 'len' becomes 'end'
while (offset < len) {
byte b = utf8[offset++];
int ch = b;
if (ch < 0 || escCodes[ch] == 0) {
outputBuffer[outputPtr++] = b;
continue;
}
int escape = escCodes[ch];
if (escape > 0) { // 2-char escape, fine
outputBuffer[outputPtr++] = BYTE_BACKSLASH;
outputBuffer[outputPtr++] = (byte) escape;
} else {
// ctrl-char, 6-byte escape...
outputPtr = _writeGenericEscape(ch, outputPtr);
}
}
_outputTail = outputPtr;
}
/*
/**********************************************************
/* Internal methods, low-level writing, base64 encoded
/**********************************************************
*/
protected final void _writeBinary(Base64Variant b64variant,
byte[] input, int inputPtr, final int inputEnd)
throws IOException, JsonGenerationException
{
// Encoding is by chunks of 3 input, 4 output chars, so:
int safeInputEnd = inputEnd - 3;
// Let's also reserve room for possible (and quoted) lf char each round
int safeOutputEnd = _outputEnd - 6;
int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
// Ok, first we loop through all full triplets of data:
while (inputPtr <= safeInputEnd) {
if (_outputTail > safeOutputEnd) { // need to flush
_flushBuffer();
}
// First, mash 3 bytes into lsb of 32-bit int
int b24 = ((int) input[inputPtr++]) << 8;
b24 |= ((int) input[inputPtr++]) & 0xFF;
b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF);
_outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
if (--chunksBeforeLF <= 0) {
// note: must quote in JSON value
_outputBuffer[_outputTail++] = '\\';
_outputBuffer[_outputTail++] = 'n';
chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
}
}
// And then we may have 1 or 2 leftover bytes to encode
int inputLeft = inputEnd - inputPtr; // 0, 1 or 2
if (inputLeft > 0) { // yes, but do we have room for output?
if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
_flushBuffer();
}
int b24 = ((int) input[inputPtr++]) << 16;
if (inputLeft == 2) {
b24 |= (((int) input[inputPtr++]) & 0xFF) << 8;
}
_outputTail = b64variant.encodeBase64Partial(b24, inputLeft, _outputBuffer, _outputTail);
}
}
// write-method called when length is definitely known
protected final int _writeBinary(Base64Variant b64variant,
InputStream data, byte[] readBuffer, int bytesLeft)
throws IOException, JsonGenerationException
{
int inputPtr = 0;
int inputEnd = 0;
int lastFullOffset = -3;
// Let's also reserve room for possible (and quoted) LF char each round
int safeOutputEnd = _outputEnd - 6;
int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
while (bytesLeft > 2) { // main loop for full triplets
if (inputPtr > lastFullOffset) {
inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft);
inputPtr = 0;
if (inputEnd < 3) { // required to try to read to have at least 3 bytes
break;
}
lastFullOffset = inputEnd-3;
}
if (_outputTail > safeOutputEnd) { // need to flush
_flushBuffer();
}
int b24 = ((int) readBuffer[inputPtr++]) << 8;
b24 |= ((int) readBuffer[inputPtr++]) & 0xFF;
b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF);
bytesLeft -= 3;
_outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
if (--chunksBeforeLF <= 0) {
_outputBuffer[_outputTail++] = '\\';
_outputBuffer[_outputTail++] = 'n';
chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
}
}
// And then we may have 1 or 2 leftover bytes to encode
if (bytesLeft > 0) {
inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, bytesLeft);
inputPtr = 0;
if (inputEnd > 0) { // yes, but do we have room for output?
if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
_flushBuffer();
}
int b24 = ((int) readBuffer[inputPtr++]) << 16;
int amount;
if (inputPtr < inputEnd) {
b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8;
amount = 2;
} else {
amount = 1;
}
_outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail);
bytesLeft -= amount;
}
}
return bytesLeft;
}
// write method when length is unknown
protected final int _writeBinary(Base64Variant b64variant,
InputStream data, byte[] readBuffer)
throws IOException, JsonGenerationException
{
int inputPtr = 0;
int inputEnd = 0;
int lastFullOffset = -3;
int bytesDone = 0;
// Let's also reserve room for possible (and quoted) LF char each round
int safeOutputEnd = _outputEnd - 6;
int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
// Ok, first we loop through all full triplets of data:
while (true) {
if (inputPtr > lastFullOffset) { // need to load more
inputEnd = _readMore(data, readBuffer, inputPtr, inputEnd, readBuffer.length);
inputPtr = 0;
if (inputEnd < 3) { // required to try to read to have at least 3 bytes
break;
}
lastFullOffset = inputEnd-3;
}
if (_outputTail > safeOutputEnd) { // need to flush
_flushBuffer();
}
// First, mash 3 bytes into lsb of 32-bit int
int b24 = ((int) readBuffer[inputPtr++]) << 8;
b24 |= ((int) readBuffer[inputPtr++]) & 0xFF;
b24 = (b24 << 8) | (((int) readBuffer[inputPtr++]) & 0xFF);
bytesDone += 3;
_outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
if (--chunksBeforeLF <= 0) {
_outputBuffer[_outputTail++] = '\\';
_outputBuffer[_outputTail++] = 'n';
chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
}
}
// And then we may have 1 or 2 leftover bytes to encode
if (inputPtr < inputEnd) { // yes, but do we have room for output?
if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
_flushBuffer();
}
int b24 = ((int) readBuffer[inputPtr++]) << 16;
int amount = 1;
if (inputPtr < inputEnd) {
b24 |= (((int) readBuffer[inputPtr]) & 0xFF) << 8;
amount = 2;
}
bytesDone += amount;
_outputTail = b64variant.encodeBase64Partial(b24, amount, _outputBuffer, _outputTail);
}
return bytesDone;
}
private final int _readMore(InputStream in,
byte[] readBuffer, int inputPtr, int inputEnd,
int maxRead) throws IOException
{
// anything to shift to front?
int i = 0;
while (inputPtr < inputEnd) {
readBuffer[i++] = readBuffer[inputPtr++];
}
inputPtr = 0;
inputEnd = i;
maxRead = Math.min(maxRead, readBuffer.length);
do {
int length = maxRead - inputEnd;
if (length == 0) {
break;
}
int count = in.read(readBuffer, inputEnd, length);
if (count < 0) {
return inputEnd;
}
inputEnd += count;
} while (inputEnd < 3);
return inputEnd;
}
/*
/**********************************************************
/* Internal methods, character escapes/encoding
/**********************************************************
*/
/**
* Method called to output a character that is beyond range of
* 1- and 2-byte UTF-8 encodings, when outputting "raw"
* text (meaning it is not to be escaped or quoted)
*/
private final int _outputRawMultiByteChar(int ch, char[] cbuf, int inputOffset, int inputEnd)
throws IOException
{
// Let's handle surrogates gracefully (as 4 byte output):
if (ch >= SURR1_FIRST) {
if (ch <= SURR2_LAST) { // yes, outside of BMP
// Do we have second part?
if (inputOffset >= inputEnd || cbuf == null) { // nope... have to note down
_reportError(String.format(
"Split surrogate on writeRaw() input (last character): first character 0x%4x", ch));
}
_outputSurrogates(ch, cbuf[inputOffset]);
return inputOffset+1;
}
}
final byte[] bbuf = _outputBuffer;
bbuf[_outputTail++] = (byte) (0xe0 | (ch >> 12));
bbuf[_outputTail++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
return inputOffset;
}
protected final void _outputSurrogates(int surr1, int surr2) throws IOException
{
int c = _decodeSurrogate(surr1, surr2);
if ((_outputTail + 4) > _outputEnd) {
_flushBuffer();
}
final byte[] bbuf = _outputBuffer;
bbuf[_outputTail++] = (byte) (0xf0 | (c >> 18));
bbuf[_outputTail++] = (byte) (0x80 | ((c >> 12) & 0x3f));
bbuf[_outputTail++] = (byte) (0x80 | ((c >> 6) & 0x3f));
bbuf[_outputTail++] = (byte) (0x80 | (c & 0x3f));
}
/**
*
* @param ch
* @param outputPtr Position within output buffer to append multi-byte in
*
* @return New output position after appending
*
* @throws IOException
*/
private final int _outputMultiByteChar(int ch, int outputPtr) throws IOException
{
byte[] bbuf = _outputBuffer;
if (ch >= SURR1_FIRST && ch <= SURR2_LAST) { // yes, outside of BMP; add an escape
// 23-Nov-2015, tatu: As per [core#223], may or may not want escapes;
// it would be added here... but as things are, we do not have proper
// access yet...
// if (Feature.ESCAPE_UTF8_SURROGATES.enabledIn(_features)) {
bbuf[outputPtr++] = BYTE_BACKSLASH;
bbuf[outputPtr++] = BYTE_u;
bbuf[outputPtr++] = HEX_CHARS[(ch >> 12) & 0xF];
bbuf[outputPtr++] = HEX_CHARS[(ch >> 8) & 0xF];
bbuf[outputPtr++] = HEX_CHARS[(ch >> 4) & 0xF];
bbuf[outputPtr++] = HEX_CHARS[ch & 0xF];
// } else { ... }
} else {
bbuf[outputPtr++] = (byte) (0xe0 | (ch >> 12));
bbuf[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
bbuf[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
}
return outputPtr;
}
private final void _writeNull() throws IOException
{
if ((_outputTail + 4) >= _outputEnd) {
_flushBuffer();
}
System.arraycopy(NULL_BYTES, 0, _outputBuffer, _outputTail, 4);
_outputTail += 4;
}
/**
* Method called to write a generic Unicode escape for given character.
*
* @param charToEscape Character to escape using escape sequence (\\uXXXX)
*/
private int _writeGenericEscape(int charToEscape, int outputPtr) throws IOException
{
final byte[] bbuf = _outputBuffer;
bbuf[outputPtr++] = BYTE_BACKSLASH;
bbuf[outputPtr++] = BYTE_u;
if (charToEscape > 0xFF) {
int hi = (charToEscape >> 8) & 0xFF;
bbuf[outputPtr++] = HEX_CHARS[hi >> 4];
bbuf[outputPtr++] = HEX_CHARS[hi & 0xF];
charToEscape &= 0xFF;
} else {
bbuf[outputPtr++] = BYTE_0;
bbuf[outputPtr++] = BYTE_0;
}
// We know it's a control char, so only the last 2 chars are non-0
bbuf[outputPtr++] = HEX_CHARS[charToEscape >> 4];
bbuf[outputPtr++] = HEX_CHARS[charToEscape & 0xF];
return outputPtr;
}
protected final void _flushBuffer() throws IOException
{
int len = _outputTail;
if (len > 0) {
_outputTail = 0;
_outputStream.write(_outputBuffer, 0, len);
}
}
}
UTF8StreamJsonParser.java 0000664 0000000 0000000 00000402733 13561642473 0034016 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json package com.fasterxml.jackson.core.json;
import java.io.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.base.ParserBase;
import com.fasterxml.jackson.core.io.CharTypes;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
import com.fasterxml.jackson.core.util.*;
import static com.fasterxml.jackson.core.JsonTokenId.*;
/**
* This is a concrete implementation of {@link JsonParser}, which is
* based on a {@link java.io.InputStream} as the input source.
*/
public class UTF8StreamJsonParser
extends ParserBase
{
final static byte BYTE_LF = (byte) '\n';
@SuppressWarnings("deprecation")
private final static int FEAT_MASK_TRAILING_COMMA = Feature.ALLOW_TRAILING_COMMA.getMask();
@SuppressWarnings("deprecation")
private final static int FEAT_MASK_LEADING_ZEROS = Feature.ALLOW_NUMERIC_LEADING_ZEROS.getMask();
@SuppressWarnings("deprecation")
private final static int FEAT_MASK_NON_NUM_NUMBERS = Feature.ALLOW_NON_NUMERIC_NUMBERS.getMask();
@SuppressWarnings("deprecation")
private final static int FEAT_MASK_ALLOW_MISSING = Feature.ALLOW_MISSING_VALUES.getMask();
private final static int FEAT_MASK_ALLOW_SINGLE_QUOTES = Feature.ALLOW_SINGLE_QUOTES.getMask();
private final static int FEAT_MASK_ALLOW_UNQUOTED_NAMES = Feature.ALLOW_UNQUOTED_FIELD_NAMES.getMask();
private final static int FEAT_MASK_ALLOW_JAVA_COMMENTS = Feature.ALLOW_COMMENTS.getMask();
private final static int FEAT_MASK_ALLOW_YAML_COMMENTS = Feature.ALLOW_YAML_COMMENTS.getMask();
// This is the main input-code lookup table, fetched eagerly
private final static int[] _icUTF8 = CharTypes.getInputCodeUtf8();
// Latin1 encoding is not supported, but we do use 8-bit subset for
// pre-processing task, to simplify first pass, keep it fast.
protected final static int[] _icLatin1 = CharTypes.getInputCodeLatin1();
/*
/**********************************************************
/* Configuration
/**********************************************************
*/
/**
* Codec used for data binding when (if) requested; typically full
*
ObjectMapper
, but that abstract is not part of core
* package.
*/
protected ObjectCodec _objectCodec;
/**
* Symbol table that contains field names encountered so far
*/
final protected ByteQuadsCanonicalizer _symbols;
/*
/**********************************************************
/* Parsing state
/**********************************************************
*/
/**
* Temporary buffer used for name parsing.
*/
protected int[] _quadBuffer = new int[16];
/**
* Flag that indicates that the current token has not yet
* been fully processed, and needs to be finished for
* some access (or skipped to obtain the next token)
*/
protected boolean _tokenIncomplete;
/**
* Temporary storage for partially parsed name bytes.
*/
private int _quad1;
/**
* Value of {@link #_inputPtr} at the time when the first character of
* name token was read. Used for calculating token location when requested;
* combined with {@link #_currInputProcessed}, may be updated appropriately
* as needed.
*
* @since 2.7
*/
protected int _nameStartOffset;
/**
* @since 2.7
*/
protected int _nameStartRow;
/**
* @since 2.7
*/
protected int _nameStartCol;
/*
/**********************************************************
/* Input buffering (from former 'StreamBasedParserBase')
/**********************************************************
*/
protected InputStream _inputStream;
/*
/**********************************************************
/* Current input data
/**********************************************************
*/
/**
* Current buffer from which data is read; generally data is read into
* buffer from input source, but in some cases pre-loaded buffer
* is handed to the parser.
*/
protected byte[] _inputBuffer;
/**
* Flag that indicates whether the input buffer is recycable (and
* needs to be returned to recycler once we are done) or not.
*_writeFieldName
, off-lined
* to keep the "fast path" as simple (and hopefully fast) as possible.
*/
protected final void _writePPFieldName(String name, boolean commaBefore) throws IOException
{
if (commaBefore) {
_cfgPrettyPrinter.writeObjectEntrySeparator(this);
} else {
_cfgPrettyPrinter.beforeObjectEntries(this);
}
if (_cfgUnqNames) {// non-standard, omit quotes
_writeString(name);
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
_writeString(name);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
}
protected final void _writePPFieldName(SerializableString name, boolean commaBefore) throws IOException
{
if (commaBefore) {
_cfgPrettyPrinter.writeObjectEntrySeparator(this);
} else {
_cfgPrettyPrinter.beforeObjectEntries(this);
}
final char[] quoted = name.asQuotedChars();
if (_cfgUnqNames) {// non-standard, omit quotes
writeRaw(quoted, 0, quoted.length);
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
writeRaw(quoted, 0, quoted.length);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
}
/*
/**********************************************************
/* Output method implementations, textual
/**********************************************************
*/
@Override
public void writeString(String text) throws IOException
{
_verifyValueWrite(WRITE_STRING);
if (text == null) {
_writeNull();
return;
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
_writeString(text);
// And finally, closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public void writeString(Reader reader, int len) throws IOException {
_verifyValueWrite(WRITE_STRING);
if (reader == null) {
_reportError("null reader");
}
int toRead = (len >= 0) ? len : Integer.MAX_VALUE;
//Add leading quote
if ((_outputTail + len) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
final char[] buf = _allocateCopyBuffer();
//read
while (toRead > 0) {
int toReadNow = Math.min(toRead, buf.length);
int numRead = reader.read(buf, 0, toReadNow);
if (numRead <= 0) {
break;
}
if ((_outputTail + len) >= _outputEnd) {
_flushBuffer();
}
_writeString(buf, 0, numRead);
//decrease tracker
toRead -= numRead;
}
//Add trailing quote
if ((_outputTail + len) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
if (toRead > 0 && len >= 0) {
_reportError("Didn't read enough from reader");
}
}
@Override
public void writeString(char[] text, int offset, int len) throws IOException
{
_verifyValueWrite(WRITE_STRING);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
_writeString(text, offset, len);
// And finally, closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public void writeString(SerializableString sstr) throws IOException
{
_verifyValueWrite(WRITE_STRING);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
int len = sstr.appendQuoted(_outputBuffer, _outputTail);
if (len < 0) {
_writeString2(sstr);
return;
}
_outputTail += len;
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
private void _writeString2(SerializableString sstr) throws IOException
{
// Note: copied from writeRaw:
char[] text = sstr.asQuotedChars();
final int len = text.length;
if (len < SHORT_WRITE) {
int room = _outputEnd - _outputTail;
if (len > room) {
_flushBuffer();
}
System.arraycopy(text, 0, _outputBuffer, _outputTail, len);
_outputTail += len;
} else {
_flushBuffer();
_writer.write(text, 0, len);
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException {
// could add support for buffering if we really want it...
_reportUnsupportedOperation();
}
@Override
public void writeUTF8String(byte[] text, int offset, int length) throws IOException {
// could add support for buffering if we really want it...
_reportUnsupportedOperation();
}
/*
/**********************************************************
/* Output method implementations, unprocessed ("raw")
/**********************************************************
*/
@Override
public void writeRaw(String text) throws IOException
{
// Nothing to check, can just output as is
int len = text.length();
int room = _outputEnd - _outputTail;
if (room == 0) {
_flushBuffer();
room = _outputEnd - _outputTail;
}
// But would it nicely fit in? If yes, it's easy
if (room >= len) {
text.getChars(0, len, _outputBuffer, _outputTail);
_outputTail += len;
} else {
writeRawLong(text);
}
}
@Override
public void writeRaw(String text, int start, int len) throws IOException
{
// Nothing to check, can just output as is
int room = _outputEnd - _outputTail;
if (room < len) {
_flushBuffer();
room = _outputEnd - _outputTail;
}
// But would it nicely fit in? If yes, it's easy
if (room >= len) {
text.getChars(start, start+len, _outputBuffer, _outputTail);
_outputTail += len;
} else {
writeRawLong(text.substring(start, start+len));
}
}
// @since 2.1
@Override
public void writeRaw(SerializableString text) throws IOException {
int len = text.appendUnquoted(_outputBuffer, _outputTail);
if (len < 0) {
writeRaw(text.getValue());
return;
}
_outputTail += len;
}
@Override
public void writeRaw(char[] text, int offset, int len) throws IOException
{
// Only worth buffering if it's a short write?
if (len < SHORT_WRITE) {
int room = _outputEnd - _outputTail;
if (len > room) {
_flushBuffer();
}
System.arraycopy(text, offset, _outputBuffer, _outputTail, len);
_outputTail += len;
return;
}
// Otherwise, better just pass through:
_flushBuffer();
_writer.write(text, offset, len);
}
@Override
public void writeRaw(char c) throws IOException
{
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = c;
}
private void writeRawLong(String text) throws IOException
{
int room = _outputEnd - _outputTail;
// If not, need to do it by looping
text.getChars(0, room, _outputBuffer, _outputTail);
_outputTail += room;
_flushBuffer();
int offset = room;
int len = text.length() - room;
while (len > _outputEnd) {
int amount = _outputEnd;
text.getChars(offset, offset+amount, _outputBuffer, 0);
_outputHead = 0;
_outputTail = amount;
_flushBuffer();
offset += amount;
len -= amount;
}
// And last piece (at most length of buffer)
text.getChars(offset, offset+len, _outputBuffer, 0);
_outputHead = 0;
_outputTail = len;
}
/*
/**********************************************************
/* Output method implementations, base64-encoded binary
/**********************************************************
*/
@Override
public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)
throws IOException, JsonGenerationException
{
_verifyValueWrite(WRITE_BINARY);
// Starting quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
_writeBinary(b64variant, data, offset, offset+len);
// and closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public int writeBinary(Base64Variant b64variant,
InputStream data, int dataLength)
throws IOException, JsonGenerationException
{
_verifyValueWrite(WRITE_BINARY);
// Starting quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
byte[] encodingBuffer = _ioContext.allocBase64Buffer();
int bytes;
try {
if (dataLength < 0) { // length unknown
bytes = _writeBinary(b64variant, data, encodingBuffer);
} else {
int missing = _writeBinary(b64variant, data, encodingBuffer, dataLength);
if (missing > 0) {
_reportError("Too few bytes available: missing "+missing+" bytes (out of "+dataLength+")");
}
bytes = dataLength;
}
} finally {
_ioContext.releaseBase64Buffer(encodingBuffer);
}
// and closing quotes
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
return bytes;
}
/*
/**********************************************************
/* Output method implementations, primitive
/**********************************************************
*/
@Override
public void writeNumber(short s) throws IOException
{
_verifyValueWrite(WRITE_NUMBER);
if (_cfgNumbersAsStrings) {
_writeQuotedShort(s);
return;
}
// up to 5 digits and possible minus sign
if ((_outputTail + 6) >= _outputEnd) {
_flushBuffer();
}
_outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail);
}
private void _writeQuotedShort(short s) throws IOException {
if ((_outputTail + 8) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
_outputTail = NumberOutput.outputInt(s, _outputBuffer, _outputTail);
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public void writeNumber(int i) throws IOException
{
_verifyValueWrite(WRITE_NUMBER);
if (_cfgNumbersAsStrings) {
_writeQuotedInt(i);
return;
}
// up to 10 digits and possible minus sign
if ((_outputTail + 11) >= _outputEnd) {
_flushBuffer();
}
_outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
}
private void _writeQuotedInt(int i) throws IOException {
if ((_outputTail + 13) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
_outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public void writeNumber(long l) throws IOException
{
_verifyValueWrite(WRITE_NUMBER);
if (_cfgNumbersAsStrings) {
_writeQuotedLong(l);
return;
}
if ((_outputTail + 21) >= _outputEnd) {
// up to 20 digits, minus sign
_flushBuffer();
}
_outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
}
private void _writeQuotedLong(long l) throws IOException {
if ((_outputTail + 23) >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
_outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
_outputBuffer[_outputTail++] = _quoteChar;
}
// !!! 05-Aug-2008, tatus: Any ways to optimize these?
@Override
public void writeNumber(BigInteger value) throws IOException
{
_verifyValueWrite(WRITE_NUMBER);
if (value == null) {
_writeNull();
} else if (_cfgNumbersAsStrings) {
_writeQuotedRaw(value.toString());
} else {
writeRaw(value.toString());
}
}
@SuppressWarnings("deprecation")
@Override
public void writeNumber(double d) throws IOException
{
if (_cfgNumbersAsStrings ||
(NumberOutput.notFinite(d) && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS))) {
writeString(String.valueOf(d));
return;
}
// What is the max length for doubles? 40 chars?
_verifyValueWrite(WRITE_NUMBER);
writeRaw(String.valueOf(d));
}
@SuppressWarnings("deprecation")
@Override
public void writeNumber(float f) throws IOException
{
if (_cfgNumbersAsStrings ||
(NumberOutput.notFinite(f) && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS))) {
writeString(String.valueOf(f));
return;
}
// What is the max length for floats?
_verifyValueWrite(WRITE_NUMBER);
writeRaw(String.valueOf(f));
}
@Override
public void writeNumber(BigDecimal value) throws IOException
{
// Don't really know max length for big decimal, no point checking
_verifyValueWrite(WRITE_NUMBER);
if (value == null) {
_writeNull();
} else if (_cfgNumbersAsStrings) {
_writeQuotedRaw(_asString(value));
} else {
writeRaw(_asString(value));
}
}
@Override
public void writeNumber(String encodedValue) throws IOException
{
_verifyValueWrite(WRITE_NUMBER);
if (_cfgNumbersAsStrings) {
_writeQuotedRaw(encodedValue);
} else {
writeRaw(encodedValue);
}
}
private void _writeQuotedRaw(String value) throws IOException
{
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
writeRaw(value);
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = _quoteChar;
}
@Override
public void writeBoolean(boolean state) throws IOException
{
_verifyValueWrite(WRITE_BOOLEAN);
if ((_outputTail + 5) >= _outputEnd) {
_flushBuffer();
}
int ptr = _outputTail;
char[] buf = _outputBuffer;
if (state) {
buf[ptr] = 't';
buf[++ptr] = 'r';
buf[++ptr] = 'u';
buf[++ptr] = 'e';
} else {
buf[ptr] = 'f';
buf[++ptr] = 'a';
buf[++ptr] = 'l';
buf[++ptr] = 's';
buf[++ptr] = 'e';
}
_outputTail = ptr+1;
}
@Override
public void writeNull() throws IOException {
_verifyValueWrite(WRITE_NULL);
_writeNull();
}
/*
/**********************************************************
/* Implementations for other methods
/**********************************************************
*/
@Override
protected final void _verifyValueWrite(String typeMsg) throws IOException
{
final int status = _writeContext.writeValue();
if (_cfgPrettyPrinter != null) {
// Otherwise, pretty printer knows what to do...
_verifyPrettyValueWrite(typeMsg, status);
return;
}
char c;
switch (status) {
case JsonWriteContext.STATUS_OK_AS_IS:
default:
return;
case JsonWriteContext.STATUS_OK_AFTER_COMMA:
c = ',';
break;
case JsonWriteContext.STATUS_OK_AFTER_COLON:
c = ':';
break;
case JsonWriteContext.STATUS_OK_AFTER_SPACE: // root-value separator
if (_rootValueSeparator != null) {
writeRaw(_rootValueSeparator.getValue());
}
return;
case JsonWriteContext.STATUS_EXPECT_NAME:
_reportCantWriteValueExpectName(typeMsg);
return;
}
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = c;
}
/*
/**********************************************************
/* Low-level output handling
/**********************************************************
*/
@Override
public void flush() throws IOException
{
_flushBuffer();
if (_writer != null) {
if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
_writer.flush();
}
}
}
@Override
public void close() throws IOException
{
super.close();
/* 05-Dec-2008, tatu: To add [JACKSON-27], need to close open
* scopes.
*/
// First: let's see that we still have buffers...
if (_outputBuffer != null
&& isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
while (true) {
JsonStreamContext ctxt = getOutputContext();
if (ctxt.inArray()) {
writeEndArray();
} else if (ctxt.inObject()) {
writeEndObject();
} else {
break;
}
}
}
_flushBuffer();
_outputHead = 0;
_outputTail = 0;
/* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
* on the underlying Reader, unless we "own" it, or auto-closing
* feature is enabled.
* One downside: when using UTF8Writer, underlying buffer(s)
* may not be properly recycled if we don't close the writer.
*/
if (_writer != null) {
if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) {
_writer.close();
} else if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
// If we can't close it, we should at least flush
_writer.flush();
}
}
// Internal buffer(s) generator has can now be released as well
_releaseBuffers();
}
@Override
protected void _releaseBuffers()
{
char[] buf = _outputBuffer;
if (buf != null) {
_outputBuffer = null;
_ioContext.releaseConcatBuffer(buf);
}
buf = _copyBuffer;
if (buf != null) {
_copyBuffer = null;
_ioContext.releaseNameCopyBuffer(buf);
}
}
/*
/**********************************************************
/* Internal methods, low-level writing; text, default
/**********************************************************
*/
private void _writeString(String text) throws IOException
{
/* One check first: if String won't fit in the buffer, let's
* segment writes. No point in extending buffer to huge sizes
* (like if someone wants to include multi-megabyte base64
* encoded stuff or such)
*/
final int len = text.length();
if (len > _outputEnd) { // Let's reserve space for entity at begin/end
_writeLongString(text);
return;
}
// Ok: we know String will fit in buffer ok
// But do we need to flush first?
if ((_outputTail + len) > _outputEnd) {
_flushBuffer();
}
text.getChars(0, len, _outputBuffer, _outputTail);
if (_characterEscapes != null) {
_writeStringCustom(len);
} else if (_maximumNonEscapedChar != 0) {
_writeStringASCII(len, _maximumNonEscapedChar);
} else {
_writeString2(len);
}
}
private void _writeString2(final int len) throws IOException
{
// And then we'll need to verify need for escaping etc:
final int end = _outputTail + len;
final int[] escCodes = _outputEscapes;
final int escLen = escCodes.length;
output_loop:
while (_outputTail < end) {
// Fast loop for chars not needing escaping
escape_loop:
while (true) {
char c = _outputBuffer[_outputTail];
if (c < escLen && escCodes[c] != 0) {
break escape_loop;
}
if (++_outputTail >= end) {
break output_loop;
}
}
// Ok, bumped into something that needs escaping.
/* First things first: need to flush the buffer.
* Inlined, as we don't want to lose tail pointer
*/
int flushLen = (_outputTail - _outputHead);
if (flushLen > 0) {
_writer.write(_outputBuffer, _outputHead, flushLen);
}
/* In any case, tail will be the new start, so hopefully
* we have room now.
*/
char c = _outputBuffer[_outputTail++];
_prependOrWriteCharacterEscape(c, escCodes[c]);
}
}
/**
* Method called to write "long strings", strings whose length exceeds
* output buffer length.
*/
private void _writeLongString(String text) throws IOException
{
// First things first: let's flush the buffer to get some more room
_flushBuffer();
// Then we can write
final int textLen = text.length();
int offset = 0;
do {
int max = _outputEnd;
int segmentLen = ((offset + max) > textLen)
? (textLen - offset) : max;
text.getChars(offset, offset+segmentLen, _outputBuffer, 0);
if (_characterEscapes != null) {
_writeSegmentCustom(segmentLen);
} else if (_maximumNonEscapedChar != 0) {
_writeSegmentASCII(segmentLen, _maximumNonEscapedChar);
} else {
_writeSegment(segmentLen);
}
offset += segmentLen;
} while (offset < textLen);
}
/**
* Method called to output textual context which has been copied
* to the output buffer prior to call. If any escaping is needed,
* it will also be handled by the method.
*_currInputRow
tracks `\n`). Used to simplify
* tracking of linefeeds, assuming that input typically uses various
* linefeed combinations (`\r`, `\n` or `\r\n`) consistently, in which
* case we can simply choose max of two row candidates.
*/
protected int _currInputRowAlt = 1;
/*
/**********************************************************************
/* Life-cycle
/**********************************************************************
*/
public NonBlockingJsonParserBase(IOContext ctxt, int parserFeatures,
ByteQuadsCanonicalizer sym)
{
super(ctxt, parserFeatures);
_symbols = sym;
_currToken = null;
_majorState = MAJOR_INITIAL;
_majorStateAfterValue = MAJOR_ROOT;
}
@Override
public ObjectCodec getCodec() {
return null;
}
@Override
public void setCodec(ObjectCodec c) {
throw new UnsupportedOperationException("Can not use ObjectMapper with non-blocking parser");
}
/**
* @since 2.9
*/
@Override
public boolean canParseAsync() { return true; }
/*
/**********************************************************
/* Test support
/**********************************************************
*/
protected ByteQuadsCanonicalizer symbolTableForTests() {
return _symbols;
}
/*
/**********************************************************
/* Abstract methods from JsonParser
/**********************************************************
*/
@Override
public abstract int releaseBuffered(OutputStream out) throws IOException;
@Override
protected void _releaseBuffers() throws IOException
{
super._releaseBuffers();
// Merge found symbols, if any:
_symbols.release();
// any other temp buffers?
}
@Override
public Object getInputSource() {
// since input is "pushed", to traditional source...
return null;
}
@Override
protected void _closeInput() throws IOException {
// 30-May-2017, tatu: Seems like this is the most certain way to prevent
// further decoding... not the optimal place, but due to inheritance
// hierarchy most convenient.
_currBufferStart = 0;
_inputEnd = 0;
}
/*
/**********************************************************************
/* Overridden methods
/**********************************************************************
*/
@Override
public boolean hasTextCharacters()
{
if (_currToken == JsonToken.VALUE_STRING) {
// yes; is or can be made available efficiently as char[]
return _textBuffer.hasTextAsCharacters();
}
if (_currToken == JsonToken.FIELD_NAME) {
// not necessarily; possible but:
return _nameCopied;
}
// other types, no benefit from accessing as char[]
return false;
}
@Override
public JsonLocation getCurrentLocation()
{
int col = _inputPtr - _currInputRowStart + 1; // 1-based
// Since we track CR and LF separately, max should gives us right answer
int row = Math.max(_currInputRow, _currInputRowAlt);
return new JsonLocation(_getSourceReference(),
_currInputProcessed + (_inputPtr - _currBufferStart), -1L, // bytes, chars
row, col);
}
@Override
public JsonLocation getTokenLocation()
{
return new JsonLocation(_getSourceReference(),
_tokenInputTotal, -1L, _tokenInputRow, _tokenInputCol);
}
/*
/**********************************************************************
/* Public API, access to token information, text
/**********************************************************************
*/
/**
* Method for accessing textual representation of the current event;
* if no current event (before first call to {@link #nextToken}, or
* after encountering end-of-input), returns null.
* Method can be called for any event.
*/
@Override
public String getText() throws IOException
{
if (_currToken == JsonToken.VALUE_STRING) {
return _textBuffer.contentsAsString();
}
return _getText2(_currToken);
}
protected final String _getText2(JsonToken t)
{
if (t == null) {
return null;
}
switch (t.id()) {
case ID_NOT_AVAILABLE:
return null;
case ID_FIELD_NAME:
return _parsingContext.getCurrentName();
case ID_STRING:
// fall through
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return _textBuffer.contentsAsString();
default:
return t.asString();
}
}
@Override // since 2.8
public int getText(Writer writer) throws IOException
{
JsonToken t = _currToken;
if (t == JsonToken.VALUE_STRING) {
return _textBuffer.contentsToWriter(writer);
}
if (t == JsonToken.FIELD_NAME) {
String n = _parsingContext.getCurrentName();
writer.write(n);
return n.length();
}
if (t != null) {
if (t.isNumeric()) {
return _textBuffer.contentsToWriter(writer);
}
if (t == JsonToken.NOT_AVAILABLE) {
_reportError("Current token not available: can not call this method");
}
char[] ch = t.asCharArray();
writer.write(ch);
return ch.length;
}
return 0;
}
// // // Let's override default impls for improved performance
// @since 2.1
@Override
public String getValueAsString() throws IOException
{
if (_currToken == JsonToken.VALUE_STRING) {
return _textBuffer.contentsAsString();
}
if (_currToken == JsonToken.FIELD_NAME) {
return getCurrentName();
}
return super.getValueAsString(null);
}
// @since 2.1
@Override
public String getValueAsString(String defValue) throws IOException
{
if (_currToken == JsonToken.VALUE_STRING) {
return _textBuffer.contentsAsString();
}
if (_currToken == JsonToken.FIELD_NAME) {
return getCurrentName();
}
return super.getValueAsString(defValue);
}
@Override
public char[] getTextCharacters() throws IOException
{
if (_currToken != null) { // null only before/after document
switch (_currToken.id()) {
case ID_FIELD_NAME:
if (!_nameCopied) {
String name = _parsingContext.getCurrentName();
int nameLen = name.length();
if (_nameCopyBuffer == null) {
_nameCopyBuffer = _ioContext.allocNameCopyBuffer(nameLen);
} else if (_nameCopyBuffer.length < nameLen) {
_nameCopyBuffer = new char[nameLen];
}
name.getChars(0, nameLen, _nameCopyBuffer, 0);
_nameCopied = true;
}
return _nameCopyBuffer;
case ID_STRING:
// fall through
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return _textBuffer.getTextBuffer();
default:
return _currToken.asCharArray();
}
}
return null;
}
@Override
public int getTextLength() throws IOException
{
if (_currToken != null) { // null only before/after document
switch (_currToken.id()) {
case ID_FIELD_NAME:
return _parsingContext.getCurrentName().length();
case ID_STRING:
// fall through
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return _textBuffer.size();
default:
return _currToken.asCharArray().length;
}
}
return 0;
}
@Override
public int getTextOffset() throws IOException
{
// Most have offset of 0, only some may have other values:
if (_currToken != null) {
switch (_currToken.id()) {
case ID_FIELD_NAME:
return 0;
case ID_STRING:
// fall through
case ID_NUMBER_INT:
case ID_NUMBER_FLOAT:
return _textBuffer.getTextOffset();
default:
}
}
return 0;
}
/*
/**********************************************************************
/* Public API, access to token information, binary
/**********************************************************************
*/
@Override
public byte[] getBinaryValue(Base64Variant b64variant) throws IOException
{
if (_currToken != JsonToken.VALUE_STRING) {
_reportError("Current token (%s) not VALUE_STRING or VALUE_EMBEDDED_OBJECT, can not access as binary",
_currToken);
}
if (_binaryValue == null) {
@SuppressWarnings("resource")
ByteArrayBuilder builder = _getByteArrayBuilder();
_decodeBase64(getText(), builder, b64variant);
_binaryValue = builder.toByteArray();
}
return _binaryValue;
}
@Override
public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException
{
byte[] b = getBinaryValue(b64variant);
out.write(b);
return b.length;
}
@Override
public Object getEmbeddedObject() throws IOException
{
if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT ) {
return _binaryValue;
}
return null;
}
/*
/**********************************************************************
/* Handling of nested scope, state
/**********************************************************************
*/
protected final JsonToken _startArrayScope() throws IOException
{
_parsingContext = _parsingContext.createChildArrayContext(-1, -1);
_majorState = MAJOR_ARRAY_ELEMENT_FIRST;
_majorStateAfterValue = MAJOR_ARRAY_ELEMENT_NEXT;
return (_currToken = JsonToken.START_ARRAY);
}
protected final JsonToken _startObjectScope() throws IOException
{
_parsingContext = _parsingContext.createChildObjectContext(-1, -1);
_majorState = MAJOR_OBJECT_FIELD_FIRST;
_majorStateAfterValue = MAJOR_OBJECT_FIELD_NEXT;
return (_currToken = JsonToken.START_OBJECT);
}
protected final JsonToken _closeArrayScope() throws IOException
{
if (!_parsingContext.inArray()) {
_reportMismatchedEndMarker(']', '}');
}
JsonReadContext ctxt = _parsingContext.getParent();
_parsingContext = ctxt;
int st;
if (ctxt.inObject()) {
st = MAJOR_OBJECT_FIELD_NEXT;
} else if (ctxt.inArray()) {
st = MAJOR_ARRAY_ELEMENT_NEXT;
} else {
st = MAJOR_ROOT;
}
_majorState = st;
_majorStateAfterValue = st;
return (_currToken = JsonToken.END_ARRAY);
}
protected final JsonToken _closeObjectScope() throws IOException
{
if (!_parsingContext.inObject()) {
_reportMismatchedEndMarker('}', ']');
}
JsonReadContext ctxt = _parsingContext.getParent();
_parsingContext = ctxt;
int st;
if (ctxt.inObject()) {
st = MAJOR_OBJECT_FIELD_NEXT;
} else if (ctxt.inArray()) {
st = MAJOR_ARRAY_ELEMENT_NEXT;
} else {
st = MAJOR_ROOT;
}
_majorState = st;
_majorStateAfterValue = st;
return (_currToken = JsonToken.END_OBJECT);
}
/*
/**********************************************************
/* Internal methods, symbol (name) handling
/**********************************************************
*/
protected final String _findName(int q1, int lastQuadBytes) throws JsonParseException
{
q1 = _padLastQuad(q1, lastQuadBytes);
// Usually we'll find it from the canonical symbol table already
String name = _symbols.findName(q1);
if (name != null) {
return name;
}
// If not, more work. We'll need add stuff to buffer
_quadBuffer[0] = q1;
return _addName(_quadBuffer, 1, lastQuadBytes);
}
protected final String _findName(int q1, int q2, int lastQuadBytes) throws JsonParseException
{
q2 = _padLastQuad(q2, lastQuadBytes);
// Usually we'll find it from the canonical symbol table already
String name = _symbols.findName(q1, q2);
if (name != null) {
return name;
}
// If not, more work. We'll need add stuff to buffer
_quadBuffer[0] = q1;
_quadBuffer[1] = q2;
return _addName(_quadBuffer, 2, lastQuadBytes);
}
protected final String _findName(int q1, int q2, int q3, int lastQuadBytes) throws JsonParseException
{
q3 = _padLastQuad(q3, lastQuadBytes);
String name = _symbols.findName(q1, q2, q3);
if (name != null) {
return name;
}
int[] quads = _quadBuffer;
quads[0] = q1;
quads[1] = q2;
quads[2] = _padLastQuad(q3, lastQuadBytes);
return _addName(quads, 3, lastQuadBytes);
}
/**
* This is the main workhorse method used when we take a symbol
* table miss. It needs to demultiplex individual bytes, decode
* multi-byte chars (if any), and then construct Name instance
* and add it to the symbol table.
*/
protected final String _addName(int[] quads, int qlen, int lastQuadBytes) throws JsonParseException
{
/* Ok: must decode UTF-8 chars. No other validation is
* needed, since unescaping has been done earlier as necessary
* (as well as error reporting for unescaped control chars)
*/
// 4 bytes per quad, except last one maybe less
int byteLen = (qlen << 2) - 4 + lastQuadBytes;
/* And last one is not correctly aligned (leading zero bytes instead
* need to shift a bit, instead of trailing). Only need to shift it
* for UTF-8 decoding; need revert for storage (since key will not
* be aligned, to optimize lookup speed)
*/
int lastQuad;
if (lastQuadBytes < 4) {
lastQuad = quads[qlen-1];
// 8/16/24 bit left shift
quads[qlen-1] = (lastQuad << ((4 - lastQuadBytes) << 3));
} else {
lastQuad = 0;
}
// Need some working space, TextBuffer works well:
char[] cbuf = _textBuffer.emptyAndGetCurrentSegment();
int cix = 0;
for (int ix = 0; ix < byteLen; ) {
int ch = quads[ix >> 2]; // current quad, need to shift+mask
int byteIx = (ix & 3);
ch = (ch >> ((3 - byteIx) << 3)) & 0xFF;
++ix;
if (ch > 127) { // multi-byte
int needed;
if ((ch & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
ch &= 0x1F;
needed = 1;
} else if ((ch & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
ch &= 0x0F;
needed = 2;
} else if ((ch & 0xF8) == 0xF0) { // 4 bytes; double-char with surrogates and all...
ch &= 0x07;
needed = 3;
} else { // 5- and 6-byte chars not valid xml chars
_reportInvalidInitial(ch);
needed = ch = 1; // never really gets this far
}
if ((ix + needed) > byteLen) {
_reportInvalidEOF(" in field name", JsonToken.FIELD_NAME);
}
// Ok, always need at least one more:
int ch2 = quads[ix >> 2]; // current quad, need to shift+mask
byteIx = (ix & 3);
ch2 = (ch2 >> ((3 - byteIx) << 3));
++ix;
if ((ch2 & 0xC0) != 0x080) {
_reportInvalidOther(ch2);
}
ch = (ch << 6) | (ch2 & 0x3F);
if (needed > 1) {
ch2 = quads[ix >> 2];
byteIx = (ix & 3);
ch2 = (ch2 >> ((3 - byteIx) << 3));
++ix;
if ((ch2 & 0xC0) != 0x080) {
_reportInvalidOther(ch2);
}
ch = (ch << 6) | (ch2 & 0x3F);
if (needed > 2) { // 4 bytes? (need surrogates on output)
ch2 = quads[ix >> 2];
byteIx = (ix & 3);
ch2 = (ch2 >> ((3 - byteIx) << 3));
++ix;
if ((ch2 & 0xC0) != 0x080) {
_reportInvalidOther(ch2 & 0xFF);
}
ch = (ch << 6) | (ch2 & 0x3F);
}
}
if (needed > 2) { // surrogate pair? once again, let's output one here, one later on
ch -= 0x10000; // to normalize it starting with 0x0
if (cix >= cbuf.length) {
cbuf = _textBuffer.expandCurrentSegment();
}
cbuf[cix++] = (char) (0xD800 + (ch >> 10));
ch = 0xDC00 | (ch & 0x03FF);
}
}
if (cix >= cbuf.length) {
cbuf = _textBuffer.expandCurrentSegment();
}
cbuf[cix++] = (char) ch;
}
// Ok. Now we have the character array, and can construct the String
String baseName = new String(cbuf, 0, cix);
// And finally, un-align if necessary
if (lastQuadBytes < 4) {
quads[qlen-1] = lastQuad;
}
return _symbols.addName(baseName, quads, qlen);
}
/**
* Helper method needed to fix [jackson-core#148], masking of 0x00 character
*/
protected final static int _padLastQuad(int q, int bytes) {
return (bytes == 4) ? q : (q | (-1 << (bytes << 3)));
}
/*
/**********************************************************************
/* Internal methods, state changes
/**********************************************************************
*/
/**
* Helper method called at point when all input has been exhausted and
* input feeder has indicated no more input will be forthcoming.
*/
protected final JsonToken _eofAsNextToken() throws IOException {
_majorState = MAJOR_CLOSED;
if (!_parsingContext.inRoot()) {
_handleEOF();
}
close();
return (_currToken = null);
}
protected final JsonToken _fieldComplete(String name) throws IOException
{
_majorState = MAJOR_OBJECT_VALUE;
_parsingContext.setCurrentName(name);
return (_currToken = JsonToken.FIELD_NAME);
}
protected final JsonToken _valueComplete(JsonToken t) throws IOException
{
_majorState = _majorStateAfterValue;
_currToken = t;
return t;
}
protected final JsonToken _valueCompleteInt(int value, String asText) throws IOException
{
_textBuffer.resetWithString(asText);
_intLength = asText.length();
_numTypesValid = NR_INT; // to force parsing
_numberInt = value;
_majorState = _majorStateAfterValue;
JsonToken t = JsonToken.VALUE_NUMBER_INT;
_currToken = t;
return t;
}
@SuppressWarnings("deprecation")
protected final JsonToken _valueNonStdNumberComplete(int type) throws IOException
{
String tokenStr = NON_STD_TOKENS[type];
_textBuffer.resetWithString(tokenStr);
if (!isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
_reportError("Non-standard token '%s': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow",
tokenStr);
}
_intLength = 0;
_numTypesValid = NR_DOUBLE;
_numberDouble = NON_STD_TOKEN_VALUES[type];
_majorState = _majorStateAfterValue;
return (_currToken = JsonToken.VALUE_NUMBER_FLOAT);
}
protected final String _nonStdToken(int type) {
return NON_STD_TOKENS[type];
}
/*
/**********************************************************************
/* Internal methods, error reporting, related
/**********************************************************************
*/
protected final void _updateTokenLocation()
{
_tokenInputRow = Math.max(_currInputRow, _currInputRowAlt);
final int ptr = _inputPtr;
_tokenInputCol = ptr - _currInputRowStart;
_tokenInputTotal = _currInputProcessed + (ptr - _currBufferStart);
}
protected void _reportInvalidChar(int c) throws JsonParseException {
// Either invalid WS or illegal UTF-8 start char
if (c < INT_SPACE) {
_throwInvalidSpace(c);
}
_reportInvalidInitial(c);
}
protected void _reportInvalidInitial(int mask) throws JsonParseException {
_reportError("Invalid UTF-8 start byte 0x"+Integer.toHexString(mask));
}
protected void _reportInvalidOther(int mask, int ptr) throws JsonParseException {
_inputPtr = ptr;
_reportInvalidOther(mask);
}
protected void _reportInvalidOther(int mask) throws JsonParseException {
_reportError("Invalid UTF-8 middle byte 0x"+Integer.toHexString(mask));
}
}
package-info.java 0000664 0000000 0000000 00000000176 13561642473 0033541 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/async /**
* Non-blocking ("async") JSON parser implementation.
*
* @since 2.9
*/
package com.fasterxml.jackson.core.json.async;
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/json/package-info.java 0000664 0000000 0000000 00000000436 13561642473 0032502 0 ustar 00root root 0000000 0000000 /**
* JSON-specific parser and generator implementation classes that
* Jackson defines and uses.
* Application code should not (need to) use contents of this package;
* nor are these implementations likely to be of use for sub-classing.
*/
package com.fasterxml.jackson.core.json;
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/package-info.java 0000664 0000000 0000000 00000002217 13561642473 0031530 0 ustar 00root root 0000000 0000000 /**
* Main public API classes of the core streaming JSON
* processor: most importantly {@link com.fasterxml.jackson.core.JsonFactory}
* used for constructing
* JSON parser ({@link com.fasterxml.jackson.core.JsonParser})
* and generator
* ({@link com.fasterxml.jackson.core.JsonGenerator})
* instances.
*
*
*/
package com.fasterxml.jackson.core;
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sym/ 0000775 0000000 0000000 00000000000 13561642473 0027147 5 ustar 00root root 0000000 0000000 ByteQuadsCanonicalizer.java 0000664 0000000 0000000 00000130760 13561642473 0034345 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/sym package com.fasterxml.jackson.core.sym;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.util.InternCache;
/**
* Replacement for JsonNode
) with the basic
*parsers and generators (iff using mapping-supporting factory: which
*is part of Mapping API, not core)
* com.fasterxml.jackson.databind.ObjectMapper
)
* can be exposed, without adding direct dependency to implementation.
* BytesToNameCanonicalizer
which aims at more localized
* memory access due to flattening of name quad data.
* Performance improvement modest for simple JSON document data binding (maybe 3%),
* but should help more for larger symbol tables, or for binary formats like Smile.
*
*
* and within every area, entries are 4 {@code int}s, where 1 - 3 ints contain 1 - 12
* UTF-8 encoded bytes of name (null-padded), and last int is offset in
* {@code _names} that contains actual name Strings.
*
* @since 2.6
*/
public final class ByteQuadsCanonicalizer
{
/**
* Initial size of the primary hash area. Each entry consumes 4 ints (16 bytes),
* and secondary area is same as primary; so default size will use 2kB of memory
* (plus 64x4 or 64x8 (256/512 bytes) for references to Strings, and Strings
* themselves).
*/
private static final int DEFAULT_T_SIZE = 64;
// private static final int DEFAULT_T_SIZE = 256;
/**
* Let's not expand symbol tables past some maximum size;
* with unique (~= random) names.
* Size is in
*/
private static final int MAX_T_SIZE = 0x10000; // 64k entries == 2M mem hash area
/**
* No point in trying to construct tiny tables, just need to resize soon.
*/
private final static int MIN_HASH_SIZE = 16;
/**
* Let's only share reasonably sized symbol tables. Max size set to 3/4 of 8k;
* this corresponds to 256k main hash index. This should allow for enough distinct
* names for almost any case, while preventing ballooning for cases where names
* are unique (or close thereof).
*/
final static int MAX_ENTRIES_FOR_REUSE = 6000;
/*
/**********************************************************
/* Linkage, needed for merging symbol tables
/**********************************************************
*/
/**
* Reference to the root symbol table, for child tables, so
* that they can merge table information back as necessary.
*/
final private ByteQuadsCanonicalizer _parent;
/**
* Member that is only used by the root table instance: root
* passes immutable state info child instances, and children
* may return new state if they add entries to the table.
* Child tables do NOT use the reference.
*/
final private AtomicReference2 * _hashSize
* entries of 16 bytes (4 ints), arranged in a cascading lookup
* structure (details of which may be tweaked depending on expected rates
* of collisions).
*/
private int[] _hashArea;
/**
* Number of slots for primary entries within {@link #_hashArea}; which is
* at most 1/8
of actual size of the underlying array (4-int slots,
* primary covers only half of the area; plus, additional area for longer
* symbols after hash area).
*/
private int _hashSize;
/**
* Offset within {@link #_hashArea} where secondary entries start
*/
private int _secondaryStart;
/**
* Offset within {@link #_hashArea} where tertiary entries start
*/
private int _tertiaryStart;
/**
* Constant that determines size of buckets for tertiary entries:
* 1 << _tertiaryShift
is the size, and shift value
* is also used for translating from primary offset into
* tertiary bucket (shift right by 4 + _tertiaryShift
).
*String
instances matching
* entries in {@link #_hashArea}.
* Contains nulls for unused entries. Note that this size is twice
* that of {@link #_hashArea}
*/
private String[] _names;
/*
/**********************************************************
/* Then information on collisions etc
/**********************************************************
*/
/**
* Pointer to the offset within spill-over area where there is room
* for more spilled over entries (if any).
* Spill over area is within fixed-size portion of {@link #_hashArea}.
*/
private int _spilloverEnd;
/**
* Offset within {@link #_hashArea} that follows main slots and contains
* quads for longer names (13 bytes or longer), and points to the
* first available int that may be used for appending quads of the next
* long name.
* Note that long name area follows immediately after the fixed-size
* main hash area ({@link #_hashArea}).
*/
private int _longNameOffset;
/*
/**********************************************************
/* Sharing, versioning
/**********************************************************
*/
// // // Which of the buffers may be shared (and are copy-on-write)?
/**
* Flag that indicates whether underlying data structures for
* the main hash area are shared or not. If they are, then they
* need to be handled in copy-on-write way, i.e. if they need
* to be modified, a copy needs to be made first; at this point
* it will not be shared any more, and can be modified.
*JsonFactory
"root"
* symbol tables: ones used for merging and sharing common symbols
*
* @param sz Initial primary hash area size
* @param intern Whether Strings contained should be {@link String#intern}ed
* @param seed Random seed valued used to make it more difficult to cause
* collisions (used for collision-based DoS attacks).
*/
private ByteQuadsCanonicalizer(int sz, boolean intern, int seed, boolean failOnDoS) {
_parent = null;
_seed = seed;
_intern = intern;
_failOnDoS = failOnDoS;
// Sanity check: let's now allow hash sizes below certain minimum value
if (sz < MIN_HASH_SIZE) {
sz = MIN_HASH_SIZE;
} else {
// Also; size must be 2^N; otherwise hash algorithm won't
// work... so let's just pad it up, if so
if ((sz & (sz - 1)) != 0) { // only true if it's 2^N
int curr = MIN_HASH_SIZE;
while (curr < sz) {
curr += curr;
}
sz = curr;
}
}
_tableInfo = new AtomicReference
* 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.
*release
),
* parent's shared tables may be updated from the child instance.
*/
final private CharsToNameCanonicalizer _parent;
/**
* Member that is only used by the root table instance: root
* passes immutable state info child instances, and children
* may return new state if they add entries to the table.
* Child tables do NOT use the reference.
*/
final private AtomicReference_buckets.length - 1
, when _buckets.length is
* a power of two.
*/
private int _indexMask;
/**
* We need to keep track of the longest collision list; this is needed
* both to indicate problems with attacks and to allow flushing for
* other cases.
*
* @since 2.1
*/
private int _longestCollisionList;
/*
/**********************************************************
/* State regarding shared arrays
/**********************************************************
*/
/**
* Flag that indicates whether underlying data structures for
* the main hash area are shared or not. If they are, then they
* need to be handled in copy-on-write way, i.e. if they need
* to be modified, a copy needs to be made first; at this point
* it will not be shared any more, and can be modified.
*JavaType
from "databind" bundle -- this
* abstraction is only needed so that types can be passed through
* {@link com.fasterxml.jackson.core.JsonParser#readValueAs} methods.
*
* @since 2.0
*/
public abstract class ResolvedType
{
/*
/**********************************************************
/* Public API, simple property accessors
/**********************************************************
*/
/**
* Accessor for type-erased {@link Class} of resolved type.
*/
public abstract Class> getRawClass();
public abstract boolean hasRawClass(Class> clz);
public abstract boolean isAbstract();
public abstract boolean isConcrete();
public abstract boolean isThrowable();
public abstract boolean isArrayType();
public abstract boolean isEnumType();
public abstract boolean isInterface();
public abstract boolean isPrimitive();
public abstract boolean isFinal();
public abstract boolean isContainerType();
public abstract boolean isCollectionLikeType();
/**
* Whether this type is a referential type, meaning that values are
* basically pointers to "real" values (or null) and not regular
* values themselves. Typical examples include things like
* {@link java.util.concurrent.atomic.AtomicReference}, and various
* Optional
types (in JDK8, Guava).
*
* @since 2.6
*/
public boolean isReferenceType() {
return getReferencedType() != null;
}
public abstract boolean isMapLikeType();
/*
/**********************************************************
/* Public API, type parameter access
/**********************************************************
*/
/**
* Method that can be used to find out if the type directly declares generic
* parameters (for its direct super-class and/or super-interfaces).
*/
public abstract boolean hasGenericTypes();
/**
* Accessor that can be used to find out type for which parameterization
* is applied: this is often NOT same as what {@link #getRawClass} returns,
* but rather one of it supertype.
*TypeFactory
from mapper package).
* For simple types this is same as calling
* {@link Class#getName}, but for structured types it may additionally
* contain type information about contents.
*/
public abstract String toCanonical();
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/type/TypeReference.java 0000664 0000000 0000000 00000004611 13561642473 0032725 0 ustar 00root root 0000000 0000000 package 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.
*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 TypeReferenceComparable
) is to prevent constructing a
* reference without type information.
*/
@Override
public int compareTo(TypeReferenceJsonTypeInfo.As.WRAPPER_ARRAY
.
*/
WRAPPER_ARRAY,
/**
* Inclusion as wrapper Object that has one key/value pair where type id
* is the key for typed value.
*JsonTypeInfo.As.WRAPPER_OBJECT
.
*/
WRAPPER_OBJECT,
/**
* Inclusion as a property within Object to write, but logically as separate
* metadata that is not exposed as payload to caller: that is, does not match
* any of visible properties value object has.
*JsonTypeInfo.As.PROPERTY
.
*/
METADATA_PROPERTY,
/**
* Inclusion as a "regular" property within Object to write; this implies that
* its value should come from regular POJO property on serialization, and
* be deserialized into such property. This handling, however, is up to databinding.
*JsonTypeInfo.As.EXISTING_PROPERTY
.
*/
PAYLOAD_PROPERTY,
/**
* Inclusion as a property within "parent" Object of value Object to write.
* This typically requires slightly convoluted processing in which property
* that contains type id is actually written after typed value object
* itself is written.
*
* Note that it is illegal to call write method if the current (parent) write context
* is not Object: no coercion is done for other inclusion types (unlike with
* other xxx_PROPERTY
choices.
* This also means that root values MAY NOT use this type id inclusion mechanism
* (as they have no parent context).
*JsonTypeInfo.As.EXTERNAL_PROPERTY
.
*/
PARENT_PROPERTY;
public boolean requiresObjectContext() {
return (this == METADATA_PROPERTY) || (this == PAYLOAD_PROPERTY);
}
}
/**
* Java object for which type id is being written. Not needed by default handling,
* but may be useful for customized format handling.
*/
public Object forValue;
/**
* (optional) Super-type of {@link #forValue} to use for type id generation (if no
* explicit id passed): used instead of actual class of {@link #forValue} in cases
* where we do not want to use the "real" type but something more generic, usually
* to work around specific problem with implementation type, or its deserializer.
*/
public Class> forValueType;
/**
* Actual type id to use: usually {link java.lang.String}.
*/
public Object id;
/**
* If type id is to be embedded as a regular property, name of the property;
* otherwise `null`.
*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.
*READ_IO_BUFFER
constants.
*/
public final byte[] allocByteBuffer(int ix) {
return allocByteBuffer(ix, 0);
}
public byte[] allocByteBuffer(int ix, int minSize) {
final int DEF_SIZE = byteBufferLength(ix);
if (minSize < DEF_SIZE) {
minSize = DEF_SIZE;
}
byte[] buffer = _byteBuffers.getAndSet(ix, null);
if (buffer == null || buffer.length < minSize) {
buffer = balloc(minSize);
}
return buffer;
}
public void releaseByteBuffer(int ix, byte[] buffer) {
_byteBuffers.set(ix, buffer);
}
/*
/**********************************************************
/* Public API, char buffers
/**********************************************************
*/
public final char[] allocCharBuffer(int ix) {
return allocCharBuffer(ix, 0);
}
public char[] allocCharBuffer(int ix, int minSize) {
final int DEF_SIZE = charBufferLength(ix);
if (minSize < DEF_SIZE) {
minSize = DEF_SIZE;
}
char[] buffer = _charBuffers.getAndSet(ix, null);
if (buffer == null || buffer.length < minSize) {
buffer = calloc(minSize);
}
return buffer;
}
public void releaseCharBuffer(int ix, char[] buffer) {
_charBuffers.set(ix, buffer);
}
/*
/**********************************************************
/* Overridable helper methods
/**********************************************************
*/
protected int byteBufferLength(int ix) {
return BYTE_BUFFER_LENGTHS[ix];
}
protected int charBufferLength(int ix) {
return CHAR_BUFFER_LENGTHS[ix];
}
/*
/**********************************************************
/* Actual allocations separated for easier debugging/profiling
/**********************************************************
*/
protected byte[] balloc(int size) { return new byte[size]; }
protected char[] calloc(int size) { return new char[size]; }
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclers.java 0000664 0000000 0000000 00000006425 13561642473 0033253 0 ustar 00root root 0000000 0000000 package com.fasterxml.jackson.core.util;
import java.lang.ref.SoftReference;
/**
* Helper entity used to control access to simple buffer recyling scheme used for
* some encoding, decoding tasks.
*
* @see BufferRecycler
*
* @since 2.9.2
*/
public class BufferRecyclers
{
/**
* System property that is checked to see if recycled buffers (see {@link BufferRecycler})
* should be tracked, for purpose of forcing release of all such buffers, typically
* during major classloading.
*
* @since 2.9.6
*/
public final static String SYSTEM_PROPERTY_TRACK_REUSABLE_BUFFERS
= "com.fasterxml.jackson.core.util.BufferRecyclers.trackReusableBuffers";
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
/**
* Flag that indicates whether {@link BufferRecycler} instances should be tracked.
*/
private final static ThreadLocalBufferManager _bufferRecyclerTracker;
static {
_bufferRecyclerTracker = "true".equals(System.getProperty(SYSTEM_PROPERTY_TRACK_REUSABLE_BUFFERS))
? ThreadLocalBufferManager.instance()
: null;
}
/*
/**********************************************************
/* BufferRecyclers for parsers, generators
/**********************************************************
*/
/**
* This ThreadLocal
contains a {@link java.lang.ref.SoftReference}
* to a {@link BufferRecycler} used to provide a low-cost
* buffer recycling between reader and writer instances.
*/
final protected static ThreadLocalindent
string to indent one level
* and the eol
string to separate lines.
*/
public DefaultIndenter(String indent, String eol)
{
charsPerLevel = indent.length();
indents = new char[indent.length() * INDENT_LEVELS];
int offset = 0;
for (int i=0; idelagateCopyMethod
* and which defines whether copy methods are handled locally (false), or
* delegated to configured
*/
public JsonGeneratorDelegate(JsonGenerator d, boolean delegateCopyMethods) {
delegate = d;
this.delegateCopyMethods = delegateCopyMethods;
}
@Override
public Object getCurrentValue() {
return delegate.getCurrentValue();
}
@Override
public void setCurrentValue(Object v) {
delegate.setCurrentValue(v);
}
/*
/**********************************************************
/* Extended API
/**********************************************************
*/
public JsonGenerator getDelegate() { return delegate; }
/*
/**********************************************************
/* Public API, metadata
/**********************************************************
*/
@Override public ObjectCodec getCodec() { return delegate.getCodec(); }
@Override public JsonGenerator setCodec(ObjectCodec oc) {
delegate.setCodec(oc);
return this;
}
@Override public void setSchema(FormatSchema schema) { delegate.setSchema(schema); }
@Override public FormatSchema getSchema() { return delegate.getSchema(); }
@Override public Version version() { return delegate.version(); }
@Override public Object getOutputTarget() { return delegate.getOutputTarget(); }
@Override public int getOutputBuffered() { return delegate.getOutputBuffered(); }
/*
/**********************************************************
/* Public API, capability introspection (since 2.3, mostly)
/**********************************************************
*/
@Override
public boolean canUseSchema(FormatSchema schema) { return delegate.canUseSchema(schema); }
@Override
public boolean canWriteTypeId() { return delegate.canWriteTypeId(); }
@Override
public boolean canWriteObjectId() { return delegate.canWriteObjectId(); }
@Override
public boolean canWriteBinaryNatively() { return delegate.canWriteBinaryNatively(); }
@Override
public boolean canOmitFields() { return delegate.canOmitFields(); }
/*
/**********************************************************
/* Public API, configuration
/**********************************************************
*/
@Override
public JsonGenerator enable(Feature f) {
delegate.enable(f);
return this;
}
@Override
public JsonGenerator disable(Feature f) {
delegate.disable(f);
return this;
}
@Override
public boolean isEnabled(Feature f) { return delegate.isEnabled(f); }
// final, can't override (and no need to)
//public final JsonGenerator configure(Feature f, boolean state)
@Override
public int getFeatureMask() { return delegate.getFeatureMask(); }
@Override
@Deprecated
public JsonGenerator setFeatureMask(int mask) {
delegate.setFeatureMask(mask);
return this;
}
@Override
public JsonGenerator overrideStdFeatures(int values, int mask) {
delegate.overrideStdFeatures(values, mask);
return this;
}
@Override
public JsonGenerator overrideFormatFeatures(int values, int mask) {
delegate.overrideFormatFeatures(values, mask);
return this;
}
/*
/**********************************************************
/* Configuring generator
/**********************************************************
*/
@Override
public JsonGenerator setPrettyPrinter(PrettyPrinter pp) {
delegate.setPrettyPrinter(pp);
return this;
}
@Override
public PrettyPrinter getPrettyPrinter() { return delegate.getPrettyPrinter(); }
@Override
public JsonGenerator useDefaultPrettyPrinter() { delegate.useDefaultPrettyPrinter();
return this; }
@Override
public JsonGenerator setHighestNonEscapedChar(int charCode) { delegate.setHighestNonEscapedChar(charCode);
return this; }
@Override
public int getHighestEscapedChar() { return delegate.getHighestEscapedChar(); }
@Override
public CharacterEscapes getCharacterEscapes() { return delegate.getCharacterEscapes(); }
@Override
public JsonGenerator setCharacterEscapes(CharacterEscapes esc) { delegate.setCharacterEscapes(esc);
return this; }
@Override
public JsonGenerator setRootValueSeparator(SerializableString sep) { delegate.setRootValueSeparator(sep);
return this; }
/*
/**********************************************************
/* Public API, write methods, structural
/**********************************************************
*/
@Override
public void writeStartArray() throws IOException { delegate.writeStartArray(); }
@Override
public void writeStartArray(int size) throws IOException { delegate.writeStartArray(size); }
@Override
public void writeStartArray(Object forValue) throws IOException { delegate.writeStartArray(forValue); }
@Override
public void writeStartArray(Object forValue, int size) throws IOException { delegate.writeStartArray(forValue, size); }
@Override
public void writeEndArray() throws IOException { delegate.writeEndArray(); }
@Override
public void writeStartObject() throws IOException { delegate.writeStartObject(); }
@Override
public void writeStartObject(Object forValue) throws IOException { delegate.writeStartObject(forValue); }
@Override
public void writeStartObject(Object forValue, int size) throws IOException {
delegate.writeStartObject(forValue, size);
}
@Override
public void writeEndObject() throws IOException { delegate.writeEndObject(); }
@Override
public void writeFieldName(String name) throws IOException {
delegate.writeFieldName(name);
}
@Override
public void writeFieldName(SerializableString name) throws IOException {
delegate.writeFieldName(name);
}
@Override
public void writeFieldId(long id) throws IOException {
delegate.writeFieldId(id);
}
@Override
public void writeArray(int[] array, int offset, int length) throws IOException {
delegate.writeArray(array, offset, length);
}
@Override
public void writeArray(long[] array, int offset, int length) throws IOException {
delegate.writeArray(array, offset, length);
}
@Override
public void writeArray(double[] array, int offset, int length) throws IOException {
delegate.writeArray(array, offset, length);
}
/*
/**********************************************************
/* Public API, write methods, text/String values
/**********************************************************
*/
@Override
public void writeString(String text) throws IOException { delegate.writeString(text); }
@Override
public void writeString(Reader reader, int len) throws IOException {
delegate.writeString(reader, len);
}
@Override
public void writeString(char[] text, int offset, int len) throws IOException { delegate.writeString(text, offset, len); }
@Override
public void writeString(SerializableString text) throws IOException { delegate.writeString(text); }
@Override
public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException { delegate.writeRawUTF8String(text, offset, length); }
@Override
public void writeUTF8String(byte[] text, int offset, int length) throws IOException { delegate.writeUTF8String(text, offset, length); }
/*
/**********************************************************
/* Public API, write methods, binary/raw content
/**********************************************************
*/
@Override
public void writeRaw(String text) throws IOException { delegate.writeRaw(text); }
@Override
public void writeRaw(String text, int offset, int len) throws IOException { delegate.writeRaw(text, offset, len); }
@Override
public void writeRaw(SerializableString raw) throws IOException { delegate.writeRaw(raw); }
@Override
public void writeRaw(char[] text, int offset, int len) throws IOException { delegate.writeRaw(text, offset, len); }
@Override
public void writeRaw(char c) throws IOException { delegate.writeRaw(c); }
@Override
public void writeRawValue(String text) throws IOException { delegate.writeRawValue(text); }
@Override
public void writeRawValue(String text, int offset, int len) throws IOException { delegate.writeRawValue(text, offset, len); }
@Override
public void writeRawValue(char[] text, int offset, int len) throws IOException { delegate.writeRawValue(text, offset, len); }
@Override
public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException { delegate.writeBinary(b64variant, data, offset, len); }
@Override
public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) throws IOException { return delegate.writeBinary(b64variant, data, dataLength); }
/*
/**********************************************************
/* Public API, write methods, other value types
/**********************************************************
*/
@Override
public void writeNumber(short v) throws IOException { delegate.writeNumber(v); }
@Override
public void writeNumber(int v) throws IOException { delegate.writeNumber(v); }
@Override
public void writeNumber(long v) throws IOException { delegate.writeNumber(v); }
@Override
public void writeNumber(BigInteger v) throws IOException { delegate.writeNumber(v); }
@Override
public void writeNumber(double v) throws IOException { delegate.writeNumber(v); }
@Override
public void writeNumber(float v) throws IOException { delegate.writeNumber(v); }
@Override
public void writeNumber(BigDecimal v) throws IOException { delegate.writeNumber(v); }
@Override
public void writeNumber(String encodedValue) throws IOException, UnsupportedOperationException { delegate.writeNumber(encodedValue); }
@Override
public void writeBoolean(boolean state) throws IOException { delegate.writeBoolean(state); }
@Override
public void writeNull() throws IOException { delegate.writeNull(); }
/*
/**********************************************************
/* Overridden field methods
/**********************************************************
*/
@Override
public void writeOmittedField(String fieldName) throws IOException { delegate.writeOmittedField(fieldName); }
/*
/**********************************************************
/* Public API, write methods, Native Ids
/**********************************************************
*/
@Override
public void writeObjectId(Object id) throws IOException { delegate.writeObjectId(id); }
@Override
public void writeObjectRef(Object id) throws IOException { delegate.writeObjectRef(id); }
@Override
public void writeTypeId(Object id) throws IOException { delegate.writeTypeId(id); }
@Override
public void writeEmbeddedObject(Object object) throws IOException { delegate.writeEmbeddedObject(object); }
/*
/**********************************************************
/* Public API, write methods, serializing Java objects
/**********************************************************
*/
@Override
public void writeObject(Object pojo) throws IOException {
if (delegateCopyMethods) {
delegate.writeObject(pojo);
return;
}
if (pojo == null) {
writeNull();
} else {
ObjectCodec c = getCodec();
if (c != null) {
c.writeValue(this, pojo);
return;
}
_writeSimpleObject(pojo);
}
}
@Override
public void writeTree(TreeNode tree) throws IOException {
if (delegateCopyMethods) {
delegate.writeTree(tree);
return;
}
// As with 'writeObject()', we are not check if write would work
if (tree == null) {
writeNull();
} else {
ObjectCodec c = getCodec();
if (c == null) {
throw new IllegalStateException("No ObjectCodec defined");
}
c.writeTree(this, tree);
}
}
/*
/**********************************************************
/* Public API, convenience field write methods
/**********************************************************
*/
// // These are fine, just delegate to other methods...
/*
/**********************************************************
/* Public API, copy-through methods
/**********************************************************
*/
@Override
public void copyCurrentEvent(JsonParser p) throws IOException {
if (delegateCopyMethods) delegate.copyCurrentEvent(p);
else super.copyCurrentEvent(p);
}
@Override
public void copyCurrentStructure(JsonParser p) throws IOException {
if (delegateCopyMethods) delegate.copyCurrentStructure(p);
else super.copyCurrentStructure(p);
}
/*
/**********************************************************
/* Public API, context access
/**********************************************************
*/
@Override public JsonStreamContext getOutputContext() { return delegate.getOutputContext(); }
/*
/**********************************************************
/* Public API, buffer handling
/**********************************************************
*/
@Override public void flush() throws IOException { delegate.flush(); }
@Override public void close() throws IOException { delegate.close(); }
/*
/**********************************************************
/* Closeable implementation
/**********************************************************
*/
@Override public boolean isClosed() { return delegate.isClosed(); }
}
JsonParserDelegate.java 0000664 0000000 0000000 00000025034 13561642473 0033625 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util package com.fasterxml.jackson.core.util;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.math.BigDecimal;
import java.math.BigInteger;
import com.fasterxml.jackson.core.*;
/**
* Helper class that implements
* delegation pattern for {@link JsonParser},
* to allow for simple overridability of basic parsing functionality.
* The idea is that any functionality to be modified can be simply
* overridden; and anything else will be delegated by default.
*/
public class JsonParserDelegate extends JsonParser
{
/**
* Delegate object that method calls are delegated to.
*/
protected JsonParser delegate;
public JsonParserDelegate(JsonParser d) {
delegate = d;
}
@Override
public Object getCurrentValue() {
return delegate.getCurrentValue();
}
@Override
public void setCurrentValue(Object v) {
delegate.setCurrentValue(v);
}
/*
/**********************************************************
/* Public API, configuration
/**********************************************************
*/
@Override public void setCodec(ObjectCodec c) { delegate.setCodec(c); }
@Override public ObjectCodec getCodec() { return delegate.getCodec(); }
@Override
public JsonParser enable(Feature f) {
delegate.enable(f);
return this;
}
@Override
public JsonParser disable(Feature f) {
delegate.disable(f);
return this;
}
@Override public boolean isEnabled(Feature f) { return delegate.isEnabled(f); }
@Override public int getFeatureMask() { return delegate.getFeatureMask(); }
@Override
@Deprecated // since 2.7
public JsonParser setFeatureMask(int mask) {
delegate.setFeatureMask(mask);
return this;
}
@Override
public JsonParser overrideStdFeatures(int values, int mask) {
delegate.overrideStdFeatures(values, mask);
return this;
}
@Override
public JsonParser overrideFormatFeatures(int values, int mask) {
delegate.overrideFormatFeatures(values, mask);
return this;
}
@Override public FormatSchema getSchema() { return delegate.getSchema(); }
@Override public void setSchema(FormatSchema schema) { delegate.setSchema(schema); }
@Override public boolean canUseSchema(FormatSchema schema) { return delegate.canUseSchema(schema); }
@Override public Version version() { return delegate.version(); }
@Override public Object getInputSource() { return delegate.getInputSource(); }
/*
/**********************************************************
/* Capability introspection
/**********************************************************
*/
@Override public boolean requiresCustomCodec() { return delegate.requiresCustomCodec(); }
/*
/**********************************************************
/* Closeable impl
/**********************************************************
*/
@Override public void close() throws IOException { delegate.close(); }
@Override public boolean isClosed() { return delegate.isClosed(); }
/*
/**********************************************************
/* Public API, token accessors
/**********************************************************
*/
@Override public JsonToken currentToken() { return delegate.currentToken(); }
@Override public int currentTokenId() { return delegate.currentTokenId(); }
@Override public JsonToken getCurrentToken() { return delegate.getCurrentToken(); }
@Override public int getCurrentTokenId() { return delegate.getCurrentTokenId(); }
@Override public boolean hasCurrentToken() { return delegate.hasCurrentToken(); }
@Override public boolean hasTokenId(int id) { return delegate.hasTokenId(id); }
@Override public boolean hasToken(JsonToken t) { return delegate.hasToken(t); }
@Override public String getCurrentName() throws IOException { return delegate.getCurrentName(); }
@Override public JsonLocation getCurrentLocation() { return delegate.getCurrentLocation(); }
@Override public JsonStreamContext getParsingContext() { return delegate.getParsingContext(); }
@Override public boolean isExpectedStartArrayToken() { return delegate.isExpectedStartArrayToken(); }
@Override public boolean isExpectedStartObjectToken() { return delegate.isExpectedStartObjectToken(); }
@Override public boolean isNaN() throws IOException { return delegate.isNaN(); }
/*
/**********************************************************
/* Public API, token state overrides
/**********************************************************
*/
@Override public void clearCurrentToken() { delegate.clearCurrentToken(); }
@Override public JsonToken getLastClearedToken() { return delegate.getLastClearedToken(); }
@Override public void overrideCurrentName(String name) { delegate.overrideCurrentName(name); }
/*
/**********************************************************
/* Public API, access to token information, text
/**********************************************************
*/
@Override public String getText() throws IOException { return delegate.getText(); }
@Override public boolean hasTextCharacters() { return delegate.hasTextCharacters(); }
@Override public char[] getTextCharacters() throws IOException { return delegate.getTextCharacters(); }
@Override public int getTextLength() throws IOException { return delegate.getTextLength(); }
@Override public int getTextOffset() throws IOException { return delegate.getTextOffset(); }
@Override public int getText(Writer writer) throws IOException, UnsupportedOperationException { return delegate.getText(writer); }
/*
/**********************************************************
/* Public API, access to token information, numeric
/**********************************************************
*/
@Override
public BigInteger getBigIntegerValue() throws IOException { return delegate.getBigIntegerValue(); }
@Override
public boolean getBooleanValue() throws IOException { return delegate.getBooleanValue(); }
@Override
public byte getByteValue() throws IOException { return delegate.getByteValue(); }
@Override
public short getShortValue() throws IOException { return delegate.getShortValue(); }
@Override
public BigDecimal getDecimalValue() throws IOException { return delegate.getDecimalValue(); }
@Override
public double getDoubleValue() throws IOException { return delegate.getDoubleValue(); }
@Override
public float getFloatValue() throws IOException { return delegate.getFloatValue(); }
@Override
public int getIntValue() throws IOException { return delegate.getIntValue(); }
@Override
public long getLongValue() throws IOException { return delegate.getLongValue(); }
@Override
public NumberType getNumberType() throws IOException { return delegate.getNumberType(); }
@Override
public Number getNumberValue() throws IOException { return delegate.getNumberValue(); }
/*
/**********************************************************
/* Public API, access to token information, coercion/conversion
/**********************************************************
*/
@Override public int getValueAsInt() throws IOException { return delegate.getValueAsInt(); }
@Override public int getValueAsInt(int defaultValue) throws IOException { return delegate.getValueAsInt(defaultValue); }
@Override public long getValueAsLong() throws IOException { return delegate.getValueAsLong(); }
@Override public long getValueAsLong(long defaultValue) throws IOException { return delegate.getValueAsLong(defaultValue); }
@Override public double getValueAsDouble() throws IOException { return delegate.getValueAsDouble(); }
@Override public double getValueAsDouble(double defaultValue) throws IOException { return delegate.getValueAsDouble(defaultValue); }
@Override public boolean getValueAsBoolean() throws IOException { return delegate.getValueAsBoolean(); }
@Override public boolean getValueAsBoolean(boolean defaultValue) throws IOException { return delegate.getValueAsBoolean(defaultValue); }
@Override public String getValueAsString() throws IOException { return delegate.getValueAsString(); }
@Override public String getValueAsString(String defaultValue) throws IOException { return delegate.getValueAsString(defaultValue); }
/*
/**********************************************************
/* Public API, access to token values, other
/**********************************************************
*/
@Override public Object getEmbeddedObject() throws IOException { return delegate.getEmbeddedObject(); }
@Override public byte[] getBinaryValue(Base64Variant b64variant) throws IOException { return delegate.getBinaryValue(b64variant); }
@Override public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException { return delegate.readBinaryValue(b64variant, out); }
@Override public JsonLocation getTokenLocation() { return delegate.getTokenLocation(); }
@Override public JsonToken nextToken() throws IOException { return delegate.nextToken(); }
@Override public JsonToken nextValue() throws IOException { return delegate.nextValue(); }
@Override public void finishToken() throws IOException { delegate.finishToken(); }
@Override
public JsonParser skipChildren() throws IOException {
delegate.skipChildren();
// NOTE: must NOT delegate this method to delegate, needs to be self-reference for chaining
return this;
}
/*
/**********************************************************
/* Public API, Native Ids (type, object)
/**********************************************************
*/
@Override public boolean canReadObjectId() { return delegate.canReadObjectId(); }
@Override public boolean canReadTypeId() { return delegate.canReadTypeId(); }
@Override public Object getObjectId() throws IOException { return delegate.getObjectId(); }
@Override public Object getTypeId() throws IOException { return delegate.getTypeId(); }
/*
/**********************************************************
/* Extended API
/**********************************************************
*/
/**
* Accessor for getting the immediate {@link JsonParser} this parser delegates calls to.
*
* @since 2.10
*/
public JsonParser delegate() { return delegate; }
}
JsonParserSequence.java 0000664 0000000 0000000 00000017070 13561642473 0033664 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util package 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}.
*true
), and if so
* that token is first return before {@link JsonParser#nextToken} is called.
* If enabled, this check is made; if disabled, no check is made and
* {@link JsonParser#nextToken} is always called for all parsers.
*false
(for backwards-compatibility)
* so that possible existing token is not considered for parsers.
*
* @since 2.8
*/
protected final boolean _checkForExistingToken;
/**
* Index of the next parser in {@link #_parsers}.
*/
protected int _nextParserIndex;
/**
* Flag used to indicate that `JsonParser.nextToken()` should not be called,
* due to parser already pointing to a token.
*
* @since 2.8
*/
protected boolean _hasToken;
/*
*******************************************************
* Construction
*******************************************************
*/
@Deprecated // since 2.8
protected JsonParserSequence(JsonParser[] parsers) {
this(false, parsers);
}
/**
* @since 2.8
*/
protected JsonParserSequence(boolean checkForExistingToken, JsonParser[] parsers)
{
super(parsers[0]);
_checkForExistingToken = checkForExistingToken;
_hasToken = checkForExistingToken && delegate.hasCurrentToken();
_parsers = parsers;
_nextParserIndex = 1;
}
/**
* Method that will construct a parser (possibly a sequence) that
* contains all given sub-parsers.
* All parsers given are checked to see if they are sequences: and
* if so, they will be "flattened", that is, contained parsers are
* directly added in a new sequence instead of adding sequences
* within sequences. This is done to minimize delegation depth,
* ideally only having just a single level of delegation.
*/
public static JsonParserSequence createFlattened(boolean checkForExistingToken,
JsonParser first, JsonParser second)
{
if (!(first instanceof JsonParserSequence || second instanceof JsonParserSequence)) {
return new JsonParserSequence(checkForExistingToken,
new JsonParser[] { first, second });
}
ArrayList
*
*/
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
*char
array regardless of whether they were collected in a segmented
* fashion or not.
*/
public char[] getTextBuffer()
{
// Are we just using shared input buffer?
if (_inputStart >= 0) return _inputBuffer;
if (_resultArray != null) return _resultArray;
if (_resultString != null) {
return (_resultArray = _resultString.toCharArray());
}
// Nope; but does it fit in just one segment?
if (!_hasSegments) {
return (_currentSegment == null) ? NO_CHARS : _currentSegment;
}
// Nope, need to have/create a non-segmented array and return it
return contentsAsArray();
}
/*
/**********************************************************
/* Other accessors:
/**********************************************************
*/
public String contentsAsString()
{
if (_resultString == null) {
// Has array been requested? Can make a shortcut, if so:
if (_resultArray != null) {
_resultString = new String(_resultArray);
} else {
// Do we use shared array?
if (_inputStart >= 0) {
if (_inputLen < 1) {
return (_resultString = "");
}
_resultString = new String(_inputBuffer, _inputStart, _inputLen);
} else { // nope... need to copy
// But first, let's see if we have just one buffer
int segLen = _segmentSize;
int currLen = _currentSize;
if (segLen == 0) { // yup
_resultString = (currLen == 0) ? "" : new String(_currentSegment, 0, currLen);
} else { // no, need to combine
StringBuilder sb = new StringBuilder(segLen + currLen);
// First stored segments
if (_segments != null) {
for (int i = 0, len = _segments.size(); i < len; ++i) {
char[] curr = _segments.get(i);
sb.append(curr, 0, curr.length);
}
}
// And finally, current segment:
sb.append(_currentSegment, 0, _currentSize);
_resultString = sb.toString();
}
}
}
}
return _resultString;
}
public char[] contentsAsArray() {
char[] result = _resultArray;
if (result == null) {
_resultArray = result = resultArray();
}
return result;
}
/**
* Convenience method for converting contents of the buffer
* into a {@link BigDecimal}.
*/
public BigDecimal contentsAsDecimal() throws NumberFormatException
{
// Already got a pre-cut array?
if (_resultArray != null) {
return NumberInput.parseBigDecimal(_resultArray);
}
// Or a shared buffer?
if ((_inputStart >= 0) && (_inputBuffer != null)) {
return NumberInput.parseBigDecimal(_inputBuffer, _inputStart, _inputLen);
}
// Or if not, just a single buffer (the usual case)
if ((_segmentSize == 0) && (_currentSegment != null)) {
return NumberInput.parseBigDecimal(_currentSegment, 0, _currentSize);
}
// If not, let's just get it aggregated...
return NumberInput.parseBigDecimal(contentsAsArray());
}
/**
* Convenience method for converting contents of the buffer
* into a Double value.
*/
public double contentsAsDouble() throws NumberFormatException {
return NumberInput.parseDouble(contentsAsString());
}
/**
* Specialized convenience method that will decode a 32-bit int,
* of at most 9 digits (and possible leading minus sign).
*
* @param neg Whether contents start with a minus sign
*
* @since 2.9
*/
public int contentsAsInt(boolean neg) {
if ((_inputStart >= 0) && (_inputBuffer != null)) {
if (neg) {
return -NumberInput.parseInt(_inputBuffer, _inputStart+1, _inputLen-1);
}
return NumberInput.parseInt(_inputBuffer, _inputStart, _inputLen);
}
if (neg) {
return -NumberInput.parseInt(_currentSegment, 1, _currentSize-1);
}
return NumberInput.parseInt(_currentSegment, 0, _currentSize);
}
/**
* Specialized convenience method that will decode a 64-bit int,
* of at most 18 digits (and possible leading minus sign).
*
* @param neg Whether contents start with a minus sign
*
* @since 2.9
*/
public long contentsAsLong(boolean neg) {
if ((_inputStart >= 0) && (_inputBuffer != null)) {
if (neg) {
return -NumberInput.parseLong(_inputBuffer, _inputStart+1, _inputLen-1);
}
return NumberInput.parseLong(_inputBuffer, _inputStart, _inputLen);
}
if (neg) {
return -NumberInput.parseLong(_currentSegment, 1, _currentSize-1);
}
return NumberInput.parseLong(_currentSegment, 0, _currentSize);
}
/**
* @since 2.8
*/
public int contentsToWriter(Writer w) throws IOException
{
if (_resultArray != null) {
w.write(_resultArray);
return _resultArray.length;
}
if (_resultString != null) { // Can take a shortcut...
w.write(_resultString);
return _resultString.length();
}
// Do we use shared array?
if (_inputStart >= 0) {
final int len = _inputLen;
if (len > 0) {
w.write(_inputBuffer, _inputStart, len);
}
return len;
}
// nope, not shared
int total = 0;
if (_segments != null) {
for (int i = 0, end = _segments.size(); i < end; ++i) {
char[] curr = _segments.get(i);
int currLen = curr.length;
w.write(curr, 0, currLen);
total += currLen;
}
}
int len = _currentSize;
if (len > 0) {
w.write(_currentSegment, 0, len);
total += len;
}
return total;
}
/*
/**********************************************************
/* Public mutators:
/**********************************************************
*/
/**
* Method called to make sure that buffer is not using shared input
* buffer; if it is, it will copy such contents to private buffer.
*/
public void ensureNotShared() {
if (_inputStart >= 0) {
unshare(16);
}
}
public void append(char c) {
// Using shared buffer so far?
if (_inputStart >= 0) {
unshare(16);
}
_resultString = null;
_resultArray = null;
// Room in current segment?
char[] curr = _currentSegment;
if (_currentSize >= curr.length) {
expand(1);
curr = _currentSegment;
}
curr[_currentSize++] = c;
}
public void append(char[] c, int start, int len)
{
// Can't append to shared buf (sanity check)
if (_inputStart >= 0) {
unshare(len);
}
_resultString = null;
_resultArray = null;
// Room in current segment?
char[] curr = _currentSegment;
int max = curr.length - _currentSize;
if (max >= len) {
System.arraycopy(c, start, curr, _currentSize, len);
_currentSize += len;
return;
}
// No room for all, need to copy part(s):
if (max > 0) {
System.arraycopy(c, start, curr, _currentSize, max);
start += max;
len -= max;
}
// And then allocate new segment; we are guaranteed to now
// have enough room in segment.
do {
expand(len);
int amount = Math.min(_currentSegment.length, len);
System.arraycopy(c, start, _currentSegment, 0, amount);
_currentSize += amount;
start += amount;
len -= amount;
} while (len > 0);
}
public void append(String str, int offset, int len)
{
// Can't append to shared buf (sanity check)
if (_inputStart >= 0) {
unshare(len);
}
_resultString = null;
_resultArray = null;
// Room in current segment?
char[] curr = _currentSegment;
int max = curr.length - _currentSize;
if (max >= len) {
str.getChars(offset, offset+len, curr, _currentSize);
_currentSize += len;
return;
}
// No room for all, need to copy part(s):
if (max > 0) {
str.getChars(offset, offset+max, curr, _currentSize);
len -= max;
offset += max;
}
// And then allocate new segment; we are guaranteed to now
// have enough room in segment.
do {
expand(len);
int amount = Math.min(_currentSegment.length, len);
str.getChars(offset, offset+amount, _currentSegment, 0);
_currentSize += amount;
offset += amount;
len -= amount;
} while (len > 0);
}
/*
/**********************************************************
/* Raw access, for high-performance use:
/**********************************************************
*/
public char[] getCurrentSegment()
{
/* Since the intention of the caller is to directly add stuff into
* buffers, we should NOT have anything in shared buffer... ie. may
* need to unshare contents.
*/
if (_inputStart >= 0) {
unshare(1);
} else {
char[] curr = _currentSegment;
if (curr == null) {
_currentSegment = buf(0);
} else if (_currentSize >= curr.length) {
// Plus, we better have room for at least one more char
expand(1);
}
}
return _currentSegment;
}
public char[] emptyAndGetCurrentSegment()
{
// inlined 'resetWithEmpty()'
_inputStart = -1; // indicates shared buffer not used
_currentSize = 0;
_inputLen = 0;
_inputBuffer = null;
_resultString = null;
_resultArray = null;
// And then reset internal input buffers, if necessary:
if (_hasSegments) {
clearSegments();
}
char[] curr = _currentSegment;
if (curr == null) {
_currentSegment = curr = buf(0);
}
return curr;
}
public int getCurrentSegmentSize() { return _currentSize; }
public void setCurrentLength(int len) { _currentSize = len; }
/**
* @since 2.6
*/
public String setCurrentAndReturn(int len) {
_currentSize = len;
// We can simplify handling here compared to full `contentsAsString()`:
if (_segmentSize > 0) { // longer text; call main method
return contentsAsString();
}
// more common case: single segment
int currLen = _currentSize;
String str = (currLen == 0) ? "" : new String(_currentSegment, 0, currLen);
_resultString = str;
return str;
}
public char[] finishCurrentSegment() {
if (_segments == null) {
_segments = new ArrayListPackageVersion
classes to decode version injected by Maven build.
*/
public static Version parseVersion(String s, String groupId, String artifactId)
{
if (s != null && (s = s.trim()).length() > 0) {
String[] parts = V_SEP.split(s);
return new Version(parseVersionPart(parts[0]),
(parts.length > 1) ? parseVersionPart(parts[1]) : 0,
(parts.length > 2) ? parseVersionPart(parts[2]) : 0,
(parts.length > 3) ? parts[3] : null,
groupId, artifactId);
}
return Version.unknownVersion();
}
protected static int parseVersionPart(String s) {
int number = 0;
for (int i = 0, len = s.length(); i < len; ++i) {
char c = s.charAt(i);
if (c > '9' || c < '0') break;
number = (number * 10) + (c - '0');
}
return number;
}
private final static void _close(Closeable c) {
try {
c.close();
} catch (IOException e) { }
}
/*
/**********************************************************
/* Orphan utility methods
/**********************************************************
*/
public final static void throwInternal() {
throw new RuntimeException("Internal error: this code path should never get executed");
}
}
jackson-core-jackson-core-2.10.1/src/main/java/com/fasterxml/jackson/core/util/package-info.java 0000664 0000000 0000000 00000000150 13561642473 0032477 0 ustar 00root root 0000000 0000000 /**
* Utility classes used by Jackson Core functionality.
*/
package com.fasterxml.jackson.core.util;
jackson-core-jackson-core-2.10.1/src/main/resources/ 0000775 0000000 0000000 00000000000 13561642473 0022265 5 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/resources/META-INF/ 0000775 0000000 0000000 00000000000 13561642473 0023425 5 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/resources/META-INF/LICENSE 0000664 0000000 0000000 00000000514 13561642473 0024432 0 ustar 00root root 0000000 0000000 This copy of Jackson JSON processor streaming parser/generator is licensed under the
Apache (Software) License, version 2.0 ("the License").
See the License for details about distribution rights, and the
specific rights regarding derivate works.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
jackson-core-jackson-core-2.10.1/src/main/resources/META-INF/NOTICE 0000664 0000000 0000000 00000001466 13561642473 0024340 0 ustar 00root root 0000000 0000000 # Jackson JSON processor
Jackson is a high-performance, Free/Open Source JSON processing library.
It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has
been in development since 2007.
It is currently developed by a community of developers, as well as supported
commercially by FasterXML.com.
## Licensing
Jackson core and extension components may licensed under different licenses.
To find the details that apply to this artifact see the accompanying LICENSE file.
For more information, including possible other licensing options, contact
FasterXML.com (http://fasterxml.com).
## Credits
A list of contributors may be found from CREDITS file, which is included
in some artifacts (usually source distributions); but is always available
from the source code management (SCM) system project uses.
jackson-core-jackson-core-2.10.1/src/main/resources/META-INF/services/ 0000775 0000000 0000000 00000000000 13561642473 0025250 5 ustar 00root root 0000000 0000000 com.fasterxml.jackson.core.JsonFactory 0000664 0000000 0000000 00000000047 13561642473 0034515 0 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/main/resources/META-INF/services com.fasterxml.jackson.core.JsonFactory
jackson-core-jackson-core-2.10.1/src/moditect/ 0000775 0000000 0000000 00000000000 13561642473 0021137 5 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/moditect/module-info.java 0000664 0000000 0000000 00000002127 13561642473 0024222 0 ustar 00root root 0000000 0000000 // Generated 08-Mar-2019 using Moditect maven plugin
module com.fasterxml.jackson.core {
// 08-Mar-2019, tatu: Ugh. Can not use wildcards, stupid ass JDK 9+ module system...
// So, for 2.x core need to make sure we manually include everything.
// Worse, there is only syntactic validation, not contents, so we can both miss
// AND add bogus packages.
// However: at least syntax is verified; and this works with JKD8
exports com.fasterxml.jackson.core;
exports com.fasterxml.jackson.core.async;
exports com.fasterxml.jackson.core.base;
exports com.fasterxml.jackson.core.exc;
exports com.fasterxml.jackson.core.filter;
exports com.fasterxml.jackson.core.format;
exports com.fasterxml.jackson.core.io;
exports com.fasterxml.jackson.core.json;
exports com.fasterxml.jackson.core.json.async;
exports com.fasterxml.jackson.core.sym;
exports com.fasterxml.jackson.core.type;
exports com.fasterxml.jackson.core.util;
// 03-Oct-2019, tatu: [core#567] Add self-use to avoid warnings
uses com.fasterxml.jackson.core.ObjectCodec;
}
jackson-core-jackson-core-2.10.1/src/site/ 0000775 0000000 0000000 00000000000 13561642473 0020273 5 ustar 00root root 0000000 0000000 jackson-core-jackson-core-2.10.1/src/site/site.xml 0000664 0000000 0000000 00000001661 13561642473 0021765 0 ustar 00root root 0000000 0000000